summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/apei/erst.c12
-rw-r--r--drivers/ata/ahci_platform.c7
-rw-r--r--drivers/block/aoe/aoeblk.c14
-rw-r--r--drivers/block/brd.c4
-rw-r--r--drivers/block/cciss.c76
-rw-r--r--drivers/block/cciss.h1
-rw-r--r--drivers/block/cpqarray.c2
-rw-r--r--drivers/block/drbd/drbd_int.h2
-rw-r--r--drivers/block/drbd/drbd_req.c8
-rw-r--r--drivers/block/loop.c251
-rw-r--r--drivers/block/nbd.c69
-rw-r--r--drivers/block/pktcdvd.c11
-rw-r--r--drivers/block/ps3vram.c6
-rw-r--r--drivers/block/umem.c4
-rw-r--r--drivers/block/virtio_blk.c30
-rw-r--r--drivers/block/xen-blkback/blkback.c130
-rw-r--r--drivers/block/xen-blkback/common.h98
-rw-r--r--drivers/block/xen-blkback/xenbus.c76
-rw-r--r--drivers/block/xen-blkfront.c123
-rw-r--r--drivers/char/Kconfig6
-rw-r--r--drivers/char/agp/hp-agp.c6
-rw-r--r--drivers/char/hw_random/Kconfig28
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/atmel-rng.c158
-rw-r--r--drivers/char/ttyprintk.c2
-rw-r--r--drivers/char/virtio_console.c120
-rw-r--r--drivers/clocksource/Kconfig15
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/clksrc-dbx500-prcmu.c106
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c38
-rw-r--r--drivers/cpufreq/e_powersaver.c135
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c129
-rw-r--r--drivers/crypto/Kconfig3
-rw-r--r--drivers/crypto/hifn_795x.c6
-rw-r--r--drivers/crypto/n2_core.c4
-rw-r--r--drivers/crypto/padlock-aes.c4
-rw-r--r--drivers/crypto/picoxcell_crypto.c121
-rw-r--r--drivers/crypto/talitos.c18
-rw-r--r--drivers/dma/Kconfig3
-rw-r--r--drivers/dma/amba-pl08x.c640
-rw-r--r--drivers/dma/at_hdmac.c164
-rw-r--r--drivers/dma/at_hdmac_regs.h24
-rw-r--r--drivers/dma/dmatest.c23
-rw-r--r--drivers/dma/dw_dmac.c5
-rw-r--r--drivers/dma/ep93xx_dma.c1
-rw-r--r--drivers/dma/imx-dma.c1
-rw-r--r--drivers/dma/imx-sdma.c48
-rw-r--r--drivers/dma/intel_mid_dma.c9
-rw-r--r--drivers/dma/ipu/ipu_idmac.c65
-rw-r--r--drivers/dma/mpc512x_dma.c1
-rw-r--r--drivers/dma/mxs-dma.c45
-rw-r--r--drivers/dma/pch_dma.c7
-rw-r--r--drivers/dma/pl330.c231
-rw-r--r--drivers/dma/shdma.c129
-rw-r--r--drivers/dma/shdma.h7
-rw-r--r--drivers/dma/timb_dma.c5
-rw-r--r--drivers/edac/Kconfig16
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/amd64_edac.c37
-rw-r--r--drivers/edac/edac_core.h350
-rw-r--r--drivers/edac/edac_mce.c61
-rw-r--r--drivers/edac/i7300_edac.c51
-rw-r--r--drivers/edac/i7core_edac.c415
-rw-r--r--drivers/edac/mce_amd.c46
-rw-r--r--drivers/edac/mce_amd.h6
-rw-r--r--drivers/edac/sb_edac.c1893
-rw-r--r--drivers/firewire/core-transaction.c4
-rw-r--r--drivers/firewire/net.c15
-rw-r--r--drivers/firewire/ohci.c255
-rw-r--r--drivers/firewire/sbp2.c260
-rw-r--r--drivers/firmware/efivars.c19
-rw-r--r--drivers/gpio/Kconfig5
-rw-r--r--drivers/gpio/gpio-langwell.c27
-rw-r--r--drivers/gpio/gpio-ml-ioh.c254
-rw-r--r--drivers/gpio/gpio-mxc.c18
-rw-r--r--drivers/gpio/gpio-mxs.c2
-rw-r--r--drivers/gpio/gpio-nomadik.c127
-rw-r--r--drivers/gpio/gpio-pch.c250
-rw-r--r--drivers/gpio/gpio-pl061.c31
-rw-r--r--drivers/gpio/gpio-pxa.c2
-rw-r--r--drivers/gpio/gpio-tegra.c143
-rw-r--r--drivers/hid/hid-apple.c9
-rw-r--r--drivers/hid/hid-core.c7
-rw-r--r--drivers/hid/hid-ids.h4
-rw-r--r--drivers/hid/hid-multitouch.c47
-rw-r--r--drivers/hid/hid-roccat.c18
-rw-r--r--drivers/hwmon/Kconfig20
-rw-r--r--drivers/hwmon/ad7414.c7
-rw-r--r--drivers/hwmon/ad7418.c27
-rw-r--r--drivers/hwmon/ads1015.c21
-rw-r--r--drivers/hwmon/ads7828.c12
-rw-r--r--drivers/hwmon/asb100.c10
-rw-r--r--drivers/hwmon/ds1621.c24
-rw-r--r--drivers/hwmon/ds620.c42
-rw-r--r--drivers/hwmon/gl518sm.c4
-rw-r--r--drivers/hwmon/gl520sm.c4
-rw-r--r--drivers/hwmon/hwmon.c32
-rw-r--r--drivers/hwmon/ibmaem.c107
-rw-r--r--drivers/hwmon/jc42.c52
-rw-r--r--drivers/hwmon/lm73.c30
-rw-r--r--drivers/hwmon/lm75.c9
-rw-r--r--drivers/hwmon/lm77.c4
-rw-r--r--drivers/hwmon/lm90.c146
-rw-r--r--drivers/hwmon/lm92.c26
-rw-r--r--drivers/hwmon/max16065.c4
-rw-r--r--drivers/hwmon/mc13783-adc.c12
-rw-r--r--drivers/hwmon/sht21.c26
-rw-r--r--drivers/hwmon/smm665.c15
-rw-r--r--drivers/hwmon/smsc47b397.c13
-rw-r--r--drivers/hwmon/tmp102.c44
-rw-r--r--drivers/hwmon/w83627ehf.c210
-rw-r--r--drivers/hwmon/w83781d.c10
-rw-r--r--drivers/hwspinlock/Kconfig27
-rw-r--r--drivers/hwspinlock/Makefile1
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c204
-rw-r--r--drivers/hwspinlock/hwspinlock_internal.h40
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c127
-rw-r--r--drivers/hwspinlock/u8500_hsem.c198
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c24
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c6
-rw-r--r--drivers/i2c/busses/Kconfig21
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-au1550.c282
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c4
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c (renamed from drivers/i2c/busses/i2c-designware.c)382
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h105
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c392
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c215
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c270
-rw-r--r--drivers/i2c/busses/i2c-highlander.c2
-rw-r--r--drivers/i2c/busses/i2c-imx.c46
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c91
-rw-r--r--drivers/i2c/busses/i2c-nuc900.c2
-rw-r--r--drivers/i2c/busses/i2c-omap.c164
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c130
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c2
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c2
-rw-r--r--drivers/i2c/busses/i2c-stu300.c2
-rw-r--r--drivers/i2c/busses/i2c-tegra.c2
-rw-r--r--drivers/i2c/busses/scx200_acb.c6
-rw-r--r--drivers/ide/Kconfig6
-rw-r--r--drivers/ide/au1xxx-ide.c46
-rw-r--r--drivers/infiniband/core/cm.c63
-rw-r--r--drivers/infiniband/core/cm_msgs.h32
-rw-r--r--drivers/infiniband/core/cma.c67
-rw-r--r--drivers/infiniband/core/mad.c3
-rw-r--r--drivers/infiniband/core/sysfs.c26
-rw-r--r--drivers/infiniband/core/ucm.c2
-rw-r--r--drivers/infiniband/core/ucma.c7
-rw-r--r--drivers/infiniband/core/umem.c6
-rw-r--r--drivers/infiniband/core/user_mad.c5
-rw-r--r--drivers/infiniband/core/uverbs.h18
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c703
-rw-r--r--drivers/infiniband/core/uverbs_main.c30
-rw-r--r--drivers/infiniband/core/verbs.c376
-rw-r--r--drivers/infiniband/hw/amso1100/c2_ae.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_intr.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c5
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c10
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_ev.c6
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.h1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_qp.c14
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c469
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c3
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c41
-rw-r--r--drivers/infiniband/hw/cxgb4/ev.c10
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h23
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c39
-rw-r--r--drivers/infiniband/hw/ehca/ehca_eq.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_qp.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_srq.c5
-rw-r--r--drivers/infiniband/hw/ipath/ipath_user_pages.c6
-rw-r--r--drivers/infiniband/hw/mlx4/main.c106
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h13
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c131
-rw-r--r--drivers/infiniband/hw/mlx4/srq.c10
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c3
-rw-r--r--drivers/infiniband/hw/nes/Makefile2
-rw-r--r--drivers/infiniband/hw/nes/nes.c8
-rw-r--r--drivers/infiniband/hw/nes/nes.h17
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c1121
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h75
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c99
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.h35
-rw-r--r--drivers/infiniband/hw/nes/nes_mgt.c1162
-rw-r--r--drivers/infiniband/hw/nes/nes_mgt.h97
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c4
-rw-r--r--drivers/infiniband/hw/nes/nes_utils.c53
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c8
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.h12
-rw-r--r--drivers/infiniband/hw/qib/qib.h15
-rw-r--r--drivers/infiniband/hw/qib/qib_driver.c20
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c135
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_qp.c90
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.c25
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.h3
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c46
-rw-r--r--drivers/infiniband/hw/qib/qib_ruc.c7
-rw-r--r--drivers/infiniband/hw/qib/qib_srq.c5
-rw-r--r--drivers/infiniband/hw/qib/qib_sysfs.c3
-rw-r--r--drivers/infiniband/hw/qib/qib_uc.c25
-rw-r--r--drivers/infiniband/hw/qib/qib_user_pages.c4
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c36
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c7
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c11
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h4
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c31
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c33
-rw-r--r--drivers/input/Kconfig2
-rw-r--r--drivers/input/misc/Kconfig26
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/mc13783-pwrbutton.c282
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c34
-rw-r--r--drivers/iommu/Kconfig19
-rw-r--r--drivers/iommu/Makefile3
-rw-r--r--drivers/iommu/amd_iommu.c4
-rw-r--r--drivers/iommu/intel-iommu.c2
-rw-r--r--drivers/iommu/iommu.c114
-rw-r--r--drivers/iommu/msm_iommu.c9
-rw-r--r--drivers/iommu/omap-iommu-debug.c418
-rw-r--r--drivers/iommu/omap-iommu.c1245
-rw-r--r--drivers/iommu/omap-iovmm.c742
-rw-r--r--drivers/isdn/Kconfig2
-rw-r--r--drivers/isdn/hisax/callc.c4
-rw-r--r--drivers/isdn/hisax/hisax.h4
-rw-r--r--drivers/isdn/hisax/isdnl1.h2
-rw-r--r--drivers/isdn/hisax/isdnl3.c2
-rw-r--r--drivers/isdn/hisax/l3dss1.c6
-rw-r--r--drivers/isdn/hisax/st5481_d.c4
-rw-r--r--drivers/leds/Kconfig12
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/led-class.c7
-rw-r--r--drivers/leds/led-triggers.c5
-rw-r--r--drivers/leds/leds-asic3.c35
-rw-r--r--drivers/leds/leds-gpio.c2
-rw-r--r--drivers/leds/leds-lm3530.c3
-rw-r--r--drivers/leds/leds-lp5521.c24
-rw-r--r--drivers/leds/leds-mc13783.c60
-rw-r--r--drivers/leds/leds-renesas-tpu.c357
-rw-r--r--drivers/lguest/core.c16
-rw-r--r--drivers/md/Kconfig36
-rw-r--r--drivers/md/Makefile4
-rw-r--r--drivers/md/dm-bufio.c1699
-rw-r--r--drivers/md/dm-bufio.h112
-rw-r--r--drivers/md/dm-ioctl.c11
-rw-r--r--drivers/md/dm-kcopyd.c31
-rw-r--r--drivers/md/dm-log-userspace-base.c37
-rw-r--r--drivers/md/dm-raid.c48
-rw-r--r--drivers/md/dm-table.c73
-rw-r--r--drivers/md/dm-thin-metadata.c1391
-rw-r--r--drivers/md/dm-thin-metadata.h156
-rw-r--r--drivers/md/dm-thin.c2428
-rw-r--r--drivers/md/dm.c46
-rw-r--r--drivers/md/dm.h2
-rw-r--r--drivers/md/faulty.c14
-rw-r--r--drivers/md/linear.c17
-rw-r--r--drivers/md/md.c12
-rw-r--r--drivers/md/md.h2
-rw-r--r--drivers/md/multipath.c8
-rw-r--r--drivers/md/persistent-data/Kconfig8
-rw-r--r--drivers/md/persistent-data/Makefile11
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c620
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h123
-rw-r--r--drivers/md/persistent-data/dm-btree-internal.h137
-rw-r--r--drivers/md/persistent-data/dm-btree-remove.c566
-rw-r--r--drivers/md/persistent-data/dm-btree-spine.c244
-rw-r--r--drivers/md/persistent-data/dm-btree.c805
-rw-r--r--drivers/md/persistent-data/dm-btree.h145
-rw-r--r--drivers/md/persistent-data/dm-persistent-data-internal.h19
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.c437
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.h26
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.c705
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.h126
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c335
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.h25
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c596
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.h33
-rw-r--r--drivers/md/persistent-data/dm-space-map.h134
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c400
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h130
-rw-r--r--drivers/md/raid0.c22
-rw-r--r--drivers/md/raid1.c9
-rw-r--r--drivers/md/raid10.c21
-rw-r--r--drivers/md/raid5.c8
-rw-r--r--drivers/media/common/saa7146_core.c74
-rw-r--r--drivers/media/common/saa7146_fops.c118
-rw-r--r--drivers/media/common/saa7146_hlp.c14
-rw-r--r--drivers/media/common/saa7146_i2c.c60
-rw-r--r--drivers/media/common/saa7146_vbi.c48
-rw-r--r--drivers/media/common/saa7146_video.c183
-rw-r--r--drivers/media/common/tuners/Makefile4
-rw-r--r--drivers/media/common/tuners/mt20xx.c24
-rw-r--r--drivers/media/common/tuners/mxl5005s.c22
-rw-r--r--drivers/media/common/tuners/tda18212.c31
-rw-r--r--drivers/media/common/tuners/tda18271-common.c32
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c2
-rw-r--r--drivers/media/common/tuners/tda18271-priv.h39
-rw-r--r--drivers/media/common/tuners/tda827x.c8
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c18
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.h1
-rw-r--r--drivers/media/dvb/b2c2/Makefile4
-rw-r--r--drivers/media/dvb/bt8xx/Makefile8
-rw-r--r--drivers/media/dvb/ddbridge/Makefile8
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c43
-rw-r--r--drivers/media/dvb/dm1105/Makefile2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c95
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig28
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile16
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c4
-rw-r--r--drivers/media/dvb/dvb-usb/af9005-fe.c2
-rw-r--r--drivers/media/dvb/dvb-usb/af9005.c5
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c70
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c337
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.h1
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c9
-rw-r--r--drivers/media/dvb/dvb-usb/az6027.c26
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.c9
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-core.c5
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c142
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c99
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c377
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-common.c27
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c31
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c3
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c16
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c14
-rw-r--r--drivers/media/dvb/dvb-usb/dtv5100.c11
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c153
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h6
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-init.c41
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-urb.c28
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h37
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c115
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.c9
-rw-r--r--drivers/media/dvb/dvb-usb/friio.c7
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c9
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c17
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c5
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c702
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c60
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.c58
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.c614
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.h55
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-gpio.c763
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-gpio.h56
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-i2c.c851
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-i2c.h35
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-phy.c342
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-phy.h53
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-reg.h179
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.c476
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.h89
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c1086
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.h158
-rw-r--r--drivers/media/dvb/dvb-usb/nova-t-usb2.c4
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.c13
-rw-r--r--drivers/media/dvb/dvb-usb/pctv452e.c1079
-rw-r--r--drivers/media/dvb/dvb-usb/technisat-usb2.c28
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.c407
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c8
-rw-r--r--drivers/media/dvb/dvb-usb/usb-urb.c4
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c5
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c5
-rw-r--r--drivers/media/dvb/frontends/Kconfig30
-rw-r--r--drivers/media/dvb/frontends/Makefile8
-rw-r--r--drivers/media/dvb/frontends/a8293.c184
-rw-r--r--drivers/media/dvb/frontends/a8293.h (renamed from drivers/media/common/tuners/tda18212_priv.h)39
-rw-r--r--drivers/media/dvb/frontends/cxd2820r.h9
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_c.c1
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_core.c80
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_priv.h1
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t.c1
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t2.c1
-rw-r--r--drivers/media/dvb/frontends/dib0070.c37
-rw-r--r--drivers/media/dvb/frontends/dib0090.c70
-rw-r--r--drivers/media/dvb/frontends/dib7000m.c27
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c34
-rw-r--r--drivers/media/dvb/frontends/dib8000.c72
-rw-r--r--drivers/media/dvb/frontends/dib9000.c167
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c76
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h1
-rw-r--r--drivers/media/dvb/frontends/drxd_hard.c24
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c10
-rw-r--r--drivers/media/dvb/frontends/it913x-fe-priv.h336
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c839
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.h196
-rw-r--r--drivers/media/dvb/frontends/lnbp22.c148
-rw-r--r--drivers/media/dvb/frontends/lnbp22.h57
-rw-r--r--drivers/media/dvb/frontends/stb0899_algo.c3
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c6
-rw-r--r--drivers/media/dvb/frontends/stv0288.c29
-rw-r--r--drivers/media/dvb/frontends/stv090x.c35
-rw-r--r--drivers/media/dvb/frontends/tda10048.c37
-rw-r--r--drivers/media/dvb/frontends/tda10048.h8
-rw-r--r--drivers/media/dvb/frontends/tda10071.c1269
-rw-r--r--drivers/media/dvb/frontends/tda10071.h81
-rw-r--r--drivers/media/dvb/frontends/tda10071_priv.h122
-rw-r--r--drivers/media/dvb/frontends/tda18271c2dd.c4
-rw-r--r--drivers/media/dvb/mantis/Makefile2
-rw-r--r--drivers/media/dvb/mantis/hopper_cards.c6
-rw-r--r--drivers/media/dvb/mantis/mantis_cards.c6
-rw-r--r--drivers/media/dvb/mantis/mantis_common.h5
-rw-r--r--drivers/media/dvb/mantis/mantis_dma.c92
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1041.c1
-rw-r--r--drivers/media/dvb/ngene/Makefile8
-rw-r--r--drivers/media/dvb/pluto2/Makefile2
-rw-r--r--drivers/media/dvb/pt1/Makefile2
-rw-r--r--drivers/media/dvb/siano/Makefile4
-rw-r--r--drivers/media/dvb/ttpci/Makefile4
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c32
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c47
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c1
-rw-r--r--drivers/media/dvb/ttpci/budget-core.c2
-rw-r--r--drivers/media/dvb/ttpci/budget.h1
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.c29
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.h1
-rw-r--r--drivers/media/dvb/ttusb-budget/Makefile2
-rw-r--r--drivers/media/dvb/ttusb-dec/Makefile2
-rw-r--r--drivers/media/radio/Kconfig1
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-si4713.c4
-rw-r--r--drivers/media/radio/radio-tea5764.c4
-rw-r--r--drivers/media/radio/radio-wl1273.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c2
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c6
-rw-r--r--drivers/media/rc/Kconfig23
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/ati_remote.c (renamed from drivers/input/misc/ati_remote.c)301
-rw-r--r--drivers/media/rc/ene_ir.c73
-rw-r--r--drivers/media/rc/ene_ir.h19
-rw-r--r--drivers/media/rc/imon.c36
-rw-r--r--drivers/media/rc/ir-lirc-codec.c9
-rw-r--r--drivers/media/rc/keymaps/Makefile3
-rw-r--r--drivers/media/rc/keymaps/rc-ati-x10.c104
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10.c117
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c1
-rw-r--r--drivers/media/rc/keymaps/rc-snapstream-firefly.c107
-rw-r--r--drivers/media/rc/mceusb.c410
-rw-r--r--drivers/media/rc/rc-core-priv.h14
-rw-r--r--drivers/media/rc/rc-main.c29
-rw-r--r--drivers/media/rc/redrat3.c7
-rw-r--r--drivers/media/rc/winbond-cir.c6
-rw-r--r--drivers/media/video/Kconfig61
-rw-r--r--drivers/media/video/Makefile11
-rw-r--r--drivers/media/video/adp1653.c20
-rw-r--r--drivers/media/video/adv7175.c62
-rw-r--r--drivers/media/video/atmel-isi.c164
-rw-r--r--drivers/media/video/au0828/Makefile8
-rw-r--r--drivers/media/video/bt819.c2
-rw-r--r--drivers/media/video/bt8xx/Makefile6
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c242
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c294
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c4
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c56
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c37
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c25
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c9
-rw-r--r--drivers/media/video/bt8xx/bttvp.h18
-rw-r--r--drivers/media/video/cx18/Makefile6
-rw-r--r--drivers/media/video/cx18/cx18-driver.c2
-rw-r--r--drivers/media/video/cx18/cx18-driver.h5
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c2
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c18
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c2
-rw-r--r--drivers/media/video/cx18/cx18-streams.c13
-rw-r--r--drivers/media/video/cx231xx/Makefile10
-rw-r--r--drivers/media/video/cx23885/Kconfig2
-rw-r--r--drivers/media/video/cx23885/Makefile12
-rw-r--r--drivers/media/video/cx23885/cx23885-alsa.c535
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c55
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c99
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c1
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h3
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c72
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c358
-rw-r--r--drivers/media/video/cx23885/cx23885.h56
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c12
-rw-r--r--drivers/media/video/cx25821/Kconfig (renamed from drivers/staging/cx25821/Kconfig)0
-rw-r--r--drivers/media/video/cx25821/Makefile (renamed from drivers/staging/cx25821/Makefile)0
-rw-r--r--drivers/media/video/cx25821/cx25821-alsa.c (renamed from drivers/staging/cx25821/cx25821-alsa.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c (renamed from drivers/staging/cx25821/cx25821-audio-upstream.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.h (renamed from drivers/staging/cx25821/cx25821-audio-upstream.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio.h (renamed from drivers/staging/cx25821/cx25821-audio.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-biffuncs.h (renamed from drivers/staging/cx25821/cx25821-biffuncs.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-cards.c (renamed from drivers/staging/cx25821/cx25821-cards.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c (renamed from drivers/staging/cx25821/cx25821-core.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-gpio.c (renamed from drivers/staging/cx25821/cx25821-gpio.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c (renamed from drivers/staging/cx25821/cx25821-i2c.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-defines.h (renamed from drivers/staging/cx25821/cx25821-medusa-defines.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-reg.h (renamed from drivers/staging/cx25821/cx25821-medusa-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c (renamed from drivers/staging/cx25821/cx25821-medusa-video.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.h (renamed from drivers/staging/cx25821/cx25821-medusa-video.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-reg.h (renamed from drivers/staging/cx25821/cx25821-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-sram.h (renamed from drivers/staging/cx25821/cx25821-sram.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.h (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c (renamed from drivers/staging/cx25821/cx25821-video-upstream.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.h (renamed from drivers/staging/cx25821/cx25821-video-upstream.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c (renamed from drivers/staging/cx25821/cx25821-video.c)2
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h (renamed from drivers/staging/cx25821/cx25821-video.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821.h (renamed from drivers/staging/cx25821/cx25821.h)3
-rw-r--r--drivers/media/video/cx25840/Makefile2
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c10
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c19
-rw-r--r--drivers/media/video/cx25840/cx25840-ir.c12
-rw-r--r--drivers/media/video/cx88/Makefile8
-rw-r--r--drivers/media/video/cx88/cx88-core.c3
-rw-r--r--drivers/media/video/cx88/cx88-video.c2
-rw-r--r--drivers/media/video/davinci/vpbe_display.c1
-rw-r--r--drivers/media/video/davinci/vpbe_osd.c2
-rw-r--r--drivers/media/video/em28xx/Kconfig2
-rw-r--r--drivers/media/video/em28xx/Makefile8
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c157
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c45
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c117
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c6
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c58
-rw-r--r--drivers/media/video/em28xx/em28xx.h3
-rw-r--r--drivers/media/video/et61x251/et61x251.h66
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2
-rw-r--r--drivers/media/video/et61x251/et61x251_tas5130d1b.c2
-rw-r--r--drivers/media/video/gspca/Kconfig10
-rw-r--r--drivers/media/video/gspca/Makefile2
-rw-r--r--drivers/media/video/gspca/benq.c31
-rw-r--r--drivers/media/video/gspca/conex.c6
-rw-r--r--drivers/media/video/gspca/cpia1.c7
-rw-r--r--drivers/media/video/gspca/etoms.c6
-rw-r--r--drivers/media/video/gspca/finepix.c8
-rw-r--r--drivers/media/video/gspca/gl860/Makefile2
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c8
-rw-r--r--drivers/media/video/gspca/gspca.c287
-rw-r--r--drivers/media/video/gspca/gspca.h22
-rw-r--r--drivers/media/video/gspca/jeilinj.c20
-rw-r--r--drivers/media/video/gspca/kinect.c41
-rw-r--r--drivers/media/video/gspca/konica.c16
-rw-r--r--drivers/media/video/gspca/m5602/Makefile2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c9
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c28
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.c21
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c19
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c21
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c35
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c30
-rw-r--r--drivers/media/video/gspca/mars.c6
-rw-r--r--drivers/media/video/gspca/mr97310a.c24
-rw-r--r--drivers/media/video/gspca/nw80x.c9
-rw-r--r--drivers/media/video/gspca/ov519.c41
-rw-r--r--drivers/media/video/gspca/ov534.c12
-rw-r--r--drivers/media/video/gspca/ov534_9.c516
-rw-r--r--drivers/media/video/gspca/pac207.c14
-rw-r--r--drivers/media/video/gspca/pac7302.c15
-rw-r--r--drivers/media/video/gspca/pac7311.c15
-rw-r--r--drivers/media/video/gspca/se401.c46
-rw-r--r--drivers/media/video/gspca/sn9c2028.c14
-rw-r--r--drivers/media/video/gspca/sn9c20x.c76
-rw-r--r--drivers/media/video/gspca/sonixj.c45
-rw-r--r--drivers/media/video/gspca/spca1528.c34
-rw-r--r--drivers/media/video/gspca/spca500.c6
-rw-r--r--drivers/media/video/gspca/spca501.c4
-rw-r--r--drivers/media/video/gspca/spca505.c8
-rw-r--r--drivers/media/video/gspca/spca508.c6
-rw-r--r--drivers/media/video/gspca/spca561.c4
-rw-r--r--drivers/media/video/gspca/sq905.c17
-rw-r--r--drivers/media/video/gspca/sq905c.c10
-rw-r--r--drivers/media/video/gspca/sq930x.c21
-rw-r--r--drivers/media/video/gspca/stk014.c16
-rw-r--r--drivers/media/video/gspca/stv0680.c6
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile2
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c18
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h6
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c10
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.c4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c32
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h56
-rw-r--r--drivers/media/video/gspca/sunplus.c10
-rw-r--r--drivers/media/video/gspca/t613.c12
-rw-r--r--drivers/media/video/gspca/topro.c4989
-rw-r--r--drivers/media/video/gspca/vc032x.c13
-rw-r--r--drivers/media/video/gspca/vicam.c12
-rw-r--r--drivers/media/video/gspca/w996Xcf.c8
-rw-r--r--drivers/media/video/gspca/xirlink_cit.c14
-rw-r--r--drivers/media/video/gspca/zc3xx.c15
-rw-r--r--drivers/media/video/hdpvr/Makefile4
-rw-r--r--drivers/media/video/hexium_gemini.c44
-rw-r--r--drivers/media/video/hexium_orion.c38
-rw-r--r--drivers/media/video/imx074.c54
-rw-r--r--drivers/media/video/ivtv/Makefile8
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c15
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c6
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c15
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c1
-rw-r--r--drivers/media/video/mem2mem_testdev.c21
-rw-r--r--drivers/media/video/msp3400-driver.c20
-rw-r--r--drivers/media/video/msp3400-driver.h2
-rw-r--r--drivers/media/video/msp3400-kthreads.c86
-rw-r--r--drivers/media/video/mt9m001.c328
-rw-r--r--drivers/media/video/mt9m111.c265
-rw-r--r--drivers/media/video/mt9p031.c964
-rw-r--r--drivers/media/video/mt9t001.c836
-rw-r--r--drivers/media/video/mt9t031.c347
-rw-r--r--drivers/media/video/mt9t112.c269
-rw-r--r--drivers/media/video/mt9v022.c447
-rw-r--r--drivers/media/video/mx1_camera.c71
-rw-r--r--drivers/media/video/mx2_camera.c78
-rw-r--r--drivers/media/video/mx3_camera.c361
-rw-r--r--drivers/media/video/mxb.c80
-rw-r--r--drivers/media/video/noon010pc30.c263
-rw-r--r--drivers/media/video/omap/omap_vout.c28
-rw-r--r--drivers/media/video/omap1_camera.c62
-rw-r--r--drivers/media/video/omap3isp/Makefile4
-rw-r--r--drivers/media/video/omap3isp/isp.c54
-rw-r--r--drivers/media/video/omap3isp/isp.h90
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c122
-rw-r--r--drivers/media/video/omap3isp/ispccp2.c129
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c91
-rw-r--r--drivers/media/video/omap3isp/isph3a_aewb.c2
-rw-r--r--drivers/media/video/omap3isp/isph3a_af.c2
-rw-r--r--drivers/media/video/omap3isp/isphist.c2
-rw-r--r--drivers/media/video/omap3isp/isppreview.c419
-rw-r--r--drivers/media/video/omap3isp/isppreview.h9
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c4
-rw-r--r--drivers/media/video/omap3isp/ispreg.h3
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c104
-rw-r--r--drivers/media/video/omap3isp/ispstat.c63
-rw-r--r--drivers/media/video/omap3isp/ispstat.h2
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c37
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h1
-rw-r--r--drivers/media/video/ov2640.c178
-rw-r--r--drivers/media/video/ov5642.c288
-rw-r--r--drivers/media/video/ov6650.c504
-rw-r--r--drivers/media/video/ov772x.c198
-rw-r--r--drivers/media/video/ov9640.c186
-rw-r--r--drivers/media/video/ov9640.h4
-rw-r--r--drivers/media/video/ov9740.c151
-rw-r--r--drivers/media/video/pvrusb2/Makefile8
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c7
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h3
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c8
-rw-r--r--drivers/media/video/pwc/pwc-if.c8
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c136
-rw-r--r--drivers/media/video/pxa_camera.c140
-rw-r--r--drivers/media/video/rj54n1cb0c.c223
-rw-r--r--drivers/media/video/s5k6aa.c1680
-rw-r--r--drivers/media/video/s5p-fimc/Makefile2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c1466
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c1138
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h221
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c858
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h118
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c90
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c90
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h9
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c15
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c4
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c39
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c14
-rw-r--r--drivers/media/video/s5p-tv/Kconfig2
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c15
-rw-r--r--drivers/media/video/s5p-tv/mixer.h2
-rw-r--r--drivers/media/video/s5p-tv/mixer_grp_layer.c2
-rw-r--r--drivers/media/video/s5p-tv/mixer_reg.c11
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c26
-rw-r--r--drivers/media/video/s5p-tv/mixer_vp_layer.c4
-rw-r--r--drivers/media/video/s5p-tv/regs-hdmi.h4
-rw-r--r--drivers/media/video/s5p-tv/regs-mixer.h1
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c1
-rw-r--r--drivers/media/video/saa7115.c53
-rw-r--r--drivers/media/video/saa7134/Makefile8
-rw-r--r--drivers/media/video/saa7134/saa7134.h12
-rw-r--r--drivers/media/video/saa7164/Makefile10
-rw-r--r--drivers/media/video/saa7164/saa7164-cards.c128
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c2
-rw-r--r--drivers/media/video/saa7164/saa7164.h2
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c497
-rw-r--r--drivers/media/video/sh_mobile_csi2.c132
-rw-r--r--drivers/media/video/soc_camera.c273
-rw-r--r--drivers/media/video/soc_camera_platform.c45
-rw-r--r--drivers/media/video/soc_mediabus.c33
-rw-r--r--drivers/media/video/sr030pc30.c6
-rw-r--r--drivers/media/video/stk-webcam.c29
-rw-r--r--drivers/media/video/tlg2300/Makefile8
-rw-r--r--drivers/media/video/tm6000/Kconfig (renamed from drivers/staging/tm6000/Kconfig)0
-rw-r--r--drivers/media/video/tm6000/Makefile (renamed from drivers/staging/tm6000/Makefile)0
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c (renamed from drivers/staging/tm6000/tm6000-alsa.c)9
-rw-r--r--drivers/media/video/tm6000/tm6000-cards.c (renamed from drivers/staging/tm6000/tm6000-cards.c)44
-rw-r--r--drivers/media/video/tm6000/tm6000-core.c (renamed from drivers/staging/tm6000/tm6000-core.c)108
-rw-r--r--drivers/media/video/tm6000/tm6000-dvb.c (renamed from drivers/staging/tm6000/tm6000-dvb.c)18
-rw-r--r--drivers/media/video/tm6000/tm6000-i2c.c (renamed from drivers/staging/tm6000/tm6000-i2c.c)21
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c (renamed from drivers/staging/tm6000/tm6000-input.c)2
-rw-r--r--drivers/media/video/tm6000/tm6000-regs.h (renamed from drivers/staging/tm6000/tm6000-regs.h)6
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c659
-rw-r--r--drivers/media/video/tm6000/tm6000-usb-isoc.h (renamed from drivers/staging/tm6000/tm6000-usb-isoc.h)2
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c (renamed from drivers/staging/tm6000/tm6000-video.c)122
-rw-r--r--drivers/media/video/tm6000/tm6000.h (renamed from drivers/staging/tm6000/tm6000.h)15
-rw-r--r--drivers/media/video/tvaudio.c9
-rw-r--r--drivers/media/video/tvp5150_reg.h17
-rw-r--r--drivers/media/video/tvp7002.c14
-rw-r--r--drivers/media/video/tw9910.c268
-rw-r--r--drivers/media/video/usbvision/Makefile4
-rw-r--r--drivers/media/video/uvc/uvc_driver.c13
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c54
-rw-r--r--drivers/media/video/uvc/uvc_video.c17
-rw-r--r--drivers/media/video/uvc/uvcvideo.h104
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c76
-rw-r--r--drivers/media/video/v4l2-ctrls.c105
-rw-r--r--drivers/media/video/v4l2-device.c36
-rw-r--r--drivers/media/video/v4l2-ioctl.c559
-rw-r--r--drivers/media/video/v4l2-mem2mem.c18
-rw-r--r--drivers/media/video/v4l2-subdev.c19
-rw-r--r--drivers/media/video/videobuf2-core.c594
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c16
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c6
-rw-r--r--drivers/media/video/videobuf2-memops.c6
-rw-r--r--drivers/media/video/vivi.c27
-rw-r--r--drivers/media/video/vpx3220.c2
-rw-r--r--drivers/media/video/zr364xx.c3
-rw-r--r--drivers/mfd/Kconfig64
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/aat2870-core.c2
-rw-r--r--drivers/mfd/ab3100-core.c2
-rw-r--r--drivers/mfd/ab3550-core.c1380
-rw-r--r--drivers/mfd/ab5500-core.c1439
-rw-r--r--drivers/mfd/ab5500-core.h87
-rw-r--r--drivers/mfd/ab5500-debugfs.c806
-rw-r--r--drivers/mfd/ab5500-debugfs.h22
-rw-r--r--drivers/mfd/ab8500-core.c33
-rw-r--r--drivers/mfd/ab8500-gpadc.c56
-rw-r--r--drivers/mfd/asic3.c26
-rw-r--r--drivers/mfd/da903x.c2
-rw-r--r--drivers/mfd/db5500-prcmu.c29
-rw-r--r--drivers/mfd/db8500-prcmu-regs.h166
-rw-r--r--drivers/mfd/db8500-prcmu.c629
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h (renamed from drivers/mfd/db5500-prcmu-regs.h)105
-rw-r--r--drivers/mfd/intel_msic.c502
-rw-r--r--drivers/mfd/jz4740-adc.c2
-rw-r--r--drivers/mfd/max8997.c27
-rw-r--r--drivers/mfd/mc13xxx-core.c116
-rw-r--r--drivers/mfd/menelaus.c2
-rw-r--r--drivers/mfd/pcf50633-core.c114
-rw-r--r--drivers/mfd/tc3589x.c2
-rw-r--r--drivers/mfd/timberdale.c9
-rw-r--r--drivers/mfd/tps65912-core.c6
-rw-r--r--drivers/mfd/twl-core.c2
-rw-r--r--drivers/mfd/twl4030-irq.c342
-rw-r--r--drivers/mfd/twl4030-madc.c22
-rw-r--r--drivers/mfd/twl6030-irq.c75
-rw-r--r--drivers/mfd/wm831x-irq.c24
-rw-r--r--drivers/mfd/wm8994-core.c53
-rw-r--r--drivers/misc/Kconfig3
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c1
-rw-r--r--drivers/misc/altera-stapl/Kconfig (renamed from drivers/staging/altera-stapl/Kconfig)2
-rw-r--r--drivers/misc/altera-stapl/Makefile3
-rw-r--r--drivers/misc/altera-stapl/altera-comp.c (renamed from drivers/staging/altera-stapl/altera-comp.c)0
-rw-r--r--drivers/misc/altera-stapl/altera-exprt.h (renamed from drivers/staging/altera-stapl/altera-exprt.h)0
-rw-r--r--drivers/misc/altera-stapl/altera-jtag.c (renamed from drivers/staging/altera-stapl/altera-jtag.c)2
-rw-r--r--drivers/misc/altera-stapl/altera-jtag.h (renamed from drivers/staging/altera-stapl/altera-jtag.h)0
-rw-r--r--drivers/misc/altera-stapl/altera-lpt.c (renamed from drivers/staging/altera-stapl/altera-lpt.c)0
-rw-r--r--drivers/misc/altera-stapl/altera.c (renamed from drivers/staging/altera-stapl/altera.c)2
-rw-r--r--drivers/misc/fsa9480.c3
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c389
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h9
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c45
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c2
-rw-r--r--drivers/misc/vmw_balloon.c4
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/au1xmmc.c92
-rw-r--r--drivers/mmc/host/davinci_mmc.c13
-rw-r--r--drivers/mmc/host/mxcmmc.c1
-rw-r--r--drivers/mmc/host/omap_hsmmc.c2
-rw-r--r--drivers/mmc/host/s3cmci.c6
-rw-r--r--drivers/mtd/maps/lantiq-flash.c4
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/au1550nd.c6
-rw-r--r--drivers/net/bonding/bond_3ad.c8
-rw-r--r--drivers/net/bonding/bond_alb.c16
-rw-r--r--drivers/net/bonding/bond_main.c133
-rw-r--r--drivers/net/bonding/bond_procfs.c12
-rw-r--r--drivers/net/bonding/bonding.h1
-rw-r--r--drivers/net/ethernet/Kconfig1
-rw-r--r--drivers/net/ethernet/Makefile1
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c48
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.h2
-rw-r--r--drivers/net/ethernet/apple/Kconfig12
-rw-r--r--drivers/net/ethernet/apple/Makefile1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h4
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c217
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h3
-rw-r--r--drivers/net/ethernet/cirrus/Kconfig14
-rw-r--r--drivers/net/ethernet/cirrus/Makefile1
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c (renamed from drivers/net/ethernet/apple/mac89x0.c)0
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c412
-rw-r--r--drivers/net/ethernet/emulex/benet/be_hw.h2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c94
-rw-r--r--drivers/net/ethernet/i825xx/3c505.c6
-rw-r--r--drivers/net/ethernet/i825xx/Kconfig2
-rw-r--r--drivers/net/ethernet/intel/e100.c4
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c3
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c20
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c10
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c48
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c36
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mr.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/pd.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c72
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/qp.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/srq.c20
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c45
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c50
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c48
-rw-r--r--drivers/net/ethernet/realtek/Kconfig12
-rw-r--r--drivers/net/ethernet/realtek/Makefile1
-rw-r--r--drivers/net/ethernet/silan/Kconfig33
-rw-r--r--drivers/net/ethernet/silan/Makefile5
-rw-r--r--drivers/net/ethernet/silan/sc92031.c (renamed from drivers/net/ethernet/realtek/sc92031.c)0
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h31
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/norm_desc.c38
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c13
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c4
-rw-r--r--drivers/net/irda/Kconfig2
-rw-r--r--drivers/net/rionet.c4
-rw-r--r--drivers/net/virtio_net.c14
-rw-r--r--drivers/net/wireless/at76c50x-usb.c2
-rw-r--r--drivers/net/wireless/ath/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ath.h5
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.h4
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.h4
-rw-r--r--drivers/net/wireless/b43/b43.h12
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h16
-rw-r--r--drivers/net/wireless/rtlwifi/Kconfig4
-rw-r--r--drivers/of/base.c84
-rw-r--r--drivers/of/irq.c107
-rw-r--r--drivers/of/platform.c28
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c2
-rw-r--r--drivers/platform/x86/Kconfig17
-rw-r--r--drivers/platform/x86/acer-wmi.c488
-rw-r--r--drivers/platform/x86/asus-laptop.c378
-rw-r--r--drivers/platform/x86/asus-wmi.c4
-rw-r--r--drivers/platform/x86/dell-laptop.c84
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/hp_accel.c15
-rw-r--r--drivers/platform/x86/ideapad-laptop.c251
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c8
-rw-r--r--drivers/platform/x86/samsung-laptop.c107
-rw-r--r--drivers/platform/x86/sony-laptop.c2
-rw-r--r--drivers/platform/x86/topstar-laptop.c1
-rw-r--r--drivers/platform/x86/toshiba_acpi.c641
-rw-r--r--drivers/platform/x86/wmi.c6
-rw-r--r--drivers/power/Kconfig1
-rw-r--r--drivers/power/ds2780_battery.c86
-rw-r--r--drivers/pps/clients/Kconfig9
-rw-r--r--drivers/pps/clients/Makefile1
-rw-r--r--drivers/pps/clients/pps-gpio.c227
-rw-r--r--drivers/pps/clients/pps-ktimer.c12
-rw-r--r--drivers/pps/clients/pps_parport.c9
-rw-r--r--drivers/pps/kapi.c20
-rw-r--r--drivers/rapidio/Kconfig6
-rw-r--r--drivers/rapidio/Makefile1
-rw-r--r--drivers/rapidio/devices/Kconfig10
-rw-r--r--drivers/rapidio/devices/Makefile5
-rw-r--r--drivers/rapidio/devices/tsi721.c2360
-rw-r--r--drivers/rapidio/devices/tsi721.h766
-rw-r--r--drivers/rapidio/rio-scan.c6
-rw-r--r--drivers/regulator/88pm8607.c2
-rw-r--r--drivers/regulator/Kconfig10
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/aat2870-regulator.c1
-rw-r--r--drivers/regulator/core.c43
-rw-r--r--drivers/regulator/db8500-prcmu.c2
-rw-r--r--drivers/regulator/gpio-regulator.c358
-rw-r--r--drivers/regulator/max8649.c2
-rw-r--r--drivers/regulator/max8952.c3
-rw-r--r--drivers/regulator/mc13783-regulator.c6
-rw-r--r--drivers/regulator/tps65023-regulator.c183
-rw-r--r--drivers/regulator/tps6507x-regulator.c8
-rw-r--r--drivers/regulator/tps6586x-regulator.c32
-rw-r--r--drivers/regulator/tps65912-regulator.c2
-rw-r--r--drivers/rtc/Kconfig2
-rw-r--r--drivers/rtc/class.c32
-rw-r--r--drivers/rtc/rtc-ds1307.c27
-rw-r--r--drivers/rtc/rtc-mc13xxx.c6
-rw-r--r--drivers/s390/block/dasd.c19
-rw-r--r--drivers/s390/block/dasd_eckd.c45
-rw-r--r--drivers/s390/block/dasd_fba.c1
-rw-r--r--drivers/s390/block/dasd_int.h2
-rw-r--r--drivers/s390/block/dcssblk.c7
-rw-r--r--drivers/s390/block/xpram.c5
-rw-r--r--drivers/s390/char/con3215.c3
-rw-r--r--drivers/s390/char/raw3270.c3
-rw-r--r--drivers/s390/char/sclp_cmd.c4
-rw-r--r--drivers/s390/char/sclp_quiesce.c3
-rw-r--r--drivers/s390/char/tape_34xx.c1
-rw-r--r--drivers/s390/char/tape_3590.c1
-rw-r--r--drivers/s390/char/tape_core.c2
-rw-r--r--drivers/s390/char/vmur.c3
-rw-r--r--drivers/s390/char/zcore.c21
-rw-r--r--drivers/s390/cio/ccwgroup.c333
-rw-r--r--drivers/s390/cio/ccwreq.c23
-rw-r--r--drivers/s390/cio/chsc_sch.c5
-rw-r--r--drivers/s390/cio/cio.c19
-rw-r--r--drivers/s390/cio/css.h2
-rw-r--r--drivers/s390/cio/device.c13
-rw-r--r--drivers/s390/cio/device.h13
-rw-r--r--drivers/s390/cio/io_sch.h2
-rw-r--r--drivers/s390/cio/qdio.h40
-rw-r--r--drivers/s390/cio/qdio_debug.c10
-rw-r--r--drivers/s390/cio/qdio_main.c102
-rw-r--r--drivers/s390/cio/qdio_thinint.c52
-rw-r--r--drivers/s390/kvm/kvm_virtio.c8
-rw-r--r--drivers/s390/net/claw.c3
-rw-r--r--drivers/s390/net/ctcm_main.c3
-rw-r--r--drivers/s390/net/ctcm_sysfs.c2
-rw-r--r--drivers/s390/net/lcs.c5
-rw-r--r--drivers/s390/net/qeth_l3_main.c2
-rw-r--r--drivers/scsi/hpsa.c7
-rw-r--r--drivers/sh/intc/chip.c9
-rw-r--r--drivers/sh/intc/userimask.c1
-rw-r--r--drivers/sh/pfc.c29
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/spi-altera.c13
-rw-r--r--drivers/spi/spi-ath79.c13
-rw-r--r--drivers/spi/spi-atmel.c13
-rw-r--r--drivers/spi/spi-bfin-sport.c13
-rw-r--r--drivers/spi/spi-bfin5xx.c2
-rw-r--r--drivers/spi/spi-coldfire-qspi.c16
-rw-r--r--drivers/spi/spi-davinci.c20
-rw-r--r--drivers/spi/spi-dw-mid.c9
-rw-r--r--drivers/spi/spi-dw-mmio.c14
-rw-r--r--drivers/spi/spi-dw.c62
-rw-r--r--drivers/spi/spi-dw.h97
-rw-r--r--drivers/spi/spi-ep93xx.c21
-rw-r--r--drivers/spi/spi-fsl-espi.c13
-rw-r--r--drivers/spi/spi-gpio.c21
-rw-r--r--drivers/spi/spi-imx.c14
-rw-r--r--drivers/spi/spi-mpc512x-psc.c13
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c13
-rw-r--r--drivers/spi/spi-mpc52xx.c14
-rw-r--r--drivers/spi/spi-nuc900.c14
-rw-r--r--drivers/spi/spi-oc-tiny.c13
-rw-r--r--drivers/spi/spi-omap2-mcspi.c7
-rw-r--r--drivers/spi/spi-pl022.c146
-rw-r--r--drivers/spi/spi-ppc4xx.c15
-rw-r--r--drivers/spi/spi-s3c24xx.c21
-rw-r--r--drivers/spi/spi-s3c64xx.c175
-rw-r--r--drivers/spi/spi-sh-msiof.c15
-rw-r--r--drivers/spi/spi-sh-sci.c13
-rw-r--r--drivers/spi/spi-sh.c15
-rw-r--r--drivers/spi/spi-stmp.c13
-rw-r--r--drivers/spi/spi-tegra.c17
-rw-r--r--drivers/spi/spi-ti-ssp.c13
-rw-r--r--drivers/spi/spi-xilinx.c13
-rw-r--r--drivers/staging/Kconfig20
-rw-r--r--drivers/staging/Makefile10
-rw-r--r--drivers/staging/altera-stapl/Makefile3
-rw-r--r--drivers/staging/altera-stapl/altera.h49
-rw-r--r--drivers/staging/cx25821/README6
-rw-r--r--drivers/staging/iio/trigger.h3
-rw-r--r--drivers/staging/media/Kconfig37
-rw-r--r--drivers/staging/media/Makefile7
-rw-r--r--drivers/staging/media/as102/Kconfig7
-rw-r--r--drivers/staging/media/as102/Makefile6
-rw-r--r--drivers/staging/media/as102/as102_drv.c351
-rw-r--r--drivers/staging/media/as102/as102_drv.h141
-rw-r--r--drivers/staging/media/as102/as102_fe.c603
-rw-r--r--drivers/staging/media/as102/as102_fw.c251
-rw-r--r--drivers/staging/media/as102/as102_fw.h42
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.c478
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.h59
-rw-r--r--drivers/staging/media/as102/as10x_cmd.c452
-rw-r--r--drivers/staging/media/as102/as10x_cmd.h540
-rw-r--r--drivers/staging/media/as102/as10x_cmd_cfg.c215
-rw-r--r--drivers/staging/media/as102/as10x_cmd_stream.c223
-rw-r--r--drivers/staging/media/as102/as10x_handle.h58
-rw-r--r--drivers/staging/media/as102/as10x_types.h198
-rw-r--r--drivers/staging/media/cxd2099/Kconfig (renamed from drivers/staging/cxd2099/Kconfig)0
-rw-r--r--drivers/staging/media/cxd2099/Makefile (renamed from drivers/staging/cxd2099/Makefile)0
-rw-r--r--drivers/staging/media/cxd2099/TODO (renamed from drivers/staging/cxd2099/TODO)0
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c (renamed from drivers/staging/cxd2099/cxd2099.c)0
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.h (renamed from drivers/staging/cxd2099/cxd2099.h)0
-rw-r--r--drivers/staging/media/dt3155v4l/Kconfig (renamed from drivers/staging/dt3155v4l/Kconfig)0
-rw-r--r--drivers/staging/media/dt3155v4l/Makefile (renamed from drivers/staging/dt3155v4l/Makefile)0
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c (renamed from drivers/staging/dt3155v4l/dt3155v4l.c)4
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.h (renamed from drivers/staging/dt3155v4l/dt3155v4l.h)0
-rw-r--r--drivers/staging/media/easycap/Kconfig (renamed from drivers/staging/easycap/Kconfig)0
-rw-r--r--drivers/staging/media/easycap/Makefile (renamed from drivers/staging/easycap/Makefile)0
-rw-r--r--drivers/staging/media/easycap/README (renamed from drivers/staging/easycap/README)0
-rw-r--r--drivers/staging/media/easycap/easycap.h (renamed from drivers/staging/easycap/easycap.h)0
-rw-r--r--drivers/staging/media/easycap/easycap_ioctl.c (renamed from drivers/staging/easycap/easycap_ioctl.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_low.c (renamed from drivers/staging/easycap/easycap_low.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_main.c (renamed from drivers/staging/easycap/easycap_main.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_settings.c (renamed from drivers/staging/easycap/easycap_settings.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_sound.c (renamed from drivers/staging/easycap/easycap_sound.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_testcard.c (renamed from drivers/staging/easycap/easycap_testcard.c)0
-rw-r--r--drivers/staging/media/go7007/Kconfig (renamed from drivers/staging/go7007/Kconfig)0
-rw-r--r--drivers/staging/media/go7007/Makefile (renamed from drivers/staging/go7007/Makefile)0
-rw-r--r--drivers/staging/media/go7007/README (renamed from drivers/staging/go7007/README)0
-rw-r--r--drivers/staging/media/go7007/go7007-driver.c (renamed from drivers/staging/go7007/go7007-driver.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-fw.c (renamed from drivers/staging/go7007/go7007-fw.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-i2c.c (renamed from drivers/staging/go7007/go7007-i2c.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-priv.h (renamed from drivers/staging/go7007/go7007-priv.h)0
-rw-r--r--drivers/staging/media/go7007/go7007-usb.c (renamed from drivers/staging/go7007/go7007-usb.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c (renamed from drivers/staging/go7007/go7007-v4l2.c)0
-rw-r--r--drivers/staging/media/go7007/go7007.h (renamed from drivers/staging/go7007/go7007.h)0
-rw-r--r--drivers/staging/media/go7007/go7007.txt (renamed from drivers/staging/go7007/go7007.txt)0
-rw-r--r--drivers/staging/media/go7007/s2250-board.c (renamed from drivers/staging/go7007/s2250-board.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.c (renamed from drivers/staging/go7007/s2250-loader.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.h (renamed from drivers/staging/go7007/s2250-loader.h)0
-rw-r--r--drivers/staging/media/go7007/saa7134-go7007.c (renamed from drivers/staging/go7007/saa7134-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/snd-go7007.c (renamed from drivers/staging/go7007/snd-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/wis-i2c.h (renamed from drivers/staging/go7007/wis-i2c.h)0
-rw-r--r--drivers/staging/media/go7007/wis-ov7640.c (renamed from drivers/staging/go7007/wis-ov7640.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7113.c (renamed from drivers/staging/go7007/wis-saa7113.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7115.c (renamed from drivers/staging/go7007/wis-saa7115.c)0
-rw-r--r--drivers/staging/media/go7007/wis-sony-tuner.c (renamed from drivers/staging/go7007/wis-sony-tuner.c)0
-rw-r--r--drivers/staging/media/go7007/wis-tw2804.c (renamed from drivers/staging/go7007/wis-tw2804.c)0
-rw-r--r--drivers/staging/media/go7007/wis-tw9903.c (renamed from drivers/staging/go7007/wis-tw9903.c)0
-rw-r--r--drivers/staging/media/go7007/wis-uda1342.c (renamed from drivers/staging/go7007/wis-uda1342.c)0
-rw-r--r--drivers/staging/media/lirc/Kconfig (renamed from drivers/staging/lirc/Kconfig)0
-rw-r--r--drivers/staging/media/lirc/Makefile (renamed from drivers/staging/lirc/Makefile)0
-rw-r--r--drivers/staging/media/lirc/TODO (renamed from drivers/staging/lirc/TODO)0
-rw-r--r--drivers/staging/media/lirc/TODO.lirc_zilog (renamed from drivers/staging/lirc/TODO.lirc_zilog)0
-rw-r--r--drivers/staging/media/lirc/lirc_bt829.c (renamed from drivers/staging/lirc/lirc_bt829.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_ene0100.h (renamed from drivers/staging/lirc/lirc_ene0100.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_igorplugusb.c (renamed from drivers/staging/lirc/lirc_igorplugusb.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c (renamed from drivers/staging/lirc/lirc_imon.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c (renamed from drivers/staging/lirc/lirc_parallel.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.h (renamed from drivers/staging/lirc/lirc_parallel.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_sasem.c (renamed from drivers/staging/lirc/lirc_sasem.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c (renamed from drivers/staging/lirc/lirc_serial.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c (renamed from drivers/staging/lirc/lirc_sir.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_ttusbir.c (renamed from drivers/staging/lirc/lirc_ttusbir.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c (renamed from drivers/staging/lirc/lirc_zilog.c)0
-rw-r--r--drivers/staging/media/solo6x10/Kconfig (renamed from drivers/staging/solo6x10/Kconfig)0
-rw-r--r--drivers/staging/media/solo6x10/Makefile (renamed from drivers/staging/solo6x10/Makefile)0
-rw-r--r--drivers/staging/media/solo6x10/TODO (renamed from drivers/staging/solo6x10/TODO)0
-rw-r--r--drivers/staging/media/solo6x10/core.c (renamed from drivers/staging/solo6x10/core.c)0
-rw-r--r--drivers/staging/media/solo6x10/disp.c (renamed from drivers/staging/solo6x10/disp.c)0
-rw-r--r--drivers/staging/media/solo6x10/enc.c (renamed from drivers/staging/solo6x10/enc.c)0
-rw-r--r--drivers/staging/media/solo6x10/g723.c (renamed from drivers/staging/solo6x10/g723.c)0
-rw-r--r--drivers/staging/media/solo6x10/gpio.c (renamed from drivers/staging/solo6x10/gpio.c)0
-rw-r--r--drivers/staging/media/solo6x10/i2c.c (renamed from drivers/staging/solo6x10/i2c.c)0
-rw-r--r--drivers/staging/media/solo6x10/jpeg.h (renamed from drivers/staging/solo6x10/jpeg.h)0
-rw-r--r--drivers/staging/media/solo6x10/offsets.h (renamed from drivers/staging/solo6x10/offsets.h)0
-rw-r--r--drivers/staging/media/solo6x10/osd-font.h (renamed from drivers/staging/solo6x10/osd-font.h)0
-rw-r--r--drivers/staging/media/solo6x10/p2m.c (renamed from drivers/staging/solo6x10/p2m.c)0
-rw-r--r--drivers/staging/media/solo6x10/registers.h (renamed from drivers/staging/solo6x10/registers.h)0
-rw-r--r--drivers/staging/media/solo6x10/solo6x10.h (renamed from drivers/staging/solo6x10/solo6x10.h)0
-rw-r--r--drivers/staging/media/solo6x10/tw28.c (renamed from drivers/staging/solo6x10/tw28.c)0
-rw-r--r--drivers/staging/media/solo6x10/tw28.h (renamed from drivers/staging/solo6x10/tw28.h)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2-enc.c (renamed from drivers/staging/solo6x10/v4l2-enc.c)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2.c (renamed from drivers/staging/solo6x10/v4l2.c)0
-rw-r--r--drivers/staging/pohmelfs/inode.c2
-rw-r--r--drivers/staging/tm6000/CARDLIST16
-rw-r--r--drivers/staging/tm6000/README22
-rw-r--r--drivers/staging/tm6000/TODO8
-rw-r--r--drivers/staging/tm6000/tm6000-stds.c679
-rw-r--r--drivers/staging/xgifb/XGI_main_26.c25
-rw-r--r--drivers/staging/zram/zram_drv.c5
-rw-r--r--drivers/tty/Kconfig2
-rw-r--r--drivers/tty/serial/msm_serial.c30
-rw-r--r--drivers/tty/serial/of_serial.c12
-rw-r--r--drivers/tty/serial/sh-sci.c25
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/alchemy-common.c337
-rw-r--r--drivers/usb/host/ehci-au1xxx.c77
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ohci-at91.c239
-rw-r--r--drivers/usb/host/ohci-au1xxx.c110
-rw-r--r--drivers/video/68328fb.c4
-rw-r--r--drivers/video/Kconfig38
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/acornfb.c5
-rw-r--r--drivers/video/arkfb.c9
-rw-r--r--drivers/video/atmel_lcdfb.c15
-rw-r--r--drivers/video/aty/radeon_base.c10
-rw-r--r--drivers/video/au1100fb.c181
-rw-r--r--drivers/video/au1100fb.h6
-rw-r--r--drivers/video/au1200fb.c299
-rw-r--r--drivers/video/backlight/adp8860_bl.c1
-rw-r--r--drivers/video/backlight/adp8870_bl.c3
-rw-r--r--drivers/video/backlight/generic_bl.c4
-rw-r--r--drivers/video/backlight/l4f00242t03.c70
-rw-r--r--drivers/video/bf54x-lq043fb.c2
-rw-r--r--drivers/video/bfin-lq035q1-fb.c2
-rw-r--r--drivers/video/bfin-t350mcqb-fb.c2
-rw-r--r--drivers/video/bfin_adv7393fb.c2
-rw-r--r--drivers/video/carminefb.c6
-rw-r--r--drivers/video/controlfb.c2
-rw-r--r--drivers/video/da8xx-fb.c186
-rw-r--r--drivers/video/fb-puv3.c4
-rw-r--r--drivers/video/fb_defio.c3
-rw-r--r--drivers/video/fbmem.c3
-rw-r--r--drivers/video/fbmon.c21
-rw-r--r--drivers/video/fbsysfs.c3
-rw-r--r--drivers/video/fsl-diu-fb.c992
-rw-r--r--drivers/video/g364fb.c5
-rw-r--r--drivers/video/grvga.c579
-rw-r--r--drivers/video/gxt4500.c4
-rw-r--r--drivers/video/hgafb.c4
-rw-r--r--drivers/video/imsttfb.c2
-rw-r--r--drivers/video/intelfb/intelfbhw.c6
-rw-r--r--drivers/video/mb862xx/mb862xx-i2c.c2
-rw-r--r--drivers/video/mb862xx/mb862xxfbdrv.c6
-rw-r--r--drivers/video/mbx/mbxfb.c6
-rw-r--r--drivers/video/modedb.c444
-rw-r--r--drivers/video/msm/mddi.c2
-rw-r--r--drivers/video/msm/mdp.c6
-rw-r--r--drivers/video/mx3fb.c19
-rw-r--r--drivers/video/mxsfb.c1
-rw-r--r--drivers/video/neofb.c4
-rw-r--r--drivers/video/nuc900fb.c3
-rw-r--r--drivers/video/omap/Kconfig29
-rw-r--r--drivers/video/omap/Makefile8
-rw-r--r--drivers/video/omap/lcd_2430sdp.c203
-rw-r--r--drivers/video/omap/lcd_apollon.c136
-rw-r--r--drivers/video/omap/lcd_h4.c117
-rw-r--r--drivers/video/omap/lcd_ldp.c201
-rw-r--r--drivers/video/omap/lcd_omap3beagle.c130
-rw-r--r--drivers/video/omap/lcd_omap3evm.c193
-rw-r--r--drivers/video/omap/lcd_overo.c180
-rw-r--r--drivers/video/omap2/displays/Kconfig28
-rw-r--r--drivers/video/omap2/displays/Makefile3
-rw-r--r--drivers/video/omap2/displays/panel-dvi.c363
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c113
-rw-r--r--drivers/video/omap2/displays/panel-n8x0.c747
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.c594
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.h288
-rw-r--r--drivers/video/omap2/displays/panel-taal.c125
-rw-r--r--drivers/video/omap2/dss/Kconfig2
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/core.c4
-rw-r--r--drivers/video/omap2/dss/dispc.c1700
-rw-r--r--drivers/video/omap2/dss/dispc.h57
-rw-r--r--drivers/video/omap2/dss/display.c31
-rw-r--r--drivers/video/omap2/dss/dpi.c28
-rw-r--r--drivers/video/omap2/dss/dsi.c929
-rw-r--r--drivers/video/omap2/dss/dss.c18
-rw-r--r--drivers/video/omap2/dss/dss.h156
-rw-r--r--drivers/video/omap2/dss/dss_features.c130
-rw-r--r--drivers/video/omap2/dss/dss_features.h17
-rw-r--r--drivers/video/omap2/dss/hdmi.c1260
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c (renamed from drivers/video/omap2/dss/hdmi_omap4_panel.c)68
-rw-r--r--drivers/video/omap2/dss/manager.c191
-rw-r--r--drivers/video/omap2/dss/overlay.c122
-rw-r--r--drivers/video/omap2/dss/rfbi.c45
-rw-r--r--drivers/video/omap2/dss/sdi.c19
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h138
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c1239
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h (renamed from drivers/video/omap2/dss/hdmi.h)400
-rw-r--r--drivers/video/omap2/dss/venc.c27
-rw-r--r--drivers/video/omap2/omapfb/Kconfig2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c134
-rw-r--r--drivers/video/omap2/omapfb/omapfb-sysfs.c6
-rw-r--r--drivers/video/platinumfb.c4
-rw-r--r--drivers/video/pm2fb.c6
-rw-r--r--drivers/video/pm3fb.c6
-rw-r--r--drivers/video/ps3fb.c2
-rw-r--r--drivers/video/pxa3xx-gcu.c4
-rw-r--r--drivers/video/pxafb.c12
-rw-r--r--drivers/video/s3c-fb.c117
-rw-r--r--drivers/video/s3c2410fb.c3
-rw-r--r--drivers/video/s3fb.c9
-rw-r--r--drivers/video/sa1100fb.c3
-rw-r--r--drivers/video/savage/savagefb_driver.c16
-rw-r--r--drivers/video/sh_mobile_hdmi.c47
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1162
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h12
-rw-r--r--drivers/video/sh_mobile_meram.c208
-rw-r--r--drivers/video/sh_mobile_meram.h41
-rw-r--r--drivers/video/sis/sis_main.c30
-rw-r--r--drivers/video/skeletonfb.c2
-rw-r--r--drivers/video/sm501fb.c6
-rw-r--r--drivers/video/smscufx.c1994
-rw-r--r--drivers/video/tmiofb.c2
-rw-r--r--drivers/video/tridentfb.c4
-rw-r--r--drivers/video/udlfb.c187
-rw-r--r--drivers/video/valkyriefb.c2
-rw-r--r--drivers/video/vfb.c4
-rw-r--r--drivers/video/vga16fb.c2
-rw-r--r--drivers/video/via/dvi.c34
-rw-r--r--drivers/video/via/dvi.h3
-rw-r--r--drivers/video/via/global.c2
-rw-r--r--drivers/video/via/global.h2
-rw-r--r--drivers/video/via/hw.c544
-rw-r--r--drivers/video/via/hw.h285
-rw-r--r--drivers/video/via/lcd.c53
-rw-r--r--drivers/video/via/lcd.h7
-rw-r--r--drivers/video/via/share.h23
-rw-r--r--drivers/video/via/via-core.c2
-rw-r--r--drivers/video/via/via_modesetting.c104
-rw-r--r--drivers/video/via/via_modesetting.h23
-rw-r--r--drivers/video/via/viafbdev.c182
-rw-r--r--drivers/video/via/viamode.c60
-rw-r--r--drivers/video/via/viamode.h4
-rw-r--r--drivers/video/vt8500lcdfb.c6
-rw-r--r--drivers/video/vt8623fb.c9
-rw-r--r--drivers/video/xilinxfb.c1
-rw-r--r--drivers/virtio/Kconfig11
-rw-r--r--drivers/virtio/Makefile1
-rw-r--r--drivers/virtio/virtio_mmio.c479
-rw-r--r--drivers/virtio/virtio_pci.c10
-rw-r--r--drivers/w1/slaves/w1_ds2760.c48
-rw-r--r--drivers/w1/slaves/w1_ds2780.c96
-rw-r--r--drivers/w1/slaves/w1_ds2780.h2
-rw-r--r--drivers/w1/w1_int.c1
-rw-r--r--drivers/w1/w1_io.c5
-rw-r--r--drivers/watchdog/Kconfig6
1239 files changed, 88978 insertions, 26751 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 2ca59dc69f7f..127408069ca7 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -933,7 +933,7 @@ static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time, struct pstore_info *psi);
-static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id,
struct pstore_info *psi);
@@ -1040,11 +1040,12 @@ out:
return (rc < 0) ? rc : (len - sizeof(*rcd));
}
-static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
size_t size, struct pstore_info *psi)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
+ int ret;
memset(rcd, 0, sizeof(*rcd));
memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
@@ -1079,9 +1080,10 @@ static u64 erst_writer(enum pstore_type_id type, unsigned int part,
}
rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
- erst_write(&rcd->hdr);
+ ret = erst_write(&rcd->hdr);
+ *id = rcd->hdr.record_id;
- return rcd->hdr.record_id;
+ return ret;
}
static int erst_clearer(enum pstore_type_id type, u64 id,
@@ -1165,7 +1167,7 @@ static int __init erst_init(void)
goto err_release_erange;
buf = kmalloc(erst_erange.size, GFP_KERNEL);
- mutex_init(&erst_info.buf_mutex);
+ spin_lock_init(&erst_info.buf_lock);
if (buf) {
erst_info.buf = buf + sizeof(struct cper_pstore_record);
erst_info.bufsize = erst_erange.size -
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index c03277d37748..004f2ce3dc73 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -202,11 +202,18 @@ static int __devexit ahci_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ahci_of_match[] = {
+ { .compatible = "calxeda,hb-ahci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ahci_of_match);
+
static struct platform_driver ahci_driver = {
.remove = __devexit_p(ahci_remove),
.driver = {
.name = "ahci",
.owner = THIS_MODULE,
+ .of_match_table = ahci_of_match,
},
.id_table = ahci_devtype,
};
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 528f6318ded1..167ba0af47f5 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -159,7 +159,7 @@ aoeblk_release(struct gendisk *disk, fmode_t mode)
return 0;
}
-static int
+static void
aoeblk_make_request(struct request_queue *q, struct bio *bio)
{
struct sk_buff_head queue;
@@ -172,25 +172,25 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
if (bio == NULL) {
printk(KERN_ERR "aoe: bio is NULL\n");
BUG();
- return 0;
+ return;
}
d = bio->bi_bdev->bd_disk->private_data;
if (d == NULL) {
printk(KERN_ERR "aoe: bd_disk->private_data is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
- return 0;
+ return;
} else if (bio->bi_io_vec == NULL) {
printk(KERN_ERR "aoe: bi_io_vec is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
- return 0;
+ return;
}
buf = mempool_alloc(d->bufpool, GFP_NOIO);
if (buf == NULL) {
printk(KERN_INFO "aoe: buf allocation failure\n");
bio_endio(bio, -ENOMEM);
- return 0;
+ return;
}
memset(buf, 0, sizeof(*buf));
INIT_LIST_HEAD(&buf->bufs);
@@ -211,7 +211,7 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irqrestore(&d->lock, flags);
mempool_free(buf, d->bufpool);
bio_endio(bio, -ENXIO);
- return 0;
+ return;
}
list_add_tail(&buf->bufs, &d->bufq);
@@ -222,8 +222,6 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(&queue);
-
- return 0;
}
static int
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index dba1c32e1ddf..d22119d49e53 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -323,7 +323,7 @@ out:
return err;
}
-static int brd_make_request(struct request_queue *q, struct bio *bio)
+static void brd_make_request(struct request_queue *q, struct bio *bio)
{
struct block_device *bdev = bio->bi_bdev;
struct brd_device *brd = bdev->bd_disk->private_data;
@@ -359,8 +359,6 @@ static int brd_make_request(struct request_queue *q, struct bio *bio)
out:
bio_endio(bio, err);
-
- return 0;
}
#ifdef CONFIG_BLK_DEV_XIP
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 8f4ef656a1af..486f94ef24d4 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -68,6 +68,10 @@ static int cciss_tape_cmds = 6;
module_param(cciss_tape_cmds, int, 0644);
MODULE_PARM_DESC(cciss_tape_cmds,
"number of commands to allocate for tape devices (default: 6)");
+static int cciss_simple_mode;
+module_param(cciss_simple_mode, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(cciss_simple_mode,
+ "Use 'simple mode' rather than 'performant mode'");
static DEFINE_MUTEX(cciss_mutex);
static struct proc_dir_entry *proc_cciss;
@@ -176,6 +180,7 @@ static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
static void __devinit cciss_interrupt_mode(ctlr_info_t *);
+static int __devinit cciss_enter_simple_mode(struct ctlr_info *h);
static void start_io(ctlr_info_t *h);
static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
@@ -388,7 +393,7 @@ static void cciss_seq_show_header(struct seq_file *seq)
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
- h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT],
+ h->firm_ver[3], (unsigned int)h->intr[h->intr_mode],
h->num_luns,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);
@@ -636,6 +641,18 @@ static ssize_t host_store_rescan(struct device *dev,
}
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
+static ssize_t host_show_transport_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ctlr_info *h = to_hba(dev);
+
+ return snprintf(buf, 20, "%s\n",
+ h->transMethod & CFGTBL_Trans_Performant ?
+ "performant" : "simple");
+}
+static DEVICE_ATTR(transport_mode, S_IRUGO, host_show_transport_mode, NULL);
+
static ssize_t dev_show_unique_id(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -808,6 +825,7 @@ static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
static struct attribute *cciss_host_attrs[] = {
&dev_attr_rescan.attr,
&dev_attr_resettable.attr,
+ &dev_attr_transport_mode.attr,
NULL
};
@@ -3984,6 +4002,9 @@ static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h)
{
__u32 trans_support;
+ if (cciss_simple_mode)
+ return;
+
dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n");
/* Attempt to put controller into performant mode if supported */
/* Does board support performant mode? */
@@ -4081,7 +4102,7 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *h)
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
- h->intr[PERF_MODE_INT] = h->pdev->irq;
+ h->intr[h->intr_mode] = h->pdev->irq;
return;
}
@@ -4341,6 +4362,9 @@ static int __devinit cciss_pci_init(ctlr_info_t *h)
}
cciss_enable_scsi_prefetch(h);
cciss_p600_dma_prefetch_quirk(h);
+ err = cciss_enter_simple_mode(h);
+ if (err)
+ goto err_out_free_res;
cciss_put_controller_into_performant_mode(h);
return 0;
@@ -4533,6 +4557,13 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev,
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= PCI_D0;
pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+ /*
+ * The P600 requires a small delay when changing states.
+ * Otherwise we may think the board did not reset and we bail.
+ * This for kdump only and is particular to the P600.
+ */
+ msleep(500);
}
return 0;
}
@@ -4843,20 +4874,20 @@ static int cciss_request_irq(ctlr_info_t *h,
irqreturn_t (*intxhandler)(int, void *))
{
if (h->msix_vector || h->msi_vector) {
- if (!request_irq(h->intr[PERF_MODE_INT], msixhandler,
+ if (!request_irq(h->intr[h->intr_mode], msixhandler,
IRQF_DISABLED, h->devname, h))
return 0;
dev_err(&h->pdev->dev, "Unable to get msi irq %d"
- " for %s\n", h->intr[PERF_MODE_INT],
+ " for %s\n", h->intr[h->intr_mode],
h->devname);
return -1;
}
- if (!request_irq(h->intr[PERF_MODE_INT], intxhandler,
+ if (!request_irq(h->intr[h->intr_mode], intxhandler,
IRQF_DISABLED, h->devname, h))
return 0;
dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
- h->intr[PERF_MODE_INT], h->devname);
+ h->intr[h->intr_mode], h->devname);
return -1;
}
@@ -4887,7 +4918,7 @@ static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h)
{
int ctlr = h->ctlr;
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
#ifdef CONFIG_PCI_MSI
if (h->msix_vector)
pci_disable_msix(h->pdev);
@@ -4953,6 +4984,7 @@ reinit_after_soft_reset:
h = hba[i];
h->pdev = pdev;
h->busy_initializing = 1;
+ h->intr_mode = cciss_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
INIT_LIST_HEAD(&h->cmpQ);
INIT_LIST_HEAD(&h->reqQ);
mutex_init(&h->busy_shutting_down);
@@ -5009,7 +5041,7 @@ reinit_after_soft_reset:
dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
h->devname, pdev->device, pci_name(pdev),
- h->intr[PERF_MODE_INT], dac ? "" : " not");
+ h->intr[h->intr_mode], dac ? "" : " not");
if (cciss_allocate_cmd_pool(h))
goto clean4;
@@ -5056,7 +5088,7 @@ reinit_after_soft_reset:
spin_lock_irqsave(&h->lock, flags);
h->access.set_intr_mask(h, CCISS_INTR_OFF);
spin_unlock_irqrestore(&h->lock, flags);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
rc = cciss_request_irq(h, cciss_msix_discard_completions,
cciss_intx_discard_completions);
if (rc) {
@@ -5133,7 +5165,7 @@ clean4:
cciss_free_cmd_pool(h);
cciss_free_scatterlists(h);
cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
clean2:
unregister_blkdev(h->major, h->devname);
clean1:
@@ -5172,9 +5204,31 @@ static void cciss_shutdown(struct pci_dev *pdev)
if (return_code != IO_OK)
dev_warn(&h->pdev->dev, "Error flushing cache\n");
h->access.set_intr_mask(h, CCISS_INTR_OFF);
- free_irq(h->intr[PERF_MODE_INT], h);
+ free_irq(h->intr[h->intr_mode], h);
+}
+
+static int __devinit cciss_enter_simple_mode(struct ctlr_info *h)
+{
+ u32 trans_support;
+
+ trans_support = readl(&(h->cfgtable->TransportSupport));
+ if (!(trans_support & SIMPLE_MODE))
+ return -ENOTSUPP;
+
+ h->max_commands = readl(&(h->cfgtable->CmdsOutMax));
+ writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest));
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
+ cciss_wait_for_mode_change_ack(h);
+ print_cfg_table(h);
+ if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) {
+ dev_warn(&h->pdev->dev, "unable to get board into simple mode\n");
+ return -ENODEV;
+ }
+ h->transMethod = CFGTBL_Trans_Simple;
+ return 0;
}
+
static void __devexit cciss_remove_one(struct pci_dev *pdev)
{
ctlr_info_t *h;
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index c049548e68b7..7fda30e4a241 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -92,6 +92,7 @@ struct ctlr_info
unsigned int intr[4];
unsigned int msix_vector;
unsigned int msi_vector;
+ int intr_mode;
int cciss_max_sectors;
BYTE cciss_read;
BYTE cciss_write;
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index b2fceb53e809..9125bbeacd4d 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -620,6 +620,7 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
}
vendor_id = pdev->vendor;
device_id = pdev->device;
+ revision = pdev->revision;
irq = pdev->irq;
for(i=0; i<6; i++)
@@ -632,7 +633,6 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
}
pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 1706d60b8c99..9cf20355ceec 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -1506,7 +1506,7 @@ extern void drbd_free_mdev(struct drbd_conf *mdev);
extern int proc_details;
/* drbd_req */
-extern int drbd_make_request(struct request_queue *q, struct bio *bio);
+extern void drbd_make_request(struct request_queue *q, struct bio *bio);
extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req);
extern int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec);
extern int is_valid_ar_handle(struct drbd_request *, sector_t);
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 3424d675b769..4a0f314086e5 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1073,7 +1073,7 @@ static int drbd_fail_request_early(struct drbd_conf *mdev, int is_write)
return 0;
}
-int drbd_make_request(struct request_queue *q, struct bio *bio)
+void drbd_make_request(struct request_queue *q, struct bio *bio)
{
unsigned int s_enr, e_enr;
struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata;
@@ -1081,7 +1081,7 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
if (drbd_fail_request_early(mdev, bio_data_dir(bio) & WRITE)) {
bio_endio(bio, -EPERM);
- return 0;
+ return;
}
start_time = jiffies;
@@ -1100,7 +1100,8 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
if (likely(s_enr == e_enr)) {
inc_ap_bio(mdev, 1);
- return drbd_make_request_common(mdev, bio, start_time);
+ drbd_make_request_common(mdev, bio, start_time);
+ return;
}
/* can this bio be split generically?
@@ -1148,7 +1149,6 @@ int drbd_make_request(struct request_queue *q, struct bio *bio)
bio_pair_release(bp);
}
- return 0;
}
/* This is called by bio_add_page(). With this function we reduce
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 4720c7ade0ae..3d806820280e 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -76,6 +76,8 @@
#include <linux/splice.h>
#include <linux/sysfs.h>
#include <linux/miscdevice.h>
+#include <linux/falloc.h>
+
#include <asm/uaccess.h>
static DEFINE_IDR(loop_index_idr);
@@ -203,74 +205,6 @@ lo_do_transfer(struct loop_device *lo, int cmd,
}
/**
- * do_lo_send_aops - helper for writing data to a loop device
- *
- * This is the fast version for backing filesystems which implement the address
- * space operations write_begin and write_end.
- */
-static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec,
- loff_t pos, struct page *unused)
-{
- struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
- struct address_space *mapping = file->f_mapping;
- pgoff_t index;
- unsigned offset, bv_offs;
- int len, ret;
-
- mutex_lock(&mapping->host->i_mutex);
- index = pos >> PAGE_CACHE_SHIFT;
- offset = pos & ((pgoff_t)PAGE_CACHE_SIZE - 1);
- bv_offs = bvec->bv_offset;
- len = bvec->bv_len;
- while (len > 0) {
- sector_t IV;
- unsigned size, copied;
- int transfer_result;
- struct page *page;
- void *fsdata;
-
- IV = ((sector_t)index << (PAGE_CACHE_SHIFT - 9))+(offset >> 9);
- size = PAGE_CACHE_SIZE - offset;
- if (size > len)
- size = len;
-
- ret = pagecache_write_begin(file, mapping, pos, size, 0,
- &page, &fsdata);
- if (ret)
- goto fail;
-
- file_update_time(file);
-
- transfer_result = lo_do_transfer(lo, WRITE, page, offset,
- bvec->bv_page, bv_offs, size, IV);
- copied = size;
- if (unlikely(transfer_result))
- copied = 0;
-
- ret = pagecache_write_end(file, mapping, pos, size, copied,
- page, fsdata);
- if (ret < 0 || ret != copied)
- goto fail;
-
- if (unlikely(transfer_result))
- goto fail;
-
- bv_offs += copied;
- len -= copied;
- offset = 0;
- index++;
- pos += copied;
- }
- ret = 0;
-out:
- mutex_unlock(&mapping->host->i_mutex);
- return ret;
-fail:
- ret = -1;
- goto out;
-}
-
-/**
* __do_lo_send_write - helper for writing data to a loop device
*
* This helper just factors out common code between do_lo_send_direct_write()
@@ -297,10 +231,8 @@ static int __do_lo_send_write(struct file *file,
/**
* do_lo_send_direct_write - helper for writing data to a loop device
*
- * This is the fast, non-transforming version for backing filesystems which do
- * not implement the address space operations write_begin and write_end.
- * It uses the write file operation which should be present on all writeable
- * filesystems.
+ * This is the fast, non-transforming version that does not need double
+ * buffering.
*/
static int do_lo_send_direct_write(struct loop_device *lo,
struct bio_vec *bvec, loff_t pos, struct page *page)
@@ -316,15 +248,9 @@ static int do_lo_send_direct_write(struct loop_device *lo,
/**
* do_lo_send_write - helper for writing data to a loop device
*
- * This is the slow, transforming version for filesystems which do not
- * implement the address space operations write_begin and write_end. It
- * uses the write file operation which should be present on all writeable
- * filesystems.
- *
- * Using fops->write is slower than using aops->{prepare,commit}_write in the
- * transforming case because we need to double buffer the data as we cannot do
- * the transformations in place as we do not have direct access to the
- * destination pages of the backing file.
+ * This is the slow, transforming version that needs to double buffer the
+ * data as it cannot do the transformations in place without having direct
+ * access to the destination pages of the backing file.
*/
static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec,
loff_t pos, struct page *page)
@@ -350,17 +276,16 @@ static int lo_send(struct loop_device *lo, struct bio *bio, loff_t pos)
struct page *page = NULL;
int i, ret = 0;
- do_lo_send = do_lo_send_aops;
- if (!(lo->lo_flags & LO_FLAGS_USE_AOPS)) {
+ if (lo->transfer != transfer_none) {
+ page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
+ if (unlikely(!page))
+ goto fail;
+ kmap(page);
+ do_lo_send = do_lo_send_write;
+ } else {
do_lo_send = do_lo_send_direct_write;
- if (lo->transfer != transfer_none) {
- page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
- if (unlikely(!page))
- goto fail;
- kmap(page);
- do_lo_send = do_lo_send_write;
- }
}
+
bio_for_each_segment(bvec, bio, i) {
ret = do_lo_send(lo, bvec, pos, page);
if (ret < 0)
@@ -484,6 +409,29 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
}
}
+ /*
+ * We use punch hole to reclaim the free space used by the
+ * image a.k.a. discard. However we do support discard if
+ * encryption is enabled, because it may give an attacker
+ * useful information.
+ */
+ if (bio->bi_rw & REQ_DISCARD) {
+ struct file *file = lo->lo_backing_file;
+ int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
+
+ if ((!file->f_op->fallocate) ||
+ lo->lo_encrypt_key_size) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ ret = file->f_op->fallocate(file, mode, pos,
+ bio->bi_size);
+ if (unlikely(ret && ret != -EINVAL &&
+ ret != -EOPNOTSUPP))
+ ret = -EIO;
+ goto out;
+ }
+
ret = lo_send(lo, bio, pos);
if ((bio->bi_rw & REQ_FUA) && !ret) {
@@ -514,7 +462,7 @@ static struct bio *loop_get_bio(struct loop_device *lo)
return bio_list_pop(&lo->lo_bio_list);
}
-static int loop_make_request(struct request_queue *q, struct bio *old_bio)
+static void loop_make_request(struct request_queue *q, struct bio *old_bio)
{
struct loop_device *lo = q->queuedata;
int rw = bio_rw(old_bio);
@@ -532,12 +480,11 @@ static int loop_make_request(struct request_queue *q, struct bio *old_bio)
loop_add_bio(lo, old_bio);
wake_up(&lo->lo_event);
spin_unlock_irq(&lo->lo_lock);
- return 0;
+ return;
out:
spin_unlock_irq(&lo->lo_lock);
bio_io_error(old_bio);
- return 0;
}
struct switch_request {
@@ -700,7 +647,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
goto out_putf;
fput(old_file);
- if (max_part > 0)
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0);
return 0;
@@ -777,16 +724,25 @@ static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf)
return sprintf(buf, "%s\n", autoclear ? "1" : "0");
}
+static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf)
+{
+ int partscan = (lo->lo_flags & LO_FLAGS_PARTSCAN);
+
+ return sprintf(buf, "%s\n", partscan ? "1" : "0");
+}
+
LOOP_ATTR_RO(backing_file);
LOOP_ATTR_RO(offset);
LOOP_ATTR_RO(sizelimit);
LOOP_ATTR_RO(autoclear);
+LOOP_ATTR_RO(partscan);
static struct attribute *loop_attrs[] = {
&loop_attr_backing_file.attr,
&loop_attr_offset.attr,
&loop_attr_sizelimit.attr,
&loop_attr_autoclear.attr,
+ &loop_attr_partscan.attr,
NULL,
};
@@ -807,6 +763,35 @@ static void loop_sysfs_exit(struct loop_device *lo)
&loop_attribute_group);
}
+static void loop_config_discard(struct loop_device *lo)
+{
+ struct file *file = lo->lo_backing_file;
+ struct inode *inode = file->f_mapping->host;
+ struct request_queue *q = lo->lo_queue;
+
+ /*
+ * We use punch hole to reclaim the free space used by the
+ * image a.k.a. discard. However we do support discard if
+ * encryption is enabled, because it may give an attacker
+ * useful information.
+ */
+ if ((!file->f_op->fallocate) ||
+ lo->lo_encrypt_key_size) {
+ q->limits.discard_granularity = 0;
+ q->limits.discard_alignment = 0;
+ q->limits.max_discard_sectors = 0;
+ q->limits.discard_zeroes_data = 0;
+ queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ return;
+ }
+
+ q->limits.discard_granularity = inode->i_sb->s_blocksize;
+ q->limits.discard_alignment = inode->i_sb->s_blocksize;
+ q->limits.max_discard_sectors = UINT_MAX >> 9;
+ q->limits.discard_zeroes_data = 1;
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+}
+
static int loop_set_fd(struct loop_device *lo, fmode_t mode,
struct block_device *bdev, unsigned int arg)
{
@@ -849,35 +834,23 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
mapping = file->f_mapping;
inode = mapping->host;
- if (!(file->f_mode & FMODE_WRITE))
- lo_flags |= LO_FLAGS_READ_ONLY;
-
error = -EINVAL;
- if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
- const struct address_space_operations *aops = mapping->a_ops;
-
- if (aops->write_begin)
- lo_flags |= LO_FLAGS_USE_AOPS;
- if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
- lo_flags |= LO_FLAGS_READ_ONLY;
+ if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
+ goto out_putf;
- lo_blocksize = S_ISBLK(inode->i_mode) ?
- inode->i_bdev->bd_block_size : PAGE_SIZE;
+ if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) ||
+ !file->f_op->write)
+ lo_flags |= LO_FLAGS_READ_ONLY;
- error = 0;
- } else {
- goto out_putf;
- }
+ lo_blocksize = S_ISBLK(inode->i_mode) ?
+ inode->i_bdev->bd_block_size : PAGE_SIZE;
+ error = -EFBIG;
size = get_loop_size(lo, file);
-
- if ((loff_t)(sector_t)size != size) {
- error = -EFBIG;
+ if ((loff_t)(sector_t)size != size)
goto out_putf;
- }
- if (!(mode & FMODE_WRITE))
- lo_flags |= LO_FLAGS_READ_ONLY;
+ error = 0;
set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);
@@ -919,7 +892,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
}
lo->lo_state = Lo_bound;
wake_up_process(lo->lo_thread);
- if (max_part > 0)
+ if (part_shift)
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0);
return 0;
@@ -980,10 +955,11 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,
return err;
}
-static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
+static int loop_clr_fd(struct loop_device *lo)
{
struct file *filp = lo->lo_backing_file;
gfp_t gfp = lo->old_gfp_mask;
+ struct block_device *bdev = lo->lo_device;
if (lo->lo_state != Lo_bound)
return -ENXIO;
@@ -1012,7 +988,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_offset = 0;
lo->lo_sizelimit = 0;
lo->lo_encrypt_key_size = 0;
- lo->lo_flags = 0;
lo->lo_thread = NULL;
memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
@@ -1030,8 +1005,11 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_state = Lo_unbound;
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
- if (max_part > 0 && bdev)
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev)
ioctl_by_bdev(bdev, BLKRRPART, 0);
+ lo->lo_flags = 0;
+ if (!part_shift)
+ lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
mutex_unlock(&lo->lo_ctl_mutex);
/*
* Need not hold lo_ctl_mutex to fput backing file.
@@ -1085,6 +1063,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (figure_loop_size(lo))
return -EFBIG;
}
+ loop_config_discard(lo);
memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);
memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE);
@@ -1100,6 +1079,13 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
(info->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_flags ^= LO_FLAGS_AUTOCLEAR;
+ if ((info->lo_flags & LO_FLAGS_PARTSCAN) &&
+ !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
+ ioctl_by_bdev(lo->lo_device, BLKRRPART, 0);
+ }
+
lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
lo->lo_init[0] = info->lo_init[0];
lo->lo_init[1] = info->lo_init[1];
@@ -1293,7 +1279,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_CLR_FD:
/* loop_clr_fd would have unlocked lo_ctl_mutex on success */
- err = loop_clr_fd(lo, bdev);
+ err = loop_clr_fd(lo);
if (!err)
goto out_unlocked;
break;
@@ -1513,7 +1499,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
* In autoclear mode, stop the loop thread
* and remove configuration after last close.
*/
- err = loop_clr_fd(lo, NULL);
+ err = loop_clr_fd(lo);
if (!err)
goto out_unlocked;
} else {
@@ -1635,6 +1621,27 @@ static int loop_add(struct loop_device **l, int i)
if (!disk)
goto out_free_queue;
+ /*
+ * Disable partition scanning by default. The in-kernel partition
+ * scanning can be requested individually per-device during its
+ * setup. Userspace can always add and remove partitions from all
+ * devices. The needed partition minors are allocated from the
+ * extended minor space, the main loop device numbers will continue
+ * to match the loop minors, regardless of the number of partitions
+ * used.
+ *
+ * If max_part is given, partition scanning is globally enabled for
+ * all loop devices. The minors for the main loop devices will be
+ * multiples of max_part.
+ *
+ * Note: Global-for-all-devices, set-only-at-init, read-only module
+ * parameteters like 'max_loop' and 'max_part' make things needlessly
+ * complicated, are too static, inflexible and may surprise
+ * userspace tools. Parameters like this in general should be avoided.
+ */
+ if (!part_shift)
+ disk->flags |= GENHD_FL_NO_PART_SCAN;
+ disk->flags |= GENHD_FL_EXT_DEVT;
mutex_init(&lo->lo_ctl_mutex);
lo->lo_number = i;
lo->lo_thread = NULL;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index f533f3375e24..c3f0ee16594d 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -127,8 +127,7 @@ static void sock_shutdown(struct nbd_device *lo, int lock)
if (lock)
mutex_lock(&lo->tx_lock);
if (lo->sock) {
- printk(KERN_WARNING "%s: shutting down socket\n",
- lo->disk->disk_name);
+ dev_warn(disk_to_dev(lo->disk), "shutting down socket\n");
kernel_sock_shutdown(lo->sock, SHUT_RDWR);
lo->sock = NULL;
}
@@ -158,8 +157,9 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
sigset_t blocked, oldset;
if (unlikely(!sock)) {
- printk(KERN_ERR "%s: Attempted %s on closed socket in sock_xmit\n",
- lo->disk->disk_name, (send ? "send" : "recv"));
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted %s on closed socket in sock_xmit\n",
+ (send ? "send" : "recv"));
return -EINVAL;
}
@@ -250,8 +250,8 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
result = sock_xmit(lo, 1, &request, sizeof(request),
(nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0);
if (result <= 0) {
- printk(KERN_ERR "%s: Send control failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Send control failed (result %d)\n", result);
goto error_out;
}
@@ -270,8 +270,9 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
lo->disk->disk_name, req, bvec->bv_len);
result = sock_send_bvec(lo, bvec, flags);
if (result <= 0) {
- printk(KERN_ERR "%s: Send data failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Send data failed (result %d)\n",
+ result);
goto error_out;
}
}
@@ -328,14 +329,13 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
reply.magic = 0;
result = sock_xmit(lo, 0, &reply, sizeof(reply), MSG_WAITALL);
if (result <= 0) {
- printk(KERN_ERR "%s: Receive control failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk),
+ "Receive control failed (result %d)\n", result);
goto harderror;
}
if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
- printk(KERN_ERR "%s: Wrong magic (0x%lx)\n",
- lo->disk->disk_name,
+ dev_err(disk_to_dev(lo->disk), "Wrong magic (0x%lx)\n",
(unsigned long)ntohl(reply.magic));
result = -EPROTO;
goto harderror;
@@ -347,15 +347,15 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
if (result != -ENOENT)
goto harderror;
- printk(KERN_ERR "%s: Unexpected reply (%p)\n",
- lo->disk->disk_name, reply.handle);
+ dev_err(disk_to_dev(lo->disk), "Unexpected reply (%p)\n",
+ reply.handle);
result = -EBADR;
goto harderror;
}
if (ntohl(reply.error)) {
- printk(KERN_ERR "%s: Other side returned error (%d)\n",
- lo->disk->disk_name, ntohl(reply.error));
+ dev_err(disk_to_dev(lo->disk), "Other side returned error (%d)\n",
+ ntohl(reply.error));
req->errors++;
return req;
}
@@ -369,8 +369,8 @@ static struct request *nbd_read_stat(struct nbd_device *lo)
rq_for_each_segment(bvec, req, iter) {
result = sock_recv_bvec(lo, bvec);
if (result <= 0) {
- printk(KERN_ERR "%s: Receive data failed (result %d)\n",
- lo->disk->disk_name, result);
+ dev_err(disk_to_dev(lo->disk), "Receive data failed (result %d)\n",
+ result);
req->errors++;
return req;
}
@@ -405,10 +405,10 @@ static int nbd_do_it(struct nbd_device *lo)
BUG_ON(lo->magic != LO_MAGIC);
- lo->pid = current->pid;
- ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr);
+ lo->pid = task_pid_nr(current);
+ ret = device_create_file(disk_to_dev(lo->disk), &pid_attr);
if (ret) {
- printk(KERN_ERR "nbd: sysfs_create_file failed!");
+ dev_err(disk_to_dev(lo->disk), "device_create_file failed!\n");
lo->pid = 0;
return ret;
}
@@ -416,7 +416,7 @@ static int nbd_do_it(struct nbd_device *lo)
while ((req = nbd_read_stat(lo)) != NULL)
nbd_end_request(req);
- sysfs_remove_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr);
+ device_remove_file(disk_to_dev(lo->disk), &pid_attr);
lo->pid = 0;
return 0;
}
@@ -457,8 +457,8 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
if (rq_data_dir(req) == WRITE) {
nbd_cmd(req) = NBD_CMD_WRITE;
if (lo->flags & NBD_READ_ONLY) {
- printk(KERN_ERR "%s: Write on read-only\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Write on read-only\n");
goto error_out;
}
}
@@ -468,16 +468,15 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
mutex_lock(&lo->tx_lock);
if (unlikely(!lo->sock)) {
mutex_unlock(&lo->tx_lock);
- printk(KERN_ERR "%s: Attempted send on closed socket\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted send on closed socket\n");
goto error_out;
}
lo->active_req = req;
if (nbd_send_req(lo, req) != 0) {
- printk(KERN_ERR "%s: Request send failed\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk), "Request send failed\n");
req->errors++;
nbd_end_request(req);
} else {
@@ -549,8 +548,8 @@ static void do_nbd_request(struct request_queue *q)
BUG_ON(lo->magic != LO_MAGIC);
if (unlikely(!lo->sock)) {
- printk(KERN_ERR "%s: Attempted send on closed socket\n",
- lo->disk->disk_name);
+ dev_err(disk_to_dev(lo->disk),
+ "Attempted send on closed socket\n");
req->errors++;
nbd_end_request(req);
spin_lock_irq(q->queue_lock);
@@ -576,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
case NBD_DISCONNECT: {
struct request sreq;
- printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
+ dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n");
blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_SPECIAL;
@@ -674,7 +673,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
file = lo->file;
lo->file = NULL;
nbd_clear_que(lo);
- printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name);
+ dev_warn(disk_to_dev(lo->disk), "queue cleared\n");
if (file)
fput(file);
lo->bytesize = 0;
@@ -694,8 +693,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
return 0;
case NBD_PRINT_DEBUG:
- printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
- bdev->bd_disk->disk_name,
+ dev_info(disk_to_dev(lo->disk),
+ "next = %p, prev = %p, head = %p\n",
lo->queue_head.next, lo->queue_head.prev,
&lo->queue_head);
return 0;
@@ -745,7 +744,7 @@ static int __init nbd_init(void)
BUILD_BUG_ON(sizeof(struct nbd_request) != 28);
if (max_part < 0) {
- printk(KERN_CRIT "nbd: max_part must be >= 0\n");
+ printk(KERN_ERR "nbd: max_part must be >= 0\n");
return -EINVAL;
}
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index e133f094ab08..a63b0a2b7805 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -2444,7 +2444,7 @@ static void pkt_end_io_read_cloned(struct bio *bio, int err)
pkt_bio_finished(pd);
}
-static int pkt_make_request(struct request_queue *q, struct bio *bio)
+static void pkt_make_request(struct request_queue *q, struct bio *bio)
{
struct pktcdvd_device *pd;
char b[BDEVNAME_SIZE];
@@ -2473,7 +2473,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
cloned_bio->bi_end_io = pkt_end_io_read_cloned;
pd->stats.secs_r += bio->bi_size >> 9;
pkt_queue_bio(pd, cloned_bio);
- return 0;
+ return;
}
if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
@@ -2509,7 +2509,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
pkt_make_request(q, &bp->bio1);
pkt_make_request(q, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
}
@@ -2533,7 +2533,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
}
spin_unlock(&pkt->lock);
spin_unlock(&pd->cdrw.active_list_lock);
- return 0;
+ return;
} else {
blocked_bio = 1;
}
@@ -2584,10 +2584,9 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
*/
wake_up(&pd->wqueue);
}
- return 0;
+ return;
end_io:
bio_io_error(bio);
- return 0;
}
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index b3bdb8af89cf..7fad7af87eb2 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -596,7 +596,7 @@ out:
return next;
}
-static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
+static void ps3vram_make_request(struct request_queue *q, struct bio *bio)
{
struct ps3_system_bus_device *dev = q->queuedata;
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
@@ -610,13 +610,11 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
spin_unlock_irq(&priv->lock);
if (busy)
- return 0;
+ return;
do {
bio = ps3vram_do_bio(dev, bio);
} while (bio);
-
- return 0;
}
static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 031ca720d926..aa2712060bfb 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -513,7 +513,7 @@ static void process_page(unsigned long data)
}
}
-static int mm_make_request(struct request_queue *q, struct bio *bio)
+static void mm_make_request(struct request_queue *q, struct bio *bio)
{
struct cardinfo *card = q->queuedata;
pr_debug("mm_make_request %llu %u\n",
@@ -525,7 +525,7 @@ static int mm_make_request(struct request_queue *q, struct bio *bio)
card->biotail = &bio->bi_next;
spin_unlock_irq(&card->lock);
- return 0;
+ return;
}
static irqreturn_t mm_interrupt(int irq, void *__card)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 079c08808d8a..e7a5750a93d9 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -8,10 +8,13 @@
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
#include <scsi/scsi_cmnd.h>
+#include <linux/idr.h>
#define PART_BITS 4
-static int major, index;
+static int major;
+static DEFINE_IDA(vd_index_ida);
+
struct workqueue_struct *virtblk_wq;
struct virtio_blk
@@ -35,6 +38,9 @@ struct virtio_blk
/* What host tells us, plus 2 for header & tailer. */
unsigned int sg_elems;
+ /* Ida index - used to track minor number allocations. */
+ int index;
+
/* Scatterlist: can be too big for stack. */
struct scatterlist sg[/*sg_elems*/];
};
@@ -276,6 +282,11 @@ static int index_to_minor(int index)
return index << PART_BITS;
}
+static int minor_to_index(int minor)
+{
+ return minor >> PART_BITS;
+}
+
static ssize_t virtblk_serial_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -341,14 +352,17 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
struct request_queue *q;
- int err;
+ int err, index;
u64 cap;
u32 v, blk_size, sg_elems, opt_io_size;
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
- if (index_to_minor(index) >= 1 << MINORBITS)
- return -ENOSPC;
+ err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS),
+ GFP_KERNEL);
+ if (err < 0)
+ goto out;
+ index = err;
/* We need to know how many segments before we allocate. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
@@ -365,7 +379,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
if (!vblk) {
err = -ENOMEM;
- goto out;
+ goto out_free_index;
}
INIT_LIST_HEAD(&vblk->reqs);
@@ -421,7 +435,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
vblk->disk->private_data = vblk;
vblk->disk->fops = &virtblk_fops;
vblk->disk->driverfs_dev = &vdev->dev;
- index++;
+ vblk->index = index;
/* configure queue flush support */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
@@ -516,6 +530,8 @@ out_free_vq:
vdev->config->del_vqs(vdev);
out_free_vblk:
kfree(vblk);
+out_free_index:
+ ida_simple_remove(&vd_index_ida, index);
out:
return err;
}
@@ -523,6 +539,7 @@ out:
static void __devexit virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
+ int index = vblk->index;
flush_work(&vblk->config_work);
@@ -538,6 +555,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
mempool_destroy(vblk->pool);
vdev->config->del_vqs(vdev);
kfree(vblk);
+ ida_simple_remove(&vd_index_ida, index);
}
static const struct virtio_device_id id_table[] = {
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 1540792b1e54..15ec4db194d1 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -39,6 +39,9 @@
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/freezer.h>
+#include <linux/loop.h>
+#include <linux/falloc.h>
+#include <linux/fs.h>
#include <xen/events.h>
#include <xen/page.h>
@@ -258,13 +261,16 @@ irqreturn_t xen_blkif_be_int(int irq, void *dev_id)
static void print_stats(struct xen_blkif *blkif)
{
- pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d\n",
+ pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d"
+ " | ds %4d\n",
current->comm, blkif->st_oo_req,
- blkif->st_rd_req, blkif->st_wr_req, blkif->st_f_req);
+ blkif->st_rd_req, blkif->st_wr_req,
+ blkif->st_f_req, blkif->st_ds_req);
blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000);
blkif->st_rd_req = 0;
blkif->st_wr_req = 0;
blkif->st_oo_req = 0;
+ blkif->st_ds_req = 0;
}
int xen_blkif_schedule(void *arg)
@@ -410,6 +416,59 @@ static int xen_blkbk_map(struct blkif_request *req,
return ret;
}
+static void xen_blk_discard(struct xen_blkif *blkif, struct blkif_request *req)
+{
+ int err = 0;
+ int status = BLKIF_RSP_OKAY;
+ struct block_device *bdev = blkif->vbd.bdev;
+
+ if (blkif->blk_backend_type == BLKIF_BACKEND_PHY)
+ /* just forward the discard request */
+ err = blkdev_issue_discard(bdev,
+ req->u.discard.sector_number,
+ req->u.discard.nr_sectors,
+ GFP_KERNEL, 0);
+ else if (blkif->blk_backend_type == BLKIF_BACKEND_FILE) {
+ /* punch a hole in the backing file */
+ struct loop_device *lo = bdev->bd_disk->private_data;
+ struct file *file = lo->lo_backing_file;
+
+ if (file->f_op->fallocate)
+ err = file->f_op->fallocate(file,
+ FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+ req->u.discard.sector_number << 9,
+ req->u.discard.nr_sectors << 9);
+ else
+ err = -EOPNOTSUPP;
+ } else
+ err = -EOPNOTSUPP;
+
+ if (err == -EOPNOTSUPP) {
+ pr_debug(DRV_PFX "discard op failed, not supported\n");
+ status = BLKIF_RSP_EOPNOTSUPP;
+ } else if (err)
+ status = BLKIF_RSP_ERROR;
+
+ make_response(blkif, req->id, req->operation, status);
+}
+
+static void xen_blk_drain_io(struct xen_blkif *blkif)
+{
+ atomic_set(&blkif->drain, 1);
+ do {
+ /* The initial value is one, and one refcnt taken at the
+ * start of the xen_blkif_schedule thread. */
+ if (atomic_read(&blkif->refcnt) <= 2)
+ break;
+ wait_for_completion_interruptible_timeout(
+ &blkif->drain_complete, HZ);
+
+ if (!atomic_read(&blkif->drain))
+ break;
+ } while (!kthread_should_stop());
+ atomic_set(&blkif->drain, 0);
+}
+
/*
* Completion callback on the bio's. Called as bh->b_end_io()
*/
@@ -422,6 +481,11 @@ static void __end_block_io_op(struct pending_req *pending_req, int error)
pr_debug(DRV_PFX "flush diskcache op failed, not supported\n");
xen_blkbk_flush_diskcache(XBT_NIL, pending_req->blkif->be, 0);
pending_req->status = BLKIF_RSP_EOPNOTSUPP;
+ } else if ((pending_req->operation == BLKIF_OP_WRITE_BARRIER) &&
+ (error == -EOPNOTSUPP)) {
+ pr_debug(DRV_PFX "write barrier op failed, not supported\n");
+ xen_blkbk_barrier(XBT_NIL, pending_req->blkif->be, 0);
+ pending_req->status = BLKIF_RSP_EOPNOTSUPP;
} else if (error) {
pr_debug(DRV_PFX "Buffer not up-to-date at end of operation,"
" error=%d\n", error);
@@ -438,6 +502,10 @@ static void __end_block_io_op(struct pending_req *pending_req, int error)
make_response(pending_req->blkif, pending_req->id,
pending_req->operation, pending_req->status);
xen_blkif_put(pending_req->blkif);
+ if (atomic_read(&pending_req->blkif->refcnt) <= 2) {
+ if (atomic_read(&pending_req->blkif->drain))
+ complete(&pending_req->blkif->drain_complete);
+ }
free_req(pending_req);
}
}
@@ -532,7 +600,6 @@ do_block_io_op(struct xen_blkif *blkif)
return more_to_do;
}
-
/*
* Transmutation of the 'struct blkif_request' to a proper 'struct bio'
* and call the 'submit_bio' to pass it to the underlying storage.
@@ -549,6 +616,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
int i, nbio = 0;
int operation;
struct blk_plug plug;
+ bool drain = false;
switch (req->operation) {
case BLKIF_OP_READ:
@@ -559,11 +627,16 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
blkif->st_wr_req++;
operation = WRITE_ODIRECT;
break;
+ case BLKIF_OP_WRITE_BARRIER:
+ drain = true;
case BLKIF_OP_FLUSH_DISKCACHE:
blkif->st_f_req++;
operation = WRITE_FLUSH;
break;
- case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_DISCARD:
+ blkif->st_ds_req++;
+ operation = REQ_DISCARD;
+ break;
default:
operation = 0; /* make gcc happy */
goto fail_response;
@@ -572,7 +645,8 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
/* Check that the number of segments is sane. */
nseg = req->nr_segments;
- if (unlikely(nseg == 0 && operation != WRITE_FLUSH) ||
+ if (unlikely(nseg == 0 && operation != WRITE_FLUSH &&
+ operation != REQ_DISCARD) ||
unlikely(nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST)) {
pr_debug(DRV_PFX "Bad number of segments in request (%d)\n",
nseg);
@@ -621,16 +695,25 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
}
}
+ /* Wait on all outstanding I/O's and once that has been completed
+ * issue the WRITE_FLUSH.
+ */
+ if (drain)
+ xen_blk_drain_io(pending_req->blkif);
+
/*
* If we have failed at this point, we need to undo the M2P override,
* set gnttab_set_unmap_op on all of the grant references and perform
* the hypercall to unmap the grants - that is all done in
* xen_blkbk_unmap.
*/
- if (xen_blkbk_map(req, pending_req, seg))
+ if (operation != REQ_DISCARD && xen_blkbk_map(req, pending_req, seg))
goto fail_flush;
- /* This corresponding xen_blkif_put is done in __end_block_io_op */
+ /*
+ * This corresponding xen_blkif_put is done in __end_block_io_op, or
+ * below (in "!bio") if we are handling a BLKIF_OP_DISCARD.
+ */
xen_blkif_get(blkif);
for (i = 0; i < nseg; i++) {
@@ -654,18 +737,25 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
preq.sector_number += seg[i].nsec;
}
- /* This will be hit if the operation was a flush. */
+ /* This will be hit if the operation was a flush or discard. */
if (!bio) {
- BUG_ON(operation != WRITE_FLUSH);
+ BUG_ON(operation != WRITE_FLUSH && operation != REQ_DISCARD);
- bio = bio_alloc(GFP_KERNEL, 0);
- if (unlikely(bio == NULL))
- goto fail_put_bio;
+ if (operation == WRITE_FLUSH) {
+ bio = bio_alloc(GFP_KERNEL, 0);
+ if (unlikely(bio == NULL))
+ goto fail_put_bio;
- biolist[nbio++] = bio;
- bio->bi_bdev = preq.bdev;
- bio->bi_private = pending_req;
- bio->bi_end_io = end_block_io_op;
+ biolist[nbio++] = bio;
+ bio->bi_bdev = preq.bdev;
+ bio->bi_private = pending_req;
+ bio->bi_end_io = end_block_io_op;
+ } else if (operation == REQ_DISCARD) {
+ xen_blk_discard(blkif, req);
+ xen_blkif_put(blkif);
+ free_req(pending_req);
+ return 0;
+ }
}
/*
@@ -685,7 +775,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,
if (operation == READ)
blkif->st_rd_sect += preq.nr_sects;
- else if (operation == WRITE || operation == WRITE_FLUSH)
+ else if (operation & WRITE)
blkif->st_wr_sect += preq.nr_sects;
return 0;
@@ -765,9 +855,9 @@ static int __init xen_blkif_init(void)
mmap_pages = xen_blkif_reqs * BLKIF_MAX_SEGMENTS_PER_REQUEST;
- blkbk->pending_reqs = kmalloc(sizeof(blkbk->pending_reqs[0]) *
+ blkbk->pending_reqs = kzalloc(sizeof(blkbk->pending_reqs[0]) *
xen_blkif_reqs, GFP_KERNEL);
- blkbk->pending_grant_handles = kzalloc(sizeof(blkbk->pending_grant_handles[0]) *
+ blkbk->pending_grant_handles = kmalloc(sizeof(blkbk->pending_grant_handles[0]) *
mmap_pages, GFP_KERNEL);
blkbk->pending_pages = kzalloc(sizeof(blkbk->pending_pages[0]) *
mmap_pages, GFP_KERNEL);
@@ -790,8 +880,6 @@ static int __init xen_blkif_init(void)
if (rc)
goto failed_init;
- memset(blkbk->pending_reqs, 0, sizeof(blkbk->pending_reqs));
-
INIT_LIST_HEAD(&blkbk->pending_free);
spin_lock_init(&blkbk->pending_free_lock);
init_waitqueue_head(&blkbk->pending_free_wq);
diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h
index c4bd34063ecc..de09f525d6c1 100644
--- a/drivers/block/xen-blkback/common.h
+++ b/drivers/block/xen-blkback/common.h
@@ -62,13 +62,26 @@ struct blkif_common_response {
/* i386 protocol version */
#pragma pack(push, 4)
+
+struct blkif_x86_32_request_rw {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+
+struct blkif_x86_32_request_discard {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ uint64_t nr_sectors;
+};
+
struct blkif_x86_32_request {
uint8_t operation; /* BLKIF_OP_??? */
uint8_t nr_segments; /* number of segments */
blkif_vdev_t handle; /* only for read/write requests */
uint64_t id; /* private guest value, echoed in resp */
- blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
- struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ union {
+ struct blkif_x86_32_request_rw rw;
+ struct blkif_x86_32_request_discard discard;
+ } u;
};
struct blkif_x86_32_response {
uint64_t id; /* copied from request */
@@ -78,13 +91,26 @@ struct blkif_x86_32_response {
#pragma pack(pop)
/* x86_64 protocol version */
+
+struct blkif_x86_64_request_rw {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+
+struct blkif_x86_64_request_discard {
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ uint64_t nr_sectors;
+};
+
struct blkif_x86_64_request {
uint8_t operation; /* BLKIF_OP_??? */
uint8_t nr_segments; /* number of segments */
blkif_vdev_t handle; /* only for read/write requests */
uint64_t __attribute__((__aligned__(8))) id;
- blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
- struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ union {
+ struct blkif_x86_64_request_rw rw;
+ struct blkif_x86_64_request_discard discard;
+ } u;
};
struct blkif_x86_64_response {
uint64_t __attribute__((__aligned__(8))) id;
@@ -112,6 +138,11 @@ enum blkif_protocol {
BLKIF_PROTOCOL_X86_64 = 3,
};
+enum blkif_backend_type {
+ BLKIF_BACKEND_PHY = 1,
+ BLKIF_BACKEND_FILE = 2,
+};
+
struct xen_vbd {
/* What the domain refers to this vbd as. */
blkif_vdev_t handle;
@@ -137,6 +168,7 @@ struct xen_blkif {
unsigned int irq;
/* Comms information. */
enum blkif_protocol blk_protocol;
+ enum blkif_backend_type blk_backend_type;
union blkif_back_rings blk_rings;
struct vm_struct *blk_ring_area;
/* The VBD attached to this interface. */
@@ -148,6 +180,9 @@ struct xen_blkif {
atomic_t refcnt;
wait_queue_head_t wq;
+ /* for barrier (drain) requests */
+ struct completion drain_complete;
+ atomic_t drain;
/* One thread per one blkif. */
struct task_struct *xenblkd;
unsigned int waiting_reqs;
@@ -158,6 +193,7 @@ struct xen_blkif {
int st_wr_req;
int st_oo_req;
int st_f_req;
+ int st_ds_req;
int st_rd_sect;
int st_wr_sect;
@@ -181,7 +217,7 @@ struct xen_blkif {
struct phys_req {
unsigned short dev;
- unsigned short nr_sects;
+ blkif_sector_t nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
};
@@ -195,6 +231,8 @@ int xen_blkif_schedule(void *arg);
int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
struct backend_info *be, int state);
+int xen_blkbk_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state);
struct xenbus_device *xen_blkbk_xenbus(struct backend_info *be);
static inline void blkif_get_x86_32_req(struct blkif_request *dst,
@@ -205,12 +243,25 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst,
dst->nr_segments = src->nr_segments;
dst->handle = src->handle;
dst->id = src->id;
- dst->u.rw.sector_number = src->sector_number;
- barrier();
- if (n > dst->nr_segments)
- n = dst->nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->seg[i];
+ switch (src->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+ default:
+ break;
+ }
}
static inline void blkif_get_x86_64_req(struct blkif_request *dst,
@@ -221,12 +272,25 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst,
dst->nr_segments = src->nr_segments;
dst->handle = src->handle;
dst->id = src->id;
- dst->u.rw.sector_number = src->sector_number;
- barrier();
- if (n > dst->nr_segments)
- n = dst->nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->seg[i];
+ switch (src->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+ default:
+ break;
+ }
}
#endif /* __XEN_BLKIF__BACKEND__COMMON_H__ */
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index 5fd2010f7d2b..2c008afe63d9 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -114,6 +114,8 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid)
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
init_waitqueue_head(&blkif->wq);
+ init_completion(&blkif->drain_complete);
+ atomic_set(&blkif->drain, 0);
blkif->st_print = jiffies;
init_waitqueue_head(&blkif->waiting_to_free);
@@ -272,6 +274,7 @@ VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req);
VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req);
VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req);
VBD_SHOW(f_req, "%d\n", be->blkif->st_f_req);
+VBD_SHOW(ds_req, "%d\n", be->blkif->st_ds_req);
VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect);
VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect);
@@ -280,6 +283,7 @@ static struct attribute *xen_vbdstat_attrs[] = {
&dev_attr_rd_req.attr,
&dev_attr_wr_req.attr,
&dev_attr_f_req.attr,
+ &dev_attr_ds_req.attr,
&dev_attr_rd_sect.attr,
&dev_attr_wr_sect.attr,
NULL
@@ -419,6 +423,73 @@ int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
return err;
}
+int xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info *be)
+{
+ struct xenbus_device *dev = be->dev;
+ struct xen_blkif *blkif = be->blkif;
+ char *type;
+ int err;
+ int state = 0;
+
+ type = xenbus_read(XBT_NIL, dev->nodename, "type", NULL);
+ if (!IS_ERR(type)) {
+ if (strncmp(type, "file", 4) == 0) {
+ state = 1;
+ blkif->blk_backend_type = BLKIF_BACKEND_FILE;
+ }
+ if (strncmp(type, "phy", 3) == 0) {
+ struct block_device *bdev = be->blkif->vbd.bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+ if (blk_queue_discard(q)) {
+ err = xenbus_printf(xbt, dev->nodename,
+ "discard-granularity", "%u",
+ q->limits.discard_granularity);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "writing discard-granularity");
+ goto kfree;
+ }
+ err = xenbus_printf(xbt, dev->nodename,
+ "discard-alignment", "%u",
+ q->limits.discard_alignment);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "writing discard-alignment");
+ goto kfree;
+ }
+ state = 1;
+ blkif->blk_backend_type = BLKIF_BACKEND_PHY;
+ }
+ }
+ } else {
+ err = PTR_ERR(type);
+ xenbus_dev_fatal(dev, err, "reading type");
+ goto out;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-discard",
+ "%d", state);
+ if (err)
+ xenbus_dev_fatal(dev, err, "writing feature-discard");
+kfree:
+ kfree(type);
+out:
+ return err;
+}
+int xen_blkbk_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state)
+{
+ struct xenbus_device *dev = be->dev;
+ int err;
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-barrier",
+ "%d", state);
+ if (err)
+ xenbus_dev_fatal(dev, err, "writing feature-barrier");
+
+ return err;
+}
+
/*
* Entry point to this code when a new device is created. Allocate the basic
* structures, and watch the store waiting for the hotplug scripts to tell us
@@ -650,6 +721,11 @@ again:
if (err)
goto abort;
+ err = xen_blkbk_discard(xbt, be);
+
+ /* If we can't advertise it is OK. */
+ err = xen_blkbk_barrier(xbt, be, be->blkif->vbd.flush_support);
+
err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu",
(unsigned long long)vbd_sz(&be->blkif->vbd));
if (err) {
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 9ea8c2576c70..7b2ec5908413 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -98,6 +98,9 @@ struct blkfront_info
unsigned long shadow_free;
unsigned int feature_flush;
unsigned int flush_op;
+ unsigned int feature_discard;
+ unsigned int discard_granularity;
+ unsigned int discard_alignment;
int is_ready;
};
@@ -302,29 +305,36 @@ static int blkif_queue_request(struct request *req)
ring_req->operation = info->flush_op;
}
- ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
- BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST);
+ if (unlikely(req->cmd_flags & REQ_DISCARD)) {
+ /* id, sector_number and handle are set above. */
+ ring_req->operation = BLKIF_OP_DISCARD;
+ ring_req->nr_segments = 0;
+ ring_req->u.discard.nr_sectors = blk_rq_sectors(req);
+ } else {
+ ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
+ BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST);
- for_each_sg(info->sg, sg, ring_req->nr_segments, i) {
- buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg)));
- fsect = sg->offset >> 9;
- lsect = fsect + (sg->length >> 9) - 1;
- /* install a grant reference. */
- ref = gnttab_claim_grant_reference(&gref_head);
- BUG_ON(ref == -ENOSPC);
+ for_each_sg(info->sg, sg, ring_req->nr_segments, i) {
+ buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg)));
+ fsect = sg->offset >> 9;
+ lsect = fsect + (sg->length >> 9) - 1;
+ /* install a grant reference. */
+ ref = gnttab_claim_grant_reference(&gref_head);
+ BUG_ON(ref == -ENOSPC);
- gnttab_grant_foreign_access_ref(
- ref,
- info->xbdev->otherend_id,
- buffer_mfn,
- rq_data_dir(req) );
-
- info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
- ring_req->u.rw.seg[i] =
- (struct blkif_request_segment) {
- .gref = ref,
- .first_sect = fsect,
- .last_sect = lsect };
+ gnttab_grant_foreign_access_ref(
+ ref,
+ info->xbdev->otherend_id,
+ buffer_mfn,
+ rq_data_dir(req));
+
+ info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
+ ring_req->u.rw.seg[i] =
+ (struct blkif_request_segment) {
+ .gref = ref,
+ .first_sect = fsect,
+ .last_sect = lsect };
+ }
}
info->ring.req_prod_pvt++;
@@ -370,7 +380,9 @@ static void do_blkif_request(struct request_queue *rq)
blk_start_request(req);
- if (req->cmd_type != REQ_TYPE_FS) {
+ if ((req->cmd_type != REQ_TYPE_FS) ||
+ ((req->cmd_flags & (REQ_FLUSH | REQ_FUA)) &&
+ !info->flush_op)) {
__blk_end_request_all(req, -EIO);
continue;
}
@@ -399,6 +411,7 @@ wait:
static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
{
struct request_queue *rq;
+ struct blkfront_info *info = gd->private_data;
rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
if (rq == NULL)
@@ -406,6 +419,13 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
+ if (info->feature_discard) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_max_discard_sectors(rq, get_capacity(gd));
+ rq->limits.discard_granularity = info->discard_granularity;
+ rq->limits.discard_alignment = info->discard_alignment;
+ }
+
/* Hard sector size and max sectors impersonate the equiv. hardware. */
blk_queue_logical_block_size(rq, sector_size);
blk_queue_max_hw_sectors(rq, 512);
@@ -722,6 +742,17 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO;
switch (bret->operation) {
+ case BLKIF_OP_DISCARD:
+ if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
+ struct request_queue *rq = info->rq;
+ printk(KERN_WARNING "blkfront: %s: discard op failed\n",
+ info->gd->disk_name);
+ error = -EOPNOTSUPP;
+ info->feature_discard = 0;
+ queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
+ }
+ __blk_end_request_all(req, error);
+ break;
case BLKIF_OP_FLUSH_DISKCACHE:
case BLKIF_OP_WRITE_BARRIER:
if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
@@ -1098,6 +1129,33 @@ blkfront_closing(struct blkfront_info *info)
bdput(bdev);
}
+static void blkfront_setup_discard(struct blkfront_info *info)
+{
+ int err;
+ char *type;
+ unsigned int discard_granularity;
+ unsigned int discard_alignment;
+
+ type = xenbus_read(XBT_NIL, info->xbdev->otherend, "type", NULL);
+ if (IS_ERR(type))
+ return;
+
+ if (strncmp(type, "phy", 3) == 0) {
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
+ "discard-granularity", "%u", &discard_granularity,
+ "discard-alignment", "%u", &discard_alignment,
+ NULL);
+ if (!err) {
+ info->feature_discard = 1;
+ info->discard_granularity = discard_granularity;
+ info->discard_alignment = discard_alignment;
+ }
+ } else if (strncmp(type, "file", 4) == 0)
+ info->feature_discard = 1;
+
+ kfree(type);
+}
+
/*
* Invoked when the backend is finally 'ready' (and has told produced
* the details about the physical device - #sectors, size, etc).
@@ -1108,7 +1166,7 @@ static void blkfront_connect(struct blkfront_info *info)
unsigned long sector_size;
unsigned int binfo;
int err;
- int barrier, flush;
+ int barrier, flush, discard;
switch (info->connected) {
case BLKIF_STATE_CONNECTED:
@@ -1178,7 +1236,14 @@ static void blkfront_connect(struct blkfront_info *info)
info->feature_flush = REQ_FLUSH;
info->flush_op = BLKIF_OP_FLUSH_DISKCACHE;
}
-
+
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
+ "feature-discard", "%d", &discard,
+ NULL);
+
+ if (!err && discard)
+ blkfront_setup_discard(info);
+
err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size);
if (err) {
xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s",
@@ -1385,6 +1450,8 @@ static struct xenbus_driver blkfront = {
static int __init xlblk_init(void)
{
+ int ret;
+
if (!xen_domain())
return -ENODEV;
@@ -1394,7 +1461,13 @@ static int __init xlblk_init(void)
return -ENODEV;
}
- return xenbus_register_frontend(&blkfront);
+ ret = xenbus_register_frontend(&blkfront);
+ if (ret) {
+ unregister_blkdev(XENVBD_MAJOR, DEV_NAME);
+ return ret;
+ }
+
+ return 0;
}
module_init(xlblk_init);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 423fd56bf612..43643033a3ae 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -298,7 +298,7 @@ if RTC_LIB=n
config RTC
tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
- && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN
+ && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -346,7 +346,7 @@ config JS_RTC
config GEN_RTC
tristate "Generic /dev/rtc emulation"
- depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN
+ depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN && !UML
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -490,7 +490,7 @@ config SCx200_GPIO
config PC8736x_GPIO
tristate "NatSemi PC8736x GPIO Support"
- depends on X86_32
+ depends on X86_32 && !UML
default SCx200_GPIO # mostly N
select NSC_GPIO # needed for support routines
help
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c
index 056b289a1e89..3695773ce7c3 100644
--- a/drivers/char/agp/hp-agp.c
+++ b/drivers/char/agp/hp-agp.c
@@ -336,7 +336,8 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type)
off_t j, io_pg_start;
int io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
@@ -380,7 +381,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type)
struct _hp_private *hp = &hp_private;
int i, io_pg_start, io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 1d2ebc7a4947..0689bf6b0183 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -60,6 +60,19 @@ config HW_RANDOM_AMD
If unsure, say Y.
+config HW_RANDOM_ATMEL
+ tristate "Atmel Random Number Generator support"
+ depends on HW_RANDOM && ARCH_AT91SAM9G45
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Atmel AT91 devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
@@ -222,3 +235,18 @@ config HW_RANDOM_PPC4XX
module will be called ppc4xx-rng.
If unsure, say N.
+
+config UML_RANDOM
+ depends on UML
+ tristate "Hardware random number generator"
+ help
+ This option enables UML's "hardware" random number generator. It
+ attaches itself to the host's /dev/random, supplying as much entropy
+ as the host has, rather than the small amount the UML gets from its
+ own drivers. It registers itself as a standard hardware random number
+ generator, major 10, minor 183, and the canonical device name is
+ /dev/hwrng.
+ The way to make use of this is to install the rng-tools package
+ (check your distro, or download from
+ http://sourceforge.net/projects/gkernel/). rngd periodically reads
+ /dev/hwrng and injects the entropy into /dev/random.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index c88f244c8a71..b2ff5265a996 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -7,6 +7,7 @@ rng-core-y := core.o
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_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/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
new file mode 100644
index 000000000000..241df2e76aba
--- /dev/null
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/hw_random.h>
+#include <linux/platform_device.h>
+
+#define TRNG_CR 0x00
+#define TRNG_ISR 0x1c
+#define TRNG_ODATA 0x50
+
+#define TRNG_KEY 0x524e4700 /* RNG */
+
+struct atmel_trng {
+ struct clk *clk;
+ void __iomem *base;
+ struct hwrng rng;
+};
+
+static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ u32 *data = buf;
+
+ /* data ready? */
+ if (readl(trng->base + TRNG_ODATA) & 1) {
+ *data = readl(trng->base + TRNG_ODATA);
+ return 4;
+ } else
+ return 0;
+}
+
+static int atmel_trng_probe(struct platform_device *pdev)
+{
+ struct atmel_trng *trng;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
+
+ trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!trng->base)
+ return -EBUSY;
+
+ trng->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(trng->clk))
+ return PTR_ERR(trng->clk);
+
+ ret = clk_enable(trng->clk);
+ if (ret)
+ goto err_enable;
+
+ writel(TRNG_KEY | 1, trng->base + TRNG_CR);
+ trng->rng.name = pdev->name;
+ trng->rng.read = atmel_trng_read;
+
+ ret = hwrng_register(&trng->rng);
+ if (ret)
+ goto err_register;
+
+ platform_set_drvdata(pdev, trng);
+
+ return 0;
+
+err_register:
+ clk_disable(trng->clk);
+err_enable:
+ clk_put(trng->clk);
+
+ return ret;
+}
+
+static int __devexit atmel_trng_remove(struct platform_device *pdev)
+{
+ struct atmel_trng *trng = platform_get_drvdata(pdev);
+
+ hwrng_unregister(&trng->rng);
+
+ writel(TRNG_KEY, trng->base + TRNG_CR);
+ clk_disable(trng->clk);
+ clk_put(trng->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int atmel_trng_suspend(struct device *dev)
+{
+ struct atmel_trng *trng = dev_get_drvdata(dev);
+
+ clk_disable(trng->clk);
+
+ return 0;
+}
+
+static int atmel_trng_resume(struct device *dev)
+{
+ struct atmel_trng *trng = dev_get_drvdata(dev);
+
+ return clk_enable(trng->clk);
+}
+
+static const struct dev_pm_ops atmel_trng_pm_ops = {
+ .suspend = atmel_trng_suspend,
+ .resume = atmel_trng_resume,
+};
+#endif /* CONFIG_PM */
+
+static struct platform_driver atmel_trng_driver = {
+ .probe = atmel_trng_probe,
+ .remove = __devexit_p(atmel_trng_remove),
+ .driver = {
+ .name = "atmel-trng",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &atmel_trng_pm_ops,
+#endif /* CONFIG_PM */
+ },
+};
+
+static int __init atmel_trng_init(void)
+{
+ return platform_driver_register(&atmel_trng_driver);
+}
+module_init(atmel_trng_init);
+
+static void __exit atmel_trng_exit(void)
+{
+ platform_driver_unregister(&atmel_trng_driver);
+}
+module_exit(atmel_trng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Atmel true random number generator driver");
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
index a1f68af4ccf4..f22861511909 100644
--- a/drivers/char/ttyprintk.c
+++ b/drivers/char/ttyprintk.c
@@ -170,7 +170,7 @@ static const struct tty_operations ttyprintk_ops = {
.ioctl = tpk_ioctl,
};
-struct tty_port_operations null_ops = { };
+static struct tty_port_operations null_ops = { };
static struct tty_driver *ttyprintk_driver;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index fb68b1295373..4ca181f1378b 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -19,8 +19,10 @@
*/
#include <linux/cdev.h>
#include <linux/debugfs.h>
+#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -73,6 +75,7 @@ struct ports_driver_data {
static struct ports_driver_data pdrvdata;
DEFINE_SPINLOCK(pdrvdata_lock);
+DECLARE_COMPLETION(early_console_added);
/* This struct holds information that's relevant only for console ports */
struct console {
@@ -151,6 +154,10 @@ struct ports_device {
int chr_major;
};
+struct port_stats {
+ unsigned long bytes_sent, bytes_received, bytes_discarded;
+};
+
/* This struct holds the per-port data */
struct port {
/* Next port in the list, head is in the ports_device */
@@ -179,6 +186,13 @@ struct port {
struct dentry *debugfs_file;
/*
+ * Keep count of the bytes sent, received and discarded for
+ * this port for accounting and debugging purposes. These
+ * counts are not reset across port open / close events.
+ */
+ struct port_stats stats;
+
+ /*
* The entries in this struct will be valid if this port is
* hooked up to an hvc console
*/
@@ -347,17 +361,19 @@ fail:
}
/* Callers should take appropriate locks */
-static void *get_inbuf(struct port *port)
+static struct port_buffer *get_inbuf(struct port *port)
{
struct port_buffer *buf;
- struct virtqueue *vq;
unsigned int len;
- vq = port->in_vq;
- buf = virtqueue_get_buf(vq, &len);
+ if (port->inbuf)
+ return port->inbuf;
+
+ buf = virtqueue_get_buf(port->in_vq, &len);
if (buf) {
buf->len = len;
buf->offset = 0;
+ port->stats.bytes_received += len;
}
return buf;
}
@@ -384,32 +400,27 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
static void discard_port_data(struct port *port)
{
struct port_buffer *buf;
- struct virtqueue *vq;
- unsigned int len;
- int ret;
+ unsigned int err;
if (!port->portdev) {
/* Device has been unplugged. vqs are already gone. */
return;
}
- vq = port->in_vq;
- if (port->inbuf)
- buf = port->inbuf;
- else
- buf = virtqueue_get_buf(vq, &len);
+ buf = get_inbuf(port);
- ret = 0;
+ err = 0;
while (buf) {
- if (add_inbuf(vq, buf) < 0) {
- ret++;
+ port->stats.bytes_discarded += buf->len - buf->offset;
+ if (add_inbuf(port->in_vq, buf) < 0) {
+ err++;
free_buf(buf);
}
- buf = virtqueue_get_buf(vq, &len);
+ port->inbuf = NULL;
+ buf = get_inbuf(port);
}
- port->inbuf = NULL;
- if (ret)
+ if (err)
dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
- ret);
+ err);
}
static bool port_has_data(struct port *port)
@@ -417,18 +428,12 @@ static bool port_has_data(struct port *port)
unsigned long flags;
bool ret;
+ ret = false;
spin_lock_irqsave(&port->inbuf_lock, flags);
- if (port->inbuf) {
- ret = true;
- goto out;
- }
port->inbuf = get_inbuf(port);
- if (port->inbuf) {
+ if (port->inbuf)
ret = true;
- goto out;
- }
- ret = false;
-out:
+
spin_unlock_irqrestore(&port->inbuf_lock, flags);
return ret;
}
@@ -529,6 +534,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
cpu_relax();
done:
spin_unlock_irqrestore(&port->outvq_lock, flags);
+
+ port->stats.bytes_sent += in_count;
/*
* We're expected to return the amount of data we wrote -- all
* of it
@@ -633,8 +640,8 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- ret = wait_event_interruptible(port->waitqueue,
- !will_read_block(port));
+ ret = wait_event_freezable(port->waitqueue,
+ !will_read_block(port));
if (ret < 0)
return ret;
}
@@ -677,8 +684,8 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
if (nonblock)
return -EAGAIN;
- ret = wait_event_interruptible(port->waitqueue,
- !will_write_block(port));
+ ret = wait_event_freezable(port->waitqueue,
+ !will_write_block(port));
if (ret < 0)
return ret;
}
@@ -1059,6 +1066,14 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
out_offset += snprintf(buf + out_offset, out_count - out_offset,
"outvq_full: %d\n", port->outvq_full);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_sent: %lu\n", port->stats.bytes_sent);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_received: %lu\n",
+ port->stats.bytes_received);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "bytes_discarded: %lu\n",
+ port->stats.bytes_discarded);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
"is_console: %s\n",
is_console_port(port) ? "yes" : "no");
out_offset += snprintf(buf + out_offset, out_count - out_offset,
@@ -1143,6 +1158,7 @@ static int add_port(struct ports_device *portdev, u32 id)
port->cons.ws.ws_row = port->cons.ws.ws_col = 0;
port->host_connected = port->guest_connected = false;
+ port->stats = (struct port_stats) { 0 };
port->outvq_full = false;
@@ -1352,6 +1368,7 @@ static void handle_control_message(struct ports_device *portdev,
break;
init_port_console(port);
+ complete(&early_console_added);
/*
* Could remove the port here in case init fails - but
* have to notify the host first.
@@ -1394,6 +1411,13 @@ static void handle_control_message(struct ports_device *portdev,
break;
case VIRTIO_CONSOLE_PORT_NAME:
/*
+ * If we woke up after hibernation, we can get this
+ * again. Skip it in that case.
+ */
+ if (port->name)
+ break;
+
+ /*
* Skip the size of the header and the cpkt to get the size
* of the name that was sent
*/
@@ -1481,8 +1505,7 @@ static void in_intr(struct virtqueue *vq)
return;
spin_lock_irqsave(&port->inbuf_lock, flags);
- if (!port->inbuf)
- port->inbuf = get_inbuf(port);
+ port->inbuf = get_inbuf(port);
/*
* Don't queue up data when port is closed. This condition
@@ -1563,7 +1586,7 @@ static int init_vqs(struct ports_device *portdev)
portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
GFP_KERNEL);
if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs ||
- !portdev->out_vqs) {
+ !portdev->out_vqs) {
err = -ENOMEM;
goto free;
}
@@ -1648,6 +1671,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
struct ports_device *portdev;
int err;
bool multiport;
+ bool early = early_put_chars != NULL;
+
+ /* Ensure to read early_put_chars now */
+ barrier();
portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
if (!portdev) {
@@ -1675,13 +1702,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
multiport = false;
portdev->config.max_nr_ports = 1;
- if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+ if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
+ offsetof(struct virtio_console_config,
+ max_nr_ports),
+ &portdev->config.max_nr_ports) == 0)
multiport = true;
- vdev->config->get(vdev, offsetof(struct virtio_console_config,
- max_nr_ports),
- &portdev->config.max_nr_ports,
- sizeof(portdev->config.max_nr_ports));
- }
err = init_vqs(portdev);
if (err < 0) {
@@ -1719,6 +1744,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
+
+ /*
+ * If there was an early virtio console, assume that there are no
+ * other consoles. We need to wait until the hvc_alloc matches the
+ * hvc_instantiate, otherwise tty_open will complain, resulting in
+ * a "Warning: unable to open an initial console" boot failure.
+ * Without multiport this is done in add_port above. With multiport
+ * this might take some host<->guest communication - thus we have to
+ * wait.
+ */
+ if (multiport && early)
+ wait_for_completion(&early_console_added);
+
return 0;
free_vqs:
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 34e9c4f88926..999d6a03e436 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -15,3 +15,18 @@ config CLKSRC_MMIO
config DW_APB_TIMER
bool
+
+config CLKSRC_DBX500_PRCMU
+ bool "Clocksource PRCMU Timer"
+ depends on UX500_SOC_DB5500 || UX500_SOC_DB8500
+ default y
+ help
+ Use the always on PRCMU Timer as clocksource
+
+config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+ bool "Clocksource PRCMU Timer sched_clock"
+ depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK)
+ select HAVE_SCHED_CLOCK
+ default y
+ help
+ Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 85ad1646a7b7..8d81a1d32653 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o \ No newline at end of file
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
new file mode 100644
index 000000000000..59feefe0e3e6
--- /dev/null
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer for ST-Ericsson
+ * sched_clock implementation is based on:
+ * plat-nomadik/timer.c Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * DBx500-PRCMU Timer
+ * The PRCMU has 5 timers which are available in a always-on
+ * power domain. We use the Timer 4 for our always-on clock
+ * source on DB8500 and Timer 3 on DB5500.
+ */
+#include <linux/clockchips.h>
+#include <linux/clksrc-dbx500-prcmu.h>
+
+#include <asm/sched_clock.h>
+
+#include <mach/setup.h>
+#include <mach/hardware.h>
+
+#define RATE_32K 32768
+
+#define TIMER_MODE_CONTINOUS 0x1
+#define TIMER_DOWNCOUNT_VAL 0xffffffff
+
+#define PRCMU_TIMER_REF 0
+#define PRCMU_TIMER_DOWNCOUNT 0x4
+#define PRCMU_TIMER_MODE 0x8
+
+#define SCHED_CLOCK_MIN_WRAP 131072 /* 2^32 / 32768 */
+
+static void __iomem *clksrc_dbx500_timer_base;
+
+static cycle_t clksrc_dbx500_prcmu_read(struct clocksource *cs)
+{
+ u32 count, count2;
+
+ do {
+ count = readl(clksrc_dbx500_timer_base +
+ PRCMU_TIMER_DOWNCOUNT);
+ count2 = readl(clksrc_dbx500_timer_base +
+ PRCMU_TIMER_DOWNCOUNT);
+ } while (count2 != count);
+
+ /* Negate because the timer is a decrementing counter */
+ return ~count;
+}
+
+static struct clocksource clocksource_dbx500_prcmu = {
+ .name = "dbx500-prcmu-timer",
+ .rating = 300,
+ .read = clksrc_dbx500_prcmu_read,
+ .shift = 10,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc;
+
+ if (unlikely(!clksrc_dbx500_timer_base))
+ return 0;
+
+ cyc = clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
+
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace clksrc_dbx500_prcmu_update_sched_clock(void)
+{
+ u32 cyc = clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+#endif
+
+void __init clksrc_dbx500_prcmu_init(void __iomem *base)
+{
+ clksrc_dbx500_timer_base = base;
+
+ /*
+ * The A9 sub system expects the timer to be configured as
+ * a continous looping timer.
+ * The PRCMU should configure it but if it for some reason
+ * don't we do it here.
+ */
+ if (readl(clksrc_dbx500_timer_base + PRCMU_TIMER_MODE) !=
+ TIMER_MODE_CONTINOUS) {
+ writel(TIMER_MODE_CONTINOUS,
+ clksrc_dbx500_timer_base + PRCMU_TIMER_MODE);
+ writel(TIMER_DOWNCOUNT_VAL,
+ clksrc_dbx500_timer_base + PRCMU_TIMER_REF);
+ }
+#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+ init_sched_clock(&cd, clksrc_dbx500_prcmu_update_sched_clock,
+ 32, RATE_32K);
+#endif
+ clocksource_calc_mult_shift(&clocksource_dbx500_prcmu,
+ RATE_32K, SCHED_CLOCK_MIN_WRAP);
+ clocksource_register(&clocksource_dbx500_prcmu);
+}
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index d90456a809f9..edaa987621ea 100644
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ b/drivers/cpufreq/db8500-cpufreq.c
@@ -12,30 +12,35 @@
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <mach/id.h>
static struct cpufreq_frequency_table freq_table[] = {
[0] = {
.index = 0,
- .frequency = 300000,
+ .frequency = 200000,
},
[1] = {
.index = 1,
- .frequency = 600000,
+ .frequency = 300000,
},
[2] = {
- /* Used for MAX_OPP, if available */
.index = 2,
- .frequency = CPUFREQ_TABLE_END,
+ .frequency = 600000,
},
[3] = {
+ /* Used for MAX_OPP, if available */
.index = 3,
.frequency = CPUFREQ_TABLE_END,
},
+ [4] = {
+ .index = 4,
+ .frequency = CPUFREQ_TABLE_END,
+ },
};
static enum arm_opp idx2opp[] = {
+ ARM_EXTCLK,
ARM_50_OPP,
ARM_100_OPP,
ARM_MAX_OPP
@@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
freqs.old = policy->cur;
freqs.new = freq_table[idx].frequency;
- freqs.cpu = policy->cpu;
if (freqs.old == freqs.new)
return 0;
/* pre-change notification */
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
/* request the PRCM unit for opp change */
if (prcmu_set_arm_opp(idx2opp[idx])) {
@@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
}
/* post change notification */
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0;
}
@@ -104,16 +110,18 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
{
int res;
- int i;
BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
- if (cpu_is_u8500v2() && !prcmu_is_u8400()) {
- freq_table[0].frequency = 400000;
- freq_table[1].frequency = 800000;
+ if (!prcmu_is_u8400()) {
+ freq_table[1].frequency = 400000;
+ freq_table[2].frequency = 800000;
if (prcmu_has_arm_maxopp())
- freq_table[2].frequency = 1000000;
+ freq_table[3].frequency = 1000000;
}
+ pr_info("db8500-cpufreq : Available frequencies:\n");
+ while (freq_table[i].frequency != CPUFREQ_TABLE_END)
+ pr_info(" %d Mhz\n", freq_table[i++].frequency/1000);
/* get policy fields based on the table */
res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
@@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq;
policy->cur = db8500_cpufreq_getspeed(policy->cpu);
-
- for (i = 0; freq_table[i].frequency != policy->cur; i++)
- ;
-
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/*
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c
index 35a257dd4bb7..4bd6815d317b 100644
--- a/drivers/cpufreq/e_powersaver.c
+++ b/drivers/cpufreq/e_powersaver.c
@@ -19,6 +19,11 @@
#include <asm/msr.h>
#include <asm/tsc.h>
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#endif
+
#define EPS_BRAND_C7M 0
#define EPS_BRAND_C7 1
#define EPS_BRAND_EDEN 2
@@ -27,11 +32,59 @@
struct eps_cpu_data {
u32 fsb;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ u32 bios_limit;
+#endif
struct cpufreq_frequency_table freq_table[];
};
static struct eps_cpu_data *eps_cpu[NR_CPUS];
+/* Module parameters */
+static int freq_failsafe_off;
+static int voltage_failsafe_off;
+static int set_max_voltage;
+
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+static int ignore_acpi_limit;
+
+static struct acpi_processor_performance *eps_acpi_cpu_perf;
+
+/* Minimum necessary to get acpi_processor_get_bios_limit() working */
+static int eps_acpi_init(void)
+{
+ eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
+ GFP_KERNEL);
+ if (!eps_acpi_cpu_perf)
+ return -ENOMEM;
+
+ if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
+ GFP_KERNEL)) {
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ return -ENOMEM;
+ }
+
+ if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
+ free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ return -EIO;
+ }
+ return 0;
+}
+
+static int eps_acpi_exit(struct cpufreq_policy *policy)
+{
+ if (eps_acpi_cpu_perf) {
+ acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0);
+ free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
+ kfree(eps_acpi_cpu_perf);
+ eps_acpi_cpu_perf = NULL;
+ }
+ return 0;
+}
+#endif
static unsigned int eps_get(unsigned int cpu)
{
@@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
int k, step, voltage;
int ret;
int states;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ unsigned int limit;
+#endif
if (policy->cpu != 0)
return -ENODEV;
@@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
return -EINVAL;
if (current_voltage > 0x1f || max_voltage > 0x1f)
return -EINVAL;
- if (max_voltage < min_voltage)
+ if (max_voltage < min_voltage
+ || current_voltage < min_voltage
+ || current_voltage > max_voltage)
return -EINVAL;
+ /* Check for systems using underclocked CPU */
+ if (!freq_failsafe_off && max_multiplier != current_multiplier) {
+ printk(KERN_INFO "eps: Your processor is running at different "
+ "frequency then its maximum. Aborting.\n");
+ printk(KERN_INFO "eps: You can use freq_failsafe_off option "
+ "to disable this check.\n");
+ return -EINVAL;
+ }
+ if (!voltage_failsafe_off && max_voltage != current_voltage) {
+ printk(KERN_INFO "eps: Your processor is running at different "
+ "voltage then its maximum. Aborting.\n");
+ printk(KERN_INFO "eps: You can use voltage_failsafe_off "
+ "option to disable this check.\n");
+ return -EINVAL;
+ }
+
/* Calc FSB speed */
fsb = cpu_khz / current_multiplier;
+
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ /* Check for ACPI processor speed limit */
+ if (!ignore_acpi_limit && !eps_acpi_init()) {
+ if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
+ printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n",
+ limit/1000000,
+ (limit%1000000)/10000);
+ eps_acpi_exit(policy);
+ /* Check if max_multiplier is in BIOS limits */
+ if (limit && max_multiplier * fsb > limit) {
+ printk(KERN_INFO "eps: Aborting.\n");
+ return -EINVAL;
+ }
+ }
+ }
+#endif
+
+ /* Allow user to set lower maximum voltage then that reported
+ * by processor */
+ if (brand == EPS_BRAND_C7M && set_max_voltage) {
+ u32 v;
+
+ /* Change mV to something hardware can use */
+ v = (set_max_voltage - 700) / 16;
+ /* Check if voltage is within limits */
+ if (v >= min_voltage && v <= max_voltage) {
+ printk(KERN_INFO "eps: Setting %dmV as maximum.\n",
+ v * 16 + 700);
+ max_voltage = v;
+ }
+ }
+
/* Calc number of p-states supported */
if (brand == EPS_BRAND_C7M)
states = max_multiplier - min_multiplier + 1;
@@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
/* Copy basic values */
centaur->fsb = fsb;
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+ centaur->bios_limit = limit;
+#endif
/* Fill frequency and MSR value table */
f_table = &centaur->freq_table[0];
@@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
static int eps_cpu_exit(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
- struct eps_cpu_data *centaur;
- u32 lo, hi;
- if (eps_cpu[cpu] == NULL)
- return -ENODEV;
- centaur = eps_cpu[cpu];
-
- /* Get max frequency */
- rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
- /* Set max frequency */
- eps_set_state(centaur, cpu, hi & 0xffff);
/* Bye */
cpufreq_frequency_table_put_attr(policy->cpu);
kfree(eps_cpu[cpu]);
@@ -359,6 +459,19 @@ static void __exit eps_exit(void)
cpufreq_unregister_driver(&eps_driver);
}
+/* Allow user to overclock his machine or to change frequency to higher after
+ * unloading module */
+module_param(freq_failsafe_off, int, 0644);
+MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
+module_param(voltage_failsafe_off, int, 0644);
+MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
+#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
+module_param(ignore_acpi_limit, int, 0644);
+MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
+#endif
+module_param(set_max_voltage, int, 0644);
+MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
+
MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index b7c3a84c4cfa..ab9741fab92e 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/cpufreq.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
@@ -36,6 +38,10 @@ static struct regulator *int_regulator;
static struct cpufreq_freqs freqs;
static unsigned int memtype;
+static unsigned int locking_frequency;
+static bool frequency_locked;
+static DEFINE_MUTEX(cpufreq_lock);
+
enum exynos4_memory_type {
DDR2 = 4,
LPDDR2,
@@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy,
{
unsigned int index, old_index;
unsigned int arm_volt, int_volt;
+ int err = -EINVAL;
freqs.old = exynos4_getspeed(policy->cpu);
+ mutex_lock(&cpufreq_lock);
+
+ if (frequency_locked && target_freq != locking_frequency) {
+ err = -EAGAIN;
+ goto out;
+ }
+
if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
freqs.old, relation, &old_index))
- return -EINVAL;
+ goto out;
if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
target_freq, relation, &index))
- return -EINVAL;
+ goto out;
+
+ err = 0;
freqs.new = exynos4_freq_table[index].frequency;
freqs.cpu = policy->cpu;
if (freqs.new == freqs.old)
- return 0;
+ goto out;
/* get the voltage value */
arm_volt = exynos4_volt_table[index].arm_volt;
@@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- return 0;
+out:
+ mutex_unlock(&cpufreq_lock);
+ return err;
}
#ifdef CONFIG_PM
+/*
+ * These suspend/resume are used as syscore_ops, it is already too
+ * late to set regulator voltages at this stage.
+ */
static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
{
return 0;
@@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
}
#endif
+/**
+ * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
+ * context
+ * @notifier
+ * @pm_event
+ * @v
+ *
+ * While frequency_locked == true, target() ignores every frequency but
+ * locking_frequency. The locking_frequency value is the initial frequency,
+ * which is set by the bootloader. In order to eliminate possible
+ * inconsistency in clock values, we save and restore frequencies during
+ * suspend and resume and block CPUFREQ activities. Note that the standard
+ * suspend/resume cannot be used as they are too deep (syscore_ops) for
+ * regulator actions.
+ */
+static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *v)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
+ static unsigned int saved_frequency;
+ unsigned int temp;
+
+ mutex_lock(&cpufreq_lock);
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ if (frequency_locked)
+ goto out;
+ frequency_locked = true;
+
+ if (locking_frequency) {
+ saved_frequency = exynos4_getspeed(0);
+
+ mutex_unlock(&cpufreq_lock);
+ exynos4_target(policy, locking_frequency,
+ CPUFREQ_RELATION_H);
+ mutex_lock(&cpufreq_lock);
+ }
+
+ break;
+ case PM_POST_SUSPEND:
+
+ if (saved_frequency) {
+ /*
+ * While frequency_locked, only locking_frequency
+ * is valid for target(). In order to use
+ * saved_frequency while keeping frequency_locked,
+ * we temporarly overwrite locking_frequency.
+ */
+ temp = locking_frequency;
+ locking_frequency = saved_frequency;
+
+ mutex_unlock(&cpufreq_lock);
+ exynos4_target(policy, locking_frequency,
+ CPUFREQ_RELATION_H);
+ mutex_lock(&cpufreq_lock);
+
+ locking_frequency = temp;
+ }
+
+ frequency_locked = false;
+ break;
+ }
+out:
+ mutex_unlock(&cpufreq_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block exynos4_cpufreq_nb = {
+ .notifier_call = exynos4_cpufreq_pm_notifier,
+};
+
static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
+ int ret;
+
policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
@@ -479,16 +575,35 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
*/
cpumask_setall(policy->cpus);
- return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
+ ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
+ if (ret)
+ return ret;
+
+ cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
+
+ return 0;
+}
+
+static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
}
+static struct freq_attr *exynos4_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
static struct cpufreq_driver exynos4_driver = {
.flags = CPUFREQ_STICKY,
.verify = exynos4_verify_speed,
.target = exynos4_target,
.get = exynos4_getspeed,
.init = exynos4_cpufreq_cpu_init,
+ .exit = exynos4_cpufreq_cpu_exit,
.name = "exynos4_cpufreq",
+ .attr = exynos4_cpufreq_attr,
#ifdef CONFIG_PM
.suspend = exynos4_cpufreq_suspend,
.resume = exynos4_cpufreq_resume,
@@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void)
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
+ locking_frequency = exynos4_getspeed(0);
+
moutcore = clk_get(NULL, "moutcore");
if (IS_ERR(moutcore))
goto out;
@@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void)
printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
}
+ register_pm_notifier(&exynos4_cpufreq_nb);
+
return cpufreq_register_driver(&exynos4_driver);
out:
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e0b25de1e339..6d16b4b0d7a0 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -200,6 +200,7 @@ config CRYPTO_DEV_HIFN_795X
select CRYPTO_BLKCIPHER
select HW_RANDOM if CRYPTO_DEV_HIFN_795X_RNG
depends on PCI
+ depends on !ARCH_DMA_ADDR_T_64BIT
help
This option allows you to have support for HIFN 795x crypto adapters.
@@ -266,7 +267,7 @@ config CRYPTO_DEV_OMAP_AES
config CRYPTO_DEV_PICOXCELL
tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
- depends on ARCH_PICOXCELL
+ depends on ARCH_PICOXCELL && HAVE_CLK
select CRYPTO_AES
select CRYPTO_AUTHENC
select CRYPTO_ALGAPI
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index a84250a5dd51..fe765f49de58 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -2744,10 +2744,8 @@ static int __init hifn_init(void)
unsigned int freq;
int err;
- if (sizeof(dma_addr_t) > 4) {
- printk(KERN_INFO "HIFN supports only 32-bit addresses.\n");
- return -EINVAL;
- }
+ /* HIFN supports only 32-bit addresses */
+ BUILD_BUG_ON(sizeof(dma_addr_t) != 4);
if (strncmp(hifn_pll_ref, "ext", 3) &&
strncmp(hifn_pll_ref, "pci", 3)) {
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index d0183ddb3076..8944dabc0e3c 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1006,9 +1006,9 @@ static int n2_do_ecb(struct ablkcipher_request *req, bool encrypt)
spin_unlock_irqrestore(&qp->lock, flags);
+out:
put_cpu();
-out:
n2_chunk_complete(req, NULL);
return err;
}
@@ -1096,9 +1096,9 @@ static int n2_do_chaining(struct ablkcipher_request *req, bool encrypt)
spin_unlock_irqrestore(&qp->lock, flags);
+out:
put_cpu();
-out:
n2_chunk_complete(req, err ? NULL : final_iv_addr);
return err;
}
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index db33d300aa23..29b9469f8378 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -508,10 +508,8 @@ static int __init padlock_init(void)
int ret;
struct cpuinfo_x86 *c = &cpu_data(0);
- if (!cpu_has_xcrypt) {
- printk(KERN_NOTICE PFX "VIA PadLock not detected.\n");
+ if (!cpu_has_xcrypt)
return -ENODEV;
- }
if (!cpu_has_xcrypt_enabled) {
printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 230b5b8cda1f..a2b553eabbdb 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -34,6 +34,7 @@
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/rtnetlink.h>
@@ -1241,8 +1242,8 @@ static void spacc_spacc_complete(unsigned long data)
spin_unlock_irqrestore(&engine->hw_lock, flags);
list_for_each_entry_safe(req, tmp, &completed, list) {
- req->complete(req);
list_del(&req->list);
+ req->complete(req);
}
}
@@ -1657,10 +1658,33 @@ static struct spacc_alg l2_engine_algs[] = {
},
};
-static int __devinit spacc_probe(struct platform_device *pdev,
- unsigned max_ctxs, size_t cipher_pg_sz,
- size_t hash_pg_sz, size_t fifo_sz,
- struct spacc_alg *algs, size_t num_algs)
+#ifdef CONFIG_OF
+static const struct of_device_id spacc_of_id_table[] = {
+ { .compatible = "picochip,spacc-ipsec" },
+ { .compatible = "picochip,spacc-l2" },
+ {}
+};
+#else /* CONFIG_OF */
+#define spacc_of_id_table NULL
+#endif /* CONFIG_OF */
+
+static bool spacc_is_compatible(struct platform_device *pdev,
+ const char *spacc_type)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+
+ if (platid && !strcmp(platid->name, spacc_type))
+ return true;
+
+#ifdef CONFIG_OF
+ if (of_device_is_compatible(pdev->dev.of_node, spacc_type))
+ return true;
+#endif /* CONFIG_OF */
+
+ return false;
+}
+
+static int __devinit spacc_probe(struct platform_device *pdev)
{
int i, err, ret = -EINVAL;
struct resource *mem, *irq;
@@ -1669,13 +1693,25 @@ static int __devinit spacc_probe(struct platform_device *pdev,
if (!engine)
return -ENOMEM;
- engine->max_ctxs = max_ctxs;
- engine->cipher_pg_sz = cipher_pg_sz;
- engine->hash_pg_sz = hash_pg_sz;
- engine->fifo_sz = fifo_sz;
- engine->algs = algs;
- engine->num_algs = num_algs;
- engine->name = dev_name(&pdev->dev);
+ if (spacc_is_compatible(pdev, "picochip,spacc-ipsec")) {
+ engine->max_ctxs = SPACC_CRYPTO_IPSEC_MAX_CTXS;
+ engine->cipher_pg_sz = SPACC_CRYPTO_IPSEC_CIPHER_PG_SZ;
+ engine->hash_pg_sz = SPACC_CRYPTO_IPSEC_HASH_PG_SZ;
+ engine->fifo_sz = SPACC_CRYPTO_IPSEC_FIFO_SZ;
+ engine->algs = ipsec_engine_algs;
+ engine->num_algs = ARRAY_SIZE(ipsec_engine_algs);
+ } else if (spacc_is_compatible(pdev, "picochip,spacc-l2")) {
+ engine->max_ctxs = SPACC_CRYPTO_L2_MAX_CTXS;
+ engine->cipher_pg_sz = SPACC_CRYPTO_L2_CIPHER_PG_SZ;
+ engine->hash_pg_sz = SPACC_CRYPTO_L2_HASH_PG_SZ;
+ engine->fifo_sz = SPACC_CRYPTO_L2_FIFO_SZ;
+ engine->algs = l2_engine_algs;
+ engine->num_algs = ARRAY_SIZE(l2_engine_algs);
+ } else {
+ return -EINVAL;
+ }
+
+ engine->name = dev_name(&pdev->dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -1711,7 +1747,7 @@ static int __devinit spacc_probe(struct platform_device *pdev,
spin_lock_init(&engine->hw_lock);
- engine->clk = clk_get(&pdev->dev, NULL);
+ engine->clk = clk_get(&pdev->dev, "ref");
if (IS_ERR(engine->clk)) {
dev_info(&pdev->dev, "clk unavailable\n");
device_remove_file(&pdev->dev, &dev_attr_stat_irq_thresh);
@@ -1800,72 +1836,33 @@ static int __devexit spacc_remove(struct platform_device *pdev)
return 0;
}
-static int __devinit ipsec_probe(struct platform_device *pdev)
-{
- return spacc_probe(pdev, SPACC_CRYPTO_IPSEC_MAX_CTXS,
- SPACC_CRYPTO_IPSEC_CIPHER_PG_SZ,
- SPACC_CRYPTO_IPSEC_HASH_PG_SZ,
- SPACC_CRYPTO_IPSEC_FIFO_SZ, ipsec_engine_algs,
- ARRAY_SIZE(ipsec_engine_algs));
-}
-
-static struct platform_driver ipsec_driver = {
- .probe = ipsec_probe,
- .remove = __devexit_p(spacc_remove),
- .driver = {
- .name = "picoxcell-ipsec",
-#ifdef CONFIG_PM
- .pm = &spacc_pm_ops,
-#endif /* CONFIG_PM */
- },
+static const struct platform_device_id spacc_id_table[] = {
+ { "picochip,spacc-ipsec", },
+ { "picochip,spacc-l2", },
};
-static int __devinit l2_probe(struct platform_device *pdev)
-{
- return spacc_probe(pdev, SPACC_CRYPTO_L2_MAX_CTXS,
- SPACC_CRYPTO_L2_CIPHER_PG_SZ,
- SPACC_CRYPTO_L2_HASH_PG_SZ, SPACC_CRYPTO_L2_FIFO_SZ,
- l2_engine_algs, ARRAY_SIZE(l2_engine_algs));
-}
-
-static struct platform_driver l2_driver = {
- .probe = l2_probe,
+static struct platform_driver spacc_driver = {
+ .probe = spacc_probe,
.remove = __devexit_p(spacc_remove),
.driver = {
- .name = "picoxcell-l2",
+ .name = "picochip,spacc",
#ifdef CONFIG_PM
.pm = &spacc_pm_ops,
#endif /* CONFIG_PM */
+ .of_match_table = spacc_of_id_table,
},
+ .id_table = spacc_id_table,
};
static int __init spacc_init(void)
{
- int ret = platform_driver_register(&ipsec_driver);
- if (ret) {
- pr_err("failed to register ipsec spacc driver");
- goto out;
- }
-
- ret = platform_driver_register(&l2_driver);
- if (ret) {
- pr_err("failed to register l2 spacc driver");
- goto l2_failed;
- }
-
- return 0;
-
-l2_failed:
- platform_driver_unregister(&ipsec_driver);
-out:
- return ret;
+ return platform_driver_register(&spacc_driver);
}
module_init(spacc_init);
static void __exit spacc_exit(void)
{
- platform_driver_unregister(&ipsec_driver);
- platform_driver_unregister(&l2_driver);
+ platform_driver_unregister(&spacc_driver);
}
module_exit(spacc_exit);
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 8a0bb417aa11..dbe76b5df9cf 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -416,7 +416,7 @@ static void talitos_done(unsigned long data)
/*
* locate current (offending) descriptor
*/
-static struct talitos_desc *current_desc(struct device *dev, int ch)
+static u32 current_desc_hdr(struct device *dev, int ch)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int tail = priv->chan[ch].tail;
@@ -428,23 +428,25 @@ static struct talitos_desc *current_desc(struct device *dev, int ch)
tail = (tail + 1) & (priv->fifo_len - 1);
if (tail == priv->chan[ch].tail) {
dev_err(dev, "couldn't locate current descriptor\n");
- return NULL;
+ return 0;
}
}
- return priv->chan[ch].fifo[tail].desc;
+ return priv->chan[ch].fifo[tail].desc->hdr;
}
/*
* user diagnostics; report root cause of error based on execution unit status
*/
-static void report_eu_error(struct device *dev, int ch,
- struct talitos_desc *desc)
+static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int i;
- switch (desc->hdr & DESC_HDR_SEL0_MASK) {
+ if (!desc_hdr)
+ desc_hdr = in_be32(priv->reg + TALITOS_DESCBUF(ch));
+
+ switch (desc_hdr & DESC_HDR_SEL0_MASK) {
case DESC_HDR_SEL0_AFEU:
dev_err(dev, "AFEUISR 0x%08x_%08x\n",
in_be32(priv->reg + TALITOS_AFEUISR),
@@ -488,7 +490,7 @@ static void report_eu_error(struct device *dev, int ch,
break;
}
- switch (desc->hdr & DESC_HDR_SEL1_MASK) {
+ switch (desc_hdr & DESC_HDR_SEL1_MASK) {
case DESC_HDR_SEL1_MDEUA:
case DESC_HDR_SEL1_MDEUB:
dev_err(dev, "MDEUISR 0x%08x_%08x\n",
@@ -550,7 +552,7 @@ static void talitos_error(unsigned long data, u32 isr, u32 isr_lo)
if (v_lo & TALITOS_CCPSR_LO_IEU)
dev_err(dev, "invalid execution unit error\n");
if (v_lo & TALITOS_CCPSR_LO_EU)
- report_eu_error(dev, ch, current_desc(dev, ch));
+ report_eu_error(dev, ch, current_desc_hdr(dev, ch));
if (v_lo & TALITOS_CCPSR_LO_GB)
dev_err(dev, "gather boundary error\n");
if (v_lo & TALITOS_CCPSR_LO_GRL)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 2e3b3d38c465..ab8f469f5cf8 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
config PL330_DMA
tristate "DMA API Driver for PL330"
select DMA_ENGINE
- depends on PL330
+ depends on ARM_AMBA
+ select PL330
help
Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index be21e3f138a8..b7cbd1ab1db1 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -66,32 +66,29 @@
* after the final transfer signalled by LBREQ or LSREQ. The DMAC
* will then move to the next LLI entry.
*
- * Only the former works sanely with scatter lists, so we only implement
- * the DMAC flow control method. However, peripherals which use the LBREQ
- * and LSREQ signals (eg, MMCI) are unable to use this mode, which through
- * these hardware restrictions prevents them from using scatter DMA.
- *
* Global TODO:
* - Break out common code from arch/arm/mach-s3c64xx and share
*/
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl08x.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
-
+#include <linux/slab.h>
#include <asm/hardware/pl080.h>
#define DRIVER_NAME "pl08xdmac"
+static struct amba_driver pl08x_amba_driver;
+
/**
* struct vendor_data - vendor-specific config parameters for PL08x derivatives
* @channels: the number of channels available in this variant
@@ -126,7 +123,8 @@ struct pl08x_lli {
* @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
+ * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
+ * fetches
* @mem_buses: set to indicate memory transfers on AHB2.
* @lock: a spinlock for this struct
*/
@@ -149,14 +147,6 @@ struct pl08x_driver_data {
* PL08X specific defines
*/
-/*
- * Memory boundaries: the manual for PL08x says that the controller
- * cannot read past a 1KiB boundary, so these defines are used to
- * create transfer LLIs that do not cross such boundaries.
- */
-#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */
-#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
-
/* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
@@ -272,7 +262,6 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
writel(val, ch->base + PL080_CH_CONFIG);
}
-
/*
* pl08x_terminate_phy_chan() stops the channel, clears the FIFO and
* clears any pending interrupt status. This should not be used for
@@ -363,7 +352,9 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
if (!list_empty(&plchan->pend_list)) {
struct pl08x_txd *txdi;
list_for_each_entry(txdi, &plchan->pend_list, node) {
- bytes += txdi->len;
+ struct pl08x_sg *dsg;
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ bytes += dsg->len;
}
}
@@ -407,6 +398,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
return NULL;
}
+ pm_runtime_get_sync(&pl08x->adev->dev);
return ch;
}
@@ -420,6 +412,8 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
/* Stop the channel and clear its interrupts */
pl08x_terminate_phy_chan(pl08x, ch);
+ pm_runtime_put(&pl08x->adev->dev);
+
/* Mark it as free */
ch->serving = NULL;
spin_unlock_irqrestore(&ch->lock, flags);
@@ -499,36 +493,30 @@ struct pl08x_lli_build_data {
};
/*
- * Autoselect a master bus to use for the transfer this prefers the
- * destination bus if both available if fixed address on one bus the
- * other will be chosen
+ * Autoselect a master bus to use for the transfer. Slave will be the chosen as
+ * victim in case src & dest are not similarly aligned. i.e. If after aligning
+ * masters address with width requirements of transfer (by sending few byte by
+ * byte data), slave is still not aligned, then its width will be reduced to
+ * BYTE.
+ * - prefers the destination bus if both available
+ * - prefers bus with fixed address (i.e. peripheral)
*/
static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd,
struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl)
{
if (!(cctl & PL080_CONTROL_DST_INCR)) {
- *mbus = &bd->srcbus;
- *sbus = &bd->dstbus;
- } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
+ } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
+ *mbus = &bd->srcbus;
+ *sbus = &bd->dstbus;
} else {
- if (bd->dstbus.buswidth == 4) {
+ if (bd->dstbus.buswidth >= bd->srcbus.buswidth) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
- } else if (bd->srcbus.buswidth == 4) {
- *mbus = &bd->srcbus;
- *sbus = &bd->dstbus;
- } else if (bd->dstbus.buswidth == 2) {
- *mbus = &bd->dstbus;
- *sbus = &bd->srcbus;
- } else if (bd->srcbus.buswidth == 2) {
+ } else {
*mbus = &bd->srcbus;
*sbus = &bd->dstbus;
- } else {
- /* bd->srcbus.buswidth == 1 */
- *mbus = &bd->dstbus;
- *sbus = &bd->srcbus;
}
}
}
@@ -547,7 +535,8 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
llis_va[num_llis].cctl = cctl;
llis_va[num_llis].src = bd->srcbus.addr;
llis_va[num_llis].dst = bd->dstbus.addr;
- llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
+ llis_va[num_llis].lli = llis_bus + (num_llis + 1) *
+ sizeof(struct pl08x_lli);
llis_va[num_llis].lli |= bd->lli_bus;
if (cctl & PL080_CONTROL_SRC_INCR)
@@ -560,16 +549,12 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
bd->remainder -= len;
}
-/*
- * Return number of bytes to fill to boundary, or len.
- * This calculation works for any value of addr.
- */
-static inline size_t pl08x_pre_boundary(u32 addr, size_t len)
+static inline void prep_byte_width_lli(struct pl08x_lli_build_data *bd,
+ u32 *cctl, u32 len, int num_llis, size_t *total_bytes)
{
- size_t boundary_len = PL08X_BOUNDARY_SIZE -
- (addr & (PL08X_BOUNDARY_SIZE - 1));
-
- return min(boundary_len, len);
+ *cctl = pl08x_cctl_bits(*cctl, 1, 1, len);
+ pl08x_fill_lli_for_desc(bd, num_llis, len, *cctl);
+ (*total_bytes) += len;
}
/*
@@ -583,13 +568,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
struct pl08x_bus_data *mbus, *sbus;
struct pl08x_lli_build_data bd;
int num_llis = 0;
- u32 cctl;
- size_t max_bytes_per_lli;
- size_t total_bytes = 0;
+ u32 cctl, early_bytes = 0;
+ size_t max_bytes_per_lli, total_bytes;
struct pl08x_lli *llis_va;
+ struct pl08x_sg *dsg;
- txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT,
- &txd->llis_bus);
+ txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus);
if (!txd->llis_va) {
dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__);
return 0;
@@ -597,13 +581,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
pl08x->pool_ctr++;
- /* Get the default CCTL */
- cctl = txd->cctl;
-
bd.txd = txd;
- bd.srcbus.addr = txd->src_addr;
- bd.dstbus.addr = txd->dst_addr;
bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
+ cctl = txd->cctl;
/* Find maximum width of the source bus */
bd.srcbus.maxwidth =
@@ -615,215 +595,179 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >>
PL080_CONTROL_DWIDTH_SHIFT);
- /* Set up the bus widths to the maximum */
- bd.srcbus.buswidth = bd.srcbus.maxwidth;
- bd.dstbus.buswidth = bd.dstbus.maxwidth;
+ list_for_each_entry(dsg, &txd->dsg_list, node) {
+ total_bytes = 0;
+ cctl = txd->cctl;
- /*
- * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
- */
- max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
- PL080_CONTROL_TRANSFER_SIZE_MASK;
+ bd.srcbus.addr = dsg->src_addr;
+ bd.dstbus.addr = dsg->dst_addr;
+ bd.remainder = dsg->len;
+ bd.srcbus.buswidth = bd.srcbus.maxwidth;
+ bd.dstbus.buswidth = bd.dstbus.maxwidth;
- /* We need to count this down to zero */
- bd.remainder = txd->len;
+ pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
- /*
- * Choose bus to align to
- * - prefers destination bus if both available
- * - if fixed address on one bus chooses other
- */
- pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
-
- dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
- bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
- bd.srcbus.buswidth,
- bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
- bd.dstbus.buswidth,
- bd.remainder, max_bytes_per_lli);
- dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
- mbus == &bd.srcbus ? "src" : "dst",
- sbus == &bd.srcbus ? "src" : "dst");
-
- if (txd->len < mbus->buswidth) {
- /* Less than a bus width available - send as single bytes */
- while (bd.remainder) {
- dev_vdbg(&pl08x->adev->dev,
- "%s single byte LLIs for a transfer of "
- "less than a bus width (remain 0x%08x)\n",
- __func__, bd.remainder);
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
- }
- } else {
- /* Make one byte LLIs until master bus is aligned */
- while ((mbus->addr) % (mbus->buswidth)) {
- dev_vdbg(&pl08x->adev->dev,
- "%s adjustment lli for less than bus width "
- "(remain 0x%08x)\n",
- __func__, bd.remainder);
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
- }
+ dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n",
+ bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
+ bd.srcbus.buswidth,
+ bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
+ bd.dstbus.buswidth,
+ bd.remainder);
+ dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
+ mbus == &bd.srcbus ? "src" : "dst",
+ sbus == &bd.srcbus ? "src" : "dst");
/*
- * Master now aligned
- * - if slave is not then we must set its width down
+ * Zero length is only allowed if all these requirements are
+ * met:
+ * - flow controller is peripheral.
+ * - src.addr is aligned to src.width
+ * - dst.addr is aligned to dst.width
+ *
+ * sg_len == 1 should be true, as there can be two cases here:
+ *
+ * - Memory addresses are contiguous and are not scattered.
+ * Here, Only one sg will be passed by user driver, with
+ * memory address and zero length. We pass this to controller
+ * and after the transfer it will receive the last burst
+ * request from peripheral and so transfer finishes.
+ *
+ * - Memory addresses are scattered and are not contiguous.
+ * Here, Obviously as DMA controller doesn't know when a lli's
+ * transfer gets over, it can't load next lli. So in this
+ * case, there has to be an assumption that only one lli is
+ * supported. Thus, we can't have scattered addresses.
*/
- if (sbus->addr % sbus->buswidth) {
- dev_dbg(&pl08x->adev->dev,
- "%s set down bus width to one byte\n",
- __func__);
+ if (!bd.remainder) {
+ u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >>
+ PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ if (!((fc >= PL080_FLOW_SRC2DST_DST) &&
+ (fc <= PL080_FLOW_SRC2DST_SRC))) {
+ dev_err(&pl08x->adev->dev, "%s sg len can't be zero",
+ __func__);
+ return 0;
+ }
+
+ if ((bd.srcbus.addr % bd.srcbus.buswidth) ||
+ (bd.srcbus.addr % bd.srcbus.buswidth)) {
+ dev_err(&pl08x->adev->dev,
+ "%s src & dst address must be aligned to src"
+ " & dst width if peripheral is flow controller",
+ __func__);
+ return 0;
+ }
- sbus->buswidth = 1;
+ cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth,
+ bd.dstbus.buswidth, 0);
+ pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl);
+ break;
}
/*
- * Make largest possible LLIs until less than one bus
- * width left
+ * Send byte by byte for following cases
+ * - Less than a bus width available
+ * - until master bus is aligned
*/
- while (bd.remainder > (mbus->buswidth - 1)) {
- size_t lli_len, target_len, tsize, odd_bytes;
+ if (bd.remainder < mbus->buswidth)
+ early_bytes = bd.remainder;
+ else if ((mbus->addr) % (mbus->buswidth)) {
+ early_bytes = mbus->buswidth - (mbus->addr) %
+ (mbus->buswidth);
+ if ((bd.remainder - early_bytes) < mbus->buswidth)
+ early_bytes = bd.remainder;
+ }
+ if (early_bytes) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s byte width LLIs (remain 0x%08x)\n",
+ __func__, bd.remainder);
+ prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++,
+ &total_bytes);
+ }
+
+ if (bd.remainder) {
/*
- * If enough left try to send max possible,
- * otherwise try to send the remainder
+ * Master now aligned
+ * - if slave is not then we must set its width down
*/
- target_len = min(bd.remainder, max_bytes_per_lli);
+ if (sbus->addr % sbus->buswidth) {
+ dev_dbg(&pl08x->adev->dev,
+ "%s set down bus width to one byte\n",
+ __func__);
+
+ sbus->buswidth = 1;
+ }
/*
- * Set bus lengths for incrementing buses to the
- * number of bytes which fill to next memory boundary,
- * limiting on the target length calculated above.
+ * Bytes transferred = tsize * src width, not
+ * MIN(buswidths)
*/
- if (cctl & PL080_CONTROL_SRC_INCR)
- bd.srcbus.fill_bytes =
- pl08x_pre_boundary(bd.srcbus.addr,
- target_len);
- else
- bd.srcbus.fill_bytes = target_len;
-
- if (cctl & PL080_CONTROL_DST_INCR)
- bd.dstbus.fill_bytes =
- pl08x_pre_boundary(bd.dstbus.addr,
- target_len);
- else
- bd.dstbus.fill_bytes = target_len;
-
- /* Find the nearest */
- lli_len = min(bd.srcbus.fill_bytes,
- bd.dstbus.fill_bytes);
-
- BUG_ON(lli_len > bd.remainder);
-
- if (lli_len <= 0) {
- dev_err(&pl08x->adev->dev,
- "%s lli_len is %zu, <= 0\n",
- __func__, lli_len);
- return 0;
- }
+ max_bytes_per_lli = bd.srcbus.buswidth *
+ PL080_CONTROL_TRANSFER_SIZE_MASK;
+ dev_vdbg(&pl08x->adev->dev,
+ "%s max bytes per lli = %zu\n",
+ __func__, max_bytes_per_lli);
+
+ /*
+ * Make largest possible LLIs until less than one bus
+ * width left
+ */
+ while (bd.remainder > (mbus->buswidth - 1)) {
+ size_t lli_len, tsize, width;
- if (lli_len == target_len) {
- /*
- * Can send what we wanted.
- * Maintain alignment
- */
- lli_len = (lli_len/mbus->buswidth) *
- mbus->buswidth;
- odd_bytes = 0;
- } else {
/*
- * So now we know how many bytes to transfer
- * to get to the nearest boundary. The next
- * LLI will past the boundary. However, we
- * may be working to a boundary on the slave
- * bus. We need to ensure the master stays
- * aligned, and that we are working in
- * multiples of the bus widths.
+ * If enough left try to send max possible,
+ * otherwise try to send the remainder
*/
- odd_bytes = lli_len % mbus->buswidth;
- lli_len -= odd_bytes;
-
- }
+ lli_len = min(bd.remainder, max_bytes_per_lli);
- if (lli_len) {
/*
- * Check against minimum bus alignment:
- * Calculate actual transfer size in relation
- * to bus width an get a maximum remainder of
- * the smallest bus width - 1
+ * Check against maximum bus alignment:
+ * Calculate actual transfer size in relation to
+ * bus width an get a maximum remainder of the
+ * highest bus width - 1
*/
- /* FIXME: use round_down()? */
- tsize = lli_len / min(mbus->buswidth,
- sbus->buswidth);
- lli_len = tsize * min(mbus->buswidth,
- sbus->buswidth);
-
- if (target_len != lli_len) {
- dev_vdbg(&pl08x->adev->dev,
- "%s can't send what we want. Desired 0x%08zx, lli of 0x%08zx bytes in txd of 0x%08zx\n",
- __func__, target_len, lli_len, txd->len);
- }
-
- cctl = pl08x_cctl_bits(cctl,
- bd.srcbus.buswidth,
- bd.dstbus.buswidth,
- tsize);
+ width = max(mbus->buswidth, sbus->buswidth);
+ lli_len = (lli_len / width) * width;
+ tsize = lli_len / bd.srcbus.buswidth;
dev_vdbg(&pl08x->adev->dev,
- "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n",
+ "%s fill lli with single lli chunk of "
+ "size 0x%08zx (remainder 0x%08zx)\n",
__func__, lli_len, bd.remainder);
+
+ cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth,
+ bd.dstbus.buswidth, tsize);
pl08x_fill_lli_for_desc(&bd, num_llis++,
- lli_len, cctl);
+ lli_len, cctl);
total_bytes += lli_len;
}
-
- if (odd_bytes) {
- /*
- * Creep past the boundary, maintaining
- * master alignment
- */
- int j;
- for (j = 0; (j < mbus->buswidth)
- && (bd.remainder); j++) {
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- dev_vdbg(&pl08x->adev->dev,
- "%s align with boundary, single byte (remain 0x%08zx)\n",
- __func__, bd.remainder);
- pl08x_fill_lli_for_desc(&bd,
- num_llis++, 1, cctl);
- total_bytes++;
- }
+ /*
+ * Send any odd bytes
+ */
+ if (bd.remainder) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s align with boundary, send odd bytes (remain %zu)\n",
+ __func__, bd.remainder);
+ prep_byte_width_lli(&bd, &cctl, bd.remainder,
+ num_llis++, &total_bytes);
}
}
- /*
- * Send any odd bytes
- */
- while (bd.remainder) {
- cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- dev_vdbg(&pl08x->adev->dev,
- "%s align with boundary, single odd byte (remain %zu)\n",
- __func__, bd.remainder);
- pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
- total_bytes++;
+ if (total_bytes != dsg->len) {
+ dev_err(&pl08x->adev->dev,
+ "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
+ __func__, total_bytes, dsg->len);
+ return 0;
}
- }
- if (total_bytes != txd->len) {
- dev_err(&pl08x->adev->dev,
- "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
- __func__, total_bytes, txd->len);
- return 0;
- }
- if (num_llis >= MAX_NUM_TSFR_LLIS) {
- dev_err(&pl08x->adev->dev,
- "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
- __func__, (u32) MAX_NUM_TSFR_LLIS);
- return 0;
+ if (num_llis >= MAX_NUM_TSFR_LLIS) {
+ dev_err(&pl08x->adev->dev,
+ "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
+ __func__, (u32) MAX_NUM_TSFR_LLIS);
+ return 0;
+ }
}
llis_va = txd->llis_va;
@@ -856,11 +800,19 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txd)
{
+ struct pl08x_sg *dsg, *_dsg;
+
/* Free the LLI */
- dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
+ 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);
+ }
+
kfree(txd);
}
@@ -917,9 +869,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
* need, but for slaves the physical signals may be muxed!
* Can the platform allow us to use this channel?
*/
- if (plchan->slave &&
- ch->signal < 0 &&
- pl08x->pd->get_signal) {
+ if (plchan->slave && pl08x->pd->get_signal) {
ret = pl08x->pd->get_signal(plchan);
if (ret < 0) {
dev_dbg(&pl08x->adev->dev,
@@ -1008,10 +958,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
* If slaves are relying on interrupts to signal completion this function
* must not be called with interrupts disabled.
*/
-static enum dma_status
-pl08x_dma_tx_status(struct dma_chan *chan,
- dma_cookie_t cookie,
- struct dma_tx_state *txstate)
+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);
dma_cookie_t last_used;
@@ -1253,7 +1201,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
if (!num_llis) {
- kfree(txd);
+ spin_lock_irqsave(&plchan->lock, flags);
+ pl08x_free_txd(pl08x, txd);
+ spin_unlock_irqrestore(&plchan->lock, flags);
return -EINVAL;
}
@@ -1301,13 +1251,14 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
unsigned long flags)
{
- struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ 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 */
txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK |
@@ -1326,6 +1277,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd;
+ struct pl08x_sg *dsg;
int ret;
txd = pl08x_get_txd(plchan, flags);
@@ -1335,10 +1287,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
return NULL;
}
+ dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
+ if (!dsg) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev, "%s no memory for pl080 sg\n",
+ __func__);
+ return NULL;
+ }
+ list_add_tail(&dsg->node, &txd->dsg_list);
+
txd->direction = DMA_NONE;
- txd->src_addr = src;
- txd->dst_addr = dest;
- txd->len = len;
+ 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;
@@ -1367,19 +1328,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd;
- int ret;
-
- /*
- * Current implementation ASSUMES only one sg
- */
- if (sg_len != 1) {
- dev_err(&pl08x->adev->dev, "%s prepared too long sglist\n",
- __func__);
- BUG();
- }
+ struct pl08x_sg *dsg;
+ struct scatterlist *sg;
+ dma_addr_t slave_addr;
+ int ret, tmp;
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
- __func__, sgl->length, plchan->name);
+ __func__, sgl->length, plchan->name);
txd = pl08x_get_txd(plchan, flags);
if (!txd) {
@@ -1398,24 +1353,49 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
* channel target address dynamically at runtime.
*/
txd->direction = direction;
- txd->len = sgl->length;
if (direction == DMA_TO_DEVICE) {
- txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
txd->cctl = plchan->dst_cctl;
- txd->src_addr = sgl->dma_address;
- txd->dst_addr = plchan->dst_addr;
+ slave_addr = plchan->dst_addr;
} else if (direction == DMA_FROM_DEVICE) {
- txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
txd->cctl = plchan->src_cctl;
- txd->src_addr = plchan->src_addr;
- txd->dst_addr = sgl->dma_address;
+ slave_addr = plchan->src_addr;
} else {
+ pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev,
"%s direction unsupported\n", __func__);
return NULL;
}
+ if (plchan->cd->device_fc)
+ tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER :
+ PL080_FLOW_PER2MEM_PER;
+ else
+ tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER :
+ PL080_FLOW_PER2MEM;
+
+ txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+
+ for_each_sg(sgl, sg, sg_len, tmp) {
+ dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
+ if (!dsg) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
+ __func__);
+ return NULL;
+ }
+ list_add_tail(&dsg->node, &txd->dsg_list);
+
+ dsg->len = sg_dma_len(sg);
+ if (direction == DMA_TO_DEVICE) {
+ dsg->src_addr = sg_phys(sg);
+ dsg->dst_addr = slave_addr;
+ } else {
+ dsg->src_addr = slave_addr;
+ dsg->dst_addr = sg_phys(sg);
+ }
+ }
+
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
return NULL;
@@ -1489,9 +1469,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_dma_chan *plchan;
char *name = chan_id;
+ /* Reject channels for devices not bound to this driver */
+ if (chan->device->dev->driver != &pl08x_amba_driver.drv)
+ return false;
+
+ plchan = to_pl08x_chan(chan);
+
/* Check that the channel is not taken! */
if (!strcmp(plchan->name, name))
return true;
@@ -1507,34 +1493,34 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
*/
static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
{
- u32 val;
-
- val = readl(pl08x->base + PL080_CONFIG);
- val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE);
- /* We implicitly clear bit 1 and that means little-endian mode */
- val |= PL080_CONFIG_ENABLE;
- writel(val, pl08x->base + PL080_CONFIG);
+ 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)
- dma_unmap_single(dev, txd->src_addr, txd->len,
- DMA_TO_DEVICE);
- else
- dma_unmap_page(dev, txd->src_addr, txd->len,
- DMA_TO_DEVICE);
+ 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)
- dma_unmap_single(dev, txd->dst_addr, txd->len,
- DMA_FROM_DEVICE);
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
else
- dma_unmap_page(dev, txd->dst_addr, txd->len,
- DMA_FROM_DEVICE);
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
}
}
@@ -1589,8 +1575,8 @@ static void pl08x_tasklet(unsigned long data)
*/
list_for_each_entry(waiting, &pl08x->memcpy.channels,
chan.device_node) {
- if (waiting->state == PL08X_CHAN_WAITING &&
- waiting->waiting != NULL) {
+ if (waiting->state == PL08X_CHAN_WAITING &&
+ waiting->waiting != NULL) {
int ret;
/* This should REALLY not fail now */
@@ -1630,38 +1616,40 @@ static void pl08x_tasklet(unsigned long data)
static irqreturn_t pl08x_irq(int irq, void *dev)
{
struct pl08x_driver_data *pl08x = dev;
- u32 mask = 0;
- u32 val;
- int i;
-
- val = readl(pl08x->base + PL080_ERR_STATUS);
- if (val) {
- /* An error interrupt (on one or more channels) */
- dev_err(&pl08x->adev->dev,
- "%s error interrupt, register value 0x%08x\n",
- __func__, val);
- /*
- * Simply clear ALL PL08X error interrupts,
- * regardless of channel and cause
- * FIXME: should be 0x00000003 on PL081 really.
- */
- writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
+ u32 mask = 0, err, tc, i;
+
+ /* check & clear - ERR & TC interrupts */
+ err = readl(pl08x->base + PL080_ERR_STATUS);
+ if (err) {
+ dev_err(&pl08x->adev->dev, "%s error interrupt, register value 0x%08x\n",
+ __func__, err);
+ writel(err, pl08x->base + PL080_ERR_CLEAR);
}
- val = readl(pl08x->base + PL080_INT_STATUS);
+ tc = readl(pl08x->base + PL080_INT_STATUS);
+ if (tc)
+ writel(tc, pl08x->base + PL080_TC_CLEAR);
+
+ if (!err && !tc)
+ return IRQ_NONE;
+
for (i = 0; i < pl08x->vd->channels; i++) {
- if ((1 << i) & val) {
+ if (((1 << i) & err) || ((1 << i) & tc)) {
/* Locate physical channel */
struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
struct pl08x_dma_chan *plchan = phychan->serving;
+ if (!plchan) {
+ dev_err(&pl08x->adev->dev,
+ "%s Error TC interrupt on unused channel: 0x%08x\n",
+ __func__, i);
+ continue;
+ }
+
/* Schedule tasklet on this channel */
tasklet_schedule(&plchan->tasklet);
-
mask |= (1 << i);
}
}
- /* Clear only the terminal interrupts on channels we processed */
- writel(mask, pl08x->base + PL080_TC_CLEAR);
return mask ? IRQ_HANDLED : IRQ_NONE;
}
@@ -1685,9 +1673,7 @@ static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
* Make a local wrapper to hold required data
*/
static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
- struct dma_device *dmadev,
- unsigned int channels,
- bool slave)
+ struct dma_device *dmadev, unsigned int channels, bool slave)
{
struct pl08x_dma_chan *chan;
int i;
@@ -1700,7 +1686,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
* to cope with that situation.
*/
for (i = 0; i < channels; i++) {
- chan = kzalloc(sizeof(struct pl08x_dma_chan), GFP_KERNEL);
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan) {
dev_err(&pl08x->adev->dev,
"%s no memory for channel\n", __func__);
@@ -1728,7 +1714,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
kfree(chan);
continue;
}
- dev_info(&pl08x->adev->dev,
+ dev_dbg(&pl08x->adev->dev,
"initialize virtual channel \"%s\"\n",
chan->name);
@@ -1837,9 +1823,9 @@ static const struct file_operations pl08x_debugfs_operations = {
static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
{
/* Expose a simple debugfs interface to view all clocks */
- (void) debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO,
- NULL, pl08x,
- &pl08x_debugfs_operations);
+ (void) debugfs_create_file(dev_name(&pl08x->adev->dev),
+ S_IFREG | S_IRUGO, NULL, pl08x,
+ &pl08x_debugfs_operations);
}
#else
@@ -1860,12 +1846,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
return ret;
/* Create the driver state holder */
- pl08x = kzalloc(sizeof(struct pl08x_driver_data), GFP_KERNEL);
+ pl08x = kzalloc(sizeof(*pl08x), GFP_KERNEL);
if (!pl08x) {
ret = -ENOMEM;
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;
@@ -1939,7 +1928,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
}
/* Initialize physical channels */
- pl08x->phy_chans = kmalloc((vd->channels * sizeof(struct pl08x_phy_chan)),
+ pl08x->phy_chans = kmalloc((vd->channels * sizeof(*pl08x->phy_chans)),
GFP_KERNEL);
if (!pl08x->phy_chans) {
dev_err(&adev->dev, "%s failed to allocate "
@@ -1956,9 +1945,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&ch->lock);
ch->serving = NULL;
ch->signal = -1;
- dev_info(&adev->dev,
- "physical channel %d is %s\n", i,
- pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
+ dev_dbg(&adev->dev, "physical channel %d is %s\n",
+ i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
}
/* Register as many memcpy channels as there are physical channels */
@@ -1974,8 +1962,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
/* Register slave channels */
ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave,
- pl08x->pd->num_slave_channels,
- true);
+ pl08x->pd->num_slave_channels, true);
if (ret <= 0) {
dev_warn(&pl08x->adev->dev,
"%s failed to enumerate slave channels - %d\n",
@@ -2005,6 +1992,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
dev_info(&pl08x->adev->dev, "DMA: PL%03x rev%u at 0x%08llx irq %d\n",
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:
@@ -2023,6 +2012,9 @@ 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/at_hdmac.c b/drivers/dma/at_hdmac.c
index 6a483eac7b3f..fcfa0a8b5c59 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -107,10 +107,11 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
{
struct at_desc *desc, *_desc;
struct at_desc *ret = NULL;
+ unsigned long flags;
unsigned int i = 0;
LIST_HEAD(tmp_list);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) {
i++;
if (async_tx_test_ack(&desc->txd)) {
@@ -121,7 +122,7 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
dev_dbg(chan2dev(&atchan->chan_common),
"desc %p not ACKed\n", desc);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
dev_vdbg(chan2dev(&atchan->chan_common),
"scanned %u descriptors on freelist\n", i);
@@ -129,9 +130,9 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
if (!ret) {
ret = atc_alloc_descriptor(&atchan->chan_common, GFP_ATOMIC);
if (ret) {
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated++;
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else {
dev_err(chan2dev(&atchan->chan_common),
"not enough descriptors available\n");
@@ -150,8 +151,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
{
if (desc) {
struct at_desc *child;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
list_for_each_entry(child, &desc->tx_list, desc_node)
dev_vdbg(chan2dev(&atchan->chan_common),
"moving child desc %p to freelist\n",
@@ -160,7 +162,7 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
dev_vdbg(chan2dev(&atchan->chan_common),
"moving desc %p to freelist\n", desc);
list_add(&desc->desc_node, &atchan->free_list);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
}
@@ -299,7 +301,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
/* for cyclic transfers,
* no need to replay callback function while stopping */
- if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) {
+ if (!atc_chan_is_cyclic(atchan)) {
dma_async_tx_callback callback = txd->callback;
void *param = txd->callback_param;
@@ -471,16 +473,17 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan)
static void atc_tasklet(unsigned long data)
{
struct at_dma_chan *atchan = (struct at_dma_chan *)data;
+ unsigned long flags;
- spin_lock(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status))
atc_handle_error(atchan);
- else if (test_bit(ATC_IS_CYCLIC, &atchan->status))
+ else if (atc_chan_is_cyclic(atchan))
atc_handle_cyclic(atchan);
else
atc_advance_work(atchan);
- spin_unlock(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
@@ -539,8 +542,9 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
struct at_desc *desc = txd_to_at_desc(tx);
struct at_dma_chan *atchan = to_at_dma_chan(tx->chan);
dma_cookie_t cookie;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
cookie = atc_assign_cookie(atchan, desc);
if (list_empty(&atchan->active_list)) {
@@ -554,7 +558,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
list_add_tail(&desc->desc_node, &atchan->queue);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return cookie;
}
@@ -927,28 +931,29 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma *atdma = to_at_dma(chan->device);
int chan_id = atchan->chan_common.chan_id;
+ unsigned long flags;
LIST_HEAD(list);
dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd);
if (cmd == DMA_PAUSE) {
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id));
set_bit(ATC_IS_PAUSED, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else if (cmd == DMA_RESUME) {
- if (!test_bit(ATC_IS_PAUSED, &atchan->status))
+ if (!atc_chan_is_paused(atchan))
return 0;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id));
clear_bit(ATC_IS_PAUSED, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else if (cmd == DMA_TERMINATE_ALL) {
struct at_desc *desc, *_desc;
/*
@@ -957,7 +962,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* channel. We still have to poll the channel enable bit due
* to AHB/HSB limitations.
*/
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
/* disabling channel: must also remove suspend state */
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask);
@@ -978,7 +983,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* if channel dedicated to cyclic operations, free it */
clear_bit(ATC_IS_CYCLIC, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
} else {
return -ENXIO;
}
@@ -1004,9 +1009,10 @@ atc_tx_status(struct dma_chan *chan,
struct at_dma_chan *atchan = to_at_dma_chan(chan);
dma_cookie_t last_used;
dma_cookie_t last_complete;
+ unsigned long flags;
enum dma_status ret;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
last_complete = atchan->completed_cookie;
last_used = chan->cookie;
@@ -1021,7 +1027,7 @@ atc_tx_status(struct dma_chan *chan,
ret = dma_async_is_complete(cookie, last_complete, last_used);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
if (ret != DMA_SUCCESS)
dma_set_tx_state(txstate, last_complete, last_used,
@@ -1029,7 +1035,7 @@ atc_tx_status(struct dma_chan *chan,
else
dma_set_tx_state(txstate, last_complete, last_used, 0);
- if (test_bit(ATC_IS_PAUSED, &atchan->status))
+ if (atc_chan_is_paused(atchan))
ret = DMA_PAUSED;
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n",
@@ -1046,18 +1052,19 @@ atc_tx_status(struct dma_chan *chan,
static void atc_issue_pending(struct dma_chan *chan)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
+ unsigned long flags;
dev_vdbg(chan2dev(chan), "issue_pending\n");
/* Not needed for cyclic transfers */
- if (test_bit(ATC_IS_CYCLIC, &atchan->status))
+ if (atc_chan_is_cyclic(atchan))
return;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (!atc_chan_is_enabled(atchan)) {
atc_advance_work(atchan);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
/**
@@ -1073,6 +1080,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
struct at_dma *atdma = to_at_dma(chan->device);
struct at_desc *desc;
struct at_dma_slave *atslave;
+ unsigned long flags;
int i;
u32 cfg;
LIST_HEAD(tmp_list);
@@ -1116,11 +1124,11 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
list_add_tail(&desc->desc_node, &tmp_list);
}
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated = i;
list_splice(&tmp_list, &atchan->free_list);
atchan->completed_cookie = chan->cookie = 1;
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
/* channel parameters */
channel_writel(atchan, CFG, cfg);
@@ -1260,12 +1268,11 @@ static int __init at_dma_probe(struct platform_device *pdev)
/* initialize channels related values */
INIT_LIST_HEAD(&atdma->dma_common.channels);
- for (i = 0; i < pdata->nr_channels; i++, atdma->dma_common.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct at_dma_chan *atchan = &atdma->chan[i];
atchan->chan_common.device = &atdma->dma_common;
atchan->chan_common.cookie = atchan->completed_cookie = 1;
- atchan->chan_common.chan_id = i;
list_add_tail(&atchan->chan_common.device_node,
&atdma->dma_common.channels);
@@ -1293,22 +1300,20 @@ static int __init at_dma_probe(struct platform_device *pdev)
if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask))
atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy;
- if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask))
+ if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) {
atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg;
-
- if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
+ /* controller can do slave DMA: can trigger cyclic transfers */
+ dma_cap_set(DMA_CYCLIC, atdma->dma_common.cap_mask);
atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic;
-
- if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ||
- dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
atdma->dma_common.device_control = atc_control;
+ }
dma_writel(atdma, EN, AT_DMA_ENABLE);
dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n",
dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "",
dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "",
- atdma->dma_common.chancnt);
+ pdata->nr_channels);
dma_async_device_register(&atdma->dma_common);
@@ -1377,27 +1382,112 @@ static void at_dma_shutdown(struct platform_device *pdev)
clk_disable(atdma->clk);
}
+static int at_dma_prepare(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
+
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+ /* wait for transaction completion (except in cyclic case) */
+ if (atc_chan_is_enabled(atchan) && !atc_chan_is_cyclic(atchan))
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static void atc_suspend_cyclic(struct at_dma_chan *atchan)
+{
+ struct dma_chan *chan = &atchan->chan_common;
+
+ /* Channel should be paused by user
+ * do it anyway even if it is not done already */
+ if (!atc_chan_is_paused(atchan)) {
+ dev_warn(chan2dev(chan),
+ "cyclic channel not paused, should be done by channel user\n");
+ atc_control(chan, DMA_PAUSE, 0);
+ }
+
+ /* now preserve additional data for cyclic operations */
+ /* next descriptor address in the cyclic list */
+ atchan->save_dscr = channel_readl(atchan, DSCR);
+
+ vdbg_dump_regs(atchan);
+}
+
static int at_dma_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
- at_dma_off(platform_get_drvdata(pdev));
+ /* preserve data */
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+
+ if (atc_chan_is_cyclic(atchan))
+ atc_suspend_cyclic(atchan);
+ atchan->save_cfg = channel_readl(atchan, CFG);
+ }
+ atdma->save_imr = dma_readl(atdma, EBCIMR);
+
+ /* disable DMA controller */
+ at_dma_off(atdma);
clk_disable(atdma->clk);
return 0;
}
+static void atc_resume_cyclic(struct at_dma_chan *atchan)
+{
+ struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
+
+ /* restore channel status for cyclic descriptors list:
+ * next descriptor in the cyclic list at the time of suspend */
+ channel_writel(atchan, SADDR, 0);
+ channel_writel(atchan, DADDR, 0);
+ channel_writel(atchan, CTRLA, 0);
+ channel_writel(atchan, CTRLB, 0);
+ channel_writel(atchan, DSCR, atchan->save_dscr);
+ dma_writel(atdma, CHER, atchan->mask);
+
+ /* channel pause status should be removed by channel user
+ * We cannot take the initiative to do it here */
+
+ vdbg_dump_regs(atchan);
+}
+
static int at_dma_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
+ struct dma_chan *chan, *_chan;
+ /* bring back DMA controller */
clk_enable(atdma->clk);
dma_writel(atdma, EN, AT_DMA_ENABLE);
+
+ /* clear any pending interrupt */
+ while (dma_readl(atdma, EBCISR))
+ cpu_relax();
+
+ /* restore saved data */
+ dma_writel(atdma, EBCIER, atdma->save_imr);
+ list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
+ device_node) {
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+
+ channel_writel(atchan, CFG, atchan->save_cfg);
+ if (atc_chan_is_cyclic(atchan))
+ atc_resume_cyclic(atchan);
+ }
return 0;
}
static const struct dev_pm_ops at_dma_dev_pm_ops = {
+ .prepare = at_dma_prepare,
.suspend_noirq = at_dma_suspend_noirq,
.resume_noirq = at_dma_resume_noirq,
};
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
index 087dbf1dd39c..aa4c9aebab7c 100644
--- a/drivers/dma/at_hdmac_regs.h
+++ b/drivers/dma/at_hdmac_regs.h
@@ -204,6 +204,9 @@ enum atc_status {
* @status: transmit status information from irq/prep* functions
* to tasklet (use atomic operations)
* @tasklet: bottom half to finish transaction work
+ * @save_cfg: configuration register that is saved on suspend/resume cycle
+ * @save_dscr: for cyclic operations, preserve next descriptor address in
+ * the cyclic list on suspend/resume cycle
* @lock: serializes enqueue/dequeue operations to descriptors lists
* @completed_cookie: identifier for the most recently completed operation
* @active_list: list of descriptors dmaengine is being running on
@@ -218,6 +221,8 @@ struct at_dma_chan {
u8 mask;
unsigned long status;
struct tasklet_struct tasklet;
+ u32 save_cfg;
+ u32 save_dscr;
spinlock_t lock;
@@ -248,6 +253,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan)
* @chan_common: common dmaengine dma_device object members
* @ch_regs: memory mapped register base
* @clk: dma controller clock
+ * @save_imr: interrupt mask register that is saved on suspend/resume cycle
* @all_chan_mask: all channels availlable in a mask
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @chan: channels table to store at_dma_chan structures
@@ -256,6 +262,7 @@ struct at_dma {
struct dma_device dma_common;
void __iomem *regs;
struct clk *clk;
+ u32 save_imr;
u8 all_chan_mask;
@@ -355,6 +362,23 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan)
return !!(dma_readl(atdma, CHSR) & atchan->mask);
}
+/**
+ * atc_chan_is_paused - test channel pause/resume status
+ * @atchan: channel we want to test status
+ */
+static inline int atc_chan_is_paused(struct at_dma_chan *atchan)
+{
+ return test_bit(ATC_IS_PAUSED, &atchan->status);
+}
+
+/**
+ * atc_chan_is_cyclic - test if given channel has cyclic property set
+ * @atchan: channel we want to test status
+ */
+static inline int atc_chan_is_cyclic(struct at_dma_chan *atchan)
+{
+ return test_bit(ATC_IS_CYCLIC, &atchan->status);
+}
/**
* set_desc_eol - set end-of-link to descriptor so it will end transfer
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 765f5ff22304..eb1d8641cf5c 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/freezer.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
@@ -251,6 +252,7 @@ static int dmatest_func(void *data)
int i;
thread_name = current->comm;
+ set_freezable_with_signal();
ret = -ENOMEM;
@@ -305,7 +307,8 @@ static int dmatest_func(void *data)
dma_addr_t dma_srcs[src_cnt];
dma_addr_t dma_dsts[dst_cnt];
struct completion cmp;
- unsigned long tmo = msecs_to_jiffies(timeout);
+ unsigned long start, tmo, end = 0 /* compiler... */;
+ bool reload = true;
u8 align = 0;
total_tests++;
@@ -404,7 +407,17 @@ static int dmatest_func(void *data)
}
dma_async_issue_pending(chan);
- tmo = wait_for_completion_timeout(&cmp, tmo);
+ do {
+ start = jiffies;
+ if (reload)
+ end = start + msecs_to_jiffies(timeout);
+ else if (end <= start)
+ end = start + 1;
+ tmo = wait_for_completion_interruptible_timeout(&cmp,
+ end - start);
+ reload = try_to_freeze();
+ } while (tmo == -ERESTARTSYS);
+
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if (tmo == 0) {
@@ -477,6 +490,8 @@ err_srcs:
pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
thread_name, total_tests, failed_tests, ret);
+ /* terminate all transfers on specified channels */
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
if (iterations > 0)
while (!kthread_should_stop()) {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit);
@@ -499,6 +514,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
list_del(&thread->node);
kfree(thread);
}
+
+ /* terminate all transfers on specified channels */
+ dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0);
+
kfree(dtc);
}
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c
index 4d180ca9a1d8..9bfd6d360718 100644
--- a/drivers/dma/dw_dmac.c
+++ b/drivers/dma/dw_dmac.c
@@ -1407,12 +1407,11 @@ static int __init dw_probe(struct platform_device *pdev)
dw->all_chan_mask = (1 << pdata->nr_channels) - 1;
INIT_LIST_HEAD(&dw->dma.channels);
- for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct dw_dma_chan *dwc = &dw->chan[i];
dwc->chan.device = &dw->dma;
dwc->chan.cookie = dwc->completed = 1;
- dwc->chan.chan_id = i;
if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING)
list_add_tail(&dwc->chan.device_node,
&dw->dma.channels);
@@ -1468,7 +1467,7 @@ static int __init dw_probe(struct platform_device *pdev)
dma_writel(dw, CFG, DW_CFG_DMA_EN);
printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n",
- dev_name(&pdev->dev), dw->dma.chancnt);
+ dev_name(&pdev->dev), pdata->nr_channels);
dma_async_device_register(&dw->dma);
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 5d7a49bd7c26..b47e2b803faf 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dmaengine.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index d99f71c356b5..d746899f36e1 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -14,6 +14,7 @@
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 7bd7e98548cd..eab1fe71259e 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -18,6 +18,7 @@
*/
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
@@ -318,6 +319,7 @@ struct sdma_engine {
dma_addr_t context_phys;
struct dma_device dma_device;
struct clk *clk;
+ struct mutex channel_0_lock;
struct sdma_script_start_addrs *script_addrs;
};
@@ -415,11 +417,15 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_addr_t buf_phys;
int ret;
+ mutex_lock(&sdma->channel_0_lock);
+
buf_virt = dma_alloc_coherent(NULL,
size,
&buf_phys, GFP_KERNEL);
- if (!buf_virt)
- return -ENOMEM;
+ if (!buf_virt) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
bd0->mode.command = C0_SETPM;
bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
@@ -433,6 +439,9 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_free_coherent(NULL, size, buf_virt, buf_phys);
+err_out:
+ mutex_unlock(&sdma->channel_0_lock);
+
return ret;
}
@@ -656,6 +665,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0);
dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1);
+ mutex_lock(&sdma->channel_0_lock);
+
memset(context, 0, sizeof(*context));
context->channel_state.pc = load_address;
@@ -676,6 +687,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
ret = sdma_run_channel(&sdma->channel[0]);
+ mutex_unlock(&sdma->channel_0_lock);
+
return ret;
}
@@ -1131,18 +1144,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
saddr_arr[i] = addr_arr[i];
}
-static int __init sdma_get_firmware(struct sdma_engine *sdma,
- const char *fw_name)
+static void sdma_load_firmware(const struct firmware *fw, void *context)
{
- const struct firmware *fw;
+ struct sdma_engine *sdma = context;
const struct sdma_firmware_header *header;
- int ret;
const struct sdma_script_start_addrs *addr;
unsigned short *ram_code;
- ret = request_firmware(&fw, fw_name, sdma->dev);
- if (ret)
- return ret;
+ if (!fw) {
+ dev_err(sdma->dev, "firmware not found\n");
+ return;
+ }
if (fw->size < sizeof(*header))
goto err_firmware;
@@ -1172,6 +1184,16 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma,
err_firmware:
release_firmware(fw);
+}
+
+static int __init sdma_get_firmware(struct sdma_engine *sdma,
+ const char *fw_name)
+{
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG, fw_name, sdma->dev,
+ GFP_KERNEL, sdma, sdma_load_firmware);
return ret;
}
@@ -1269,11 +1291,14 @@ static int __init sdma_probe(struct platform_device *pdev)
struct sdma_platform_data *pdata = pdev->dev.platform_data;
int i;
struct sdma_engine *sdma;
+ s32 *saddr_arr;
sdma = kzalloc(sizeof(*sdma), GFP_KERNEL);
if (!sdma)
return -ENOMEM;
+ mutex_init(&sdma->channel_0_lock);
+
sdma->dev = &pdev->dev;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1310,6 +1335,11 @@ static int __init sdma_probe(struct platform_device *pdev)
goto err_alloc;
}
+ /* initially no scripts available */
+ saddr_arr = (s32 *)sdma->script_addrs;
+ for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++)
+ saddr_arr[i] = -EINVAL;
+
if (of_id)
pdev->id_entry = of_id->data;
sdma->devtype = pdev->id_entry->driver_data;
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 8a3fdd87db97..9e96c43a846a 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -115,16 +115,15 @@ DMAC1 interrupt Functions*/
/**
* dmac1_mask_periphral_intr - mask the periphral interrupt
- * @midc: dma channel for which masking is required
+ * @mid: dma device for which masking is required
*
* Masks the DMA periphral interrupt
* this is valid for DMAC1 family controllers only
* This controller should have periphral mask registers already mapped
*/
-static void dmac1_mask_periphral_intr(struct intel_mid_dma_chan *midc)
+static void dmac1_mask_periphral_intr(struct middma_device *mid)
{
u32 pimr;
- struct middma_device *mid = to_middma_device(midc->chan.device);
if (mid->pimr_mask) {
pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK);
@@ -184,7 +183,6 @@ static void enable_dma_interrupt(struct intel_mid_dma_chan *midc)
static void disable_dma_interrupt(struct intel_mid_dma_chan *midc)
{
/*Check LPE PISR, make sure fwd is disabled*/
- dmac1_mask_periphral_intr(midc);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR);
@@ -1114,7 +1112,6 @@ static int mid_setup_dma(struct pci_dev *pdev)
midch->chan.device = &dma->common;
midch->chan.cookie = 1;
- midch->chan.chan_id = i;
midch->ch_id = dma->chan_base + i;
pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id);
@@ -1150,7 +1147,6 @@ static int mid_setup_dma(struct pci_dev *pdev)
dma_cap_set(DMA_SLAVE, dma->common.cap_mask);
dma_cap_set(DMA_PRIVATE, dma->common.cap_mask);
dma->common.dev = &pdev->dev;
- dma->common.chancnt = dma->max_chan;
dma->common.device_alloc_chan_resources =
intel_mid_dma_alloc_chan_resources;
@@ -1350,6 +1346,7 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state)
if (device->ch[i].in_use)
return -EAGAIN;
}
+ dmac1_mask_periphral_intr(device);
device->state = SUSPENDED;
pci_save_state(pci);
pci_disable_device(pci);
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index 6815905a772f..ddc2a1331822 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -1307,6 +1307,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {
callback = descnew->txd.callback;
callback_param = descnew->txd.callback_param;
+ list_del_init(&descnew->list);
spin_unlock(&ichan->lock);
if (callback)
callback(callback_param);
@@ -1428,39 +1429,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
{
struct idmac_channel *ichan = to_idmac_chan(chan);
struct idmac *idmac = to_idmac(chan->device);
+ struct ipu *ipu = to_ipu(idmac);
+ struct list_head *list, *tmp;
unsigned long flags;
int i;
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
+ switch (cmd) {
+ case DMA_PAUSE:
+ spin_lock_irqsave(&ipu->lock, flags);
+ ipu_ic_disable_task(ipu, chan->chan_id);
- ipu_disable_channel(idmac, ichan,
- ichan->status >= IPU_CHANNEL_ENABLED);
+ /* Return all descriptors into "prepared" state */
+ list_for_each_safe(list, tmp, &ichan->queue)
+ list_del_init(list);
- tasklet_disable(&to_ipu(idmac)->tasklet);
+ ichan->sg[0] = NULL;
+ ichan->sg[1] = NULL;
- /* ichan->queue is modified in ISR, have to spinlock */
- spin_lock_irqsave(&ichan->lock, flags);
- list_splice_init(&ichan->queue, &ichan->free_list);
+ spin_unlock_irqrestore(&ipu->lock, flags);
- if (ichan->desc)
- for (i = 0; i < ichan->n_tx_desc; i++) {
- struct idmac_tx_desc *desc = ichan->desc + i;
- if (list_empty(&desc->list))
- /* Descriptor was prepared, but not submitted */
- list_add(&desc->list, &ichan->free_list);
+ ichan->status = IPU_CHANNEL_INITIALIZED;
+ break;
+ case DMA_TERMINATE_ALL:
+ ipu_disable_channel(idmac, ichan,
+ ichan->status >= IPU_CHANNEL_ENABLED);
- async_tx_clear_ack(&desc->txd);
- }
+ tasklet_disable(&ipu->tasklet);
- ichan->sg[0] = NULL;
- ichan->sg[1] = NULL;
- spin_unlock_irqrestore(&ichan->lock, flags);
+ /* ichan->queue is modified in ISR, have to spinlock */
+ spin_lock_irqsave(&ichan->lock, flags);
+ list_splice_init(&ichan->queue, &ichan->free_list);
- tasklet_enable(&to_ipu(idmac)->tasklet);
+ if (ichan->desc)
+ for (i = 0; i < ichan->n_tx_desc; i++) {
+ struct idmac_tx_desc *desc = ichan->desc + i;
+ if (list_empty(&desc->list))
+ /* Descriptor was prepared, but not submitted */
+ list_add(&desc->list, &ichan->free_list);
- ichan->status = IPU_CHANNEL_INITIALIZED;
+ async_tx_clear_ack(&desc->txd);
+ }
+
+ ichan->sg[0] = NULL;
+ ichan->sg[1] = NULL;
+ spin_unlock_irqrestore(&ichan->lock, flags);
+
+ tasklet_enable(&ipu->tasklet);
+
+ ichan->status = IPU_CHANNEL_INITIALIZED;
+ break;
+ default:
+ return -ENOSYS;
+ }
return 0;
}
@@ -1663,7 +1683,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu)
struct idmac_channel *ichan = ipu->channel + i;
idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0);
- idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0);
}
dma_async_device_unregister(&idmac->dma);
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index b9bae94f2015..8ba4edc6185e 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -741,7 +741,6 @@ static int __devinit mpc_dma_probe(struct platform_device *op)
mchan = &mdma->channels[i];
mchan->chan.device = dma;
- mchan->chan.chan_id = i;
mchan->chan.cookie = 1;
mchan->completed_cookie = mchan->chan.cookie;
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index be641cbd36fc..b4588bdd98bb 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -130,6 +130,23 @@ struct mxs_dma_engine {
struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
};
+static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable)
+{
+ struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+ int chan_id = mxs_chan->chan.chan_id;
+ int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR;
+
+ /* enable apbh channel clock */
+ if (dma_is_apbh()) {
+ if (apbh_is_old())
+ writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
+ mxs_dma->base + HW_APBHX_CTRL0 + set_clr);
+ else
+ writel(1 << chan_id,
+ mxs_dma->base + HW_APBHX_CTRL0 + set_clr);
+ }
+}
+
static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
@@ -148,38 +165,21 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
+ /* clkgate needs to be enabled before writing other registers */
+ mxs_dma_clkgate(mxs_chan, 1);
+
/* set cmd_addr up */
writel(mxs_chan->ccw_phys,
mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id));
- /* enable apbh channel clock */
- if (dma_is_apbh()) {
- if (apbh_is_old())
- writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
- else
- writel(1 << chan_id,
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
- }
-
/* write 1 to SEMA to kick off the channel */
writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id));
}
static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
{
- struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
- int chan_id = mxs_chan->chan.chan_id;
-
/* disable apbh channel clock */
- if (dma_is_apbh()) {
- if (apbh_is_old())
- writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
- else
- writel(1 << chan_id,
- mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
- }
+ mxs_dma_clkgate(mxs_chan, 0);
mxs_chan->status = DMA_SUCCESS;
}
@@ -338,7 +338,10 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
if (ret)
goto err_clk;
+ /* clkgate needs to be enabled for reset to finish */
+ mxs_dma_clkgate(mxs_chan, 1);
mxs_dma_reset_chan(mxs_chan);
+ mxs_dma_clkgate(mxs_chan, 0);
dma_async_tx_descriptor_init(&mxs_chan->desc, chan);
mxs_chan->desc.tx_submit = mxs_dma_tx_submit;
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index 1ac8d4b580b7..a6d0e3dbed07 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -60,7 +60,7 @@
#define DMA_DESC_FOLLOW_WITHOUT_IRQ 0x2
#define DMA_DESC_FOLLOW_WITH_IRQ 0x3
-#define MAX_CHAN_NR 8
+#define MAX_CHAN_NR 12
#define DMA_MASK_CTL0_MODE 0x33333333
#define DMA_MASK_CTL2_MODE 0x00003333
@@ -872,8 +872,7 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
int i;
nr_channels = id->driver_data;
- pd = kzalloc(sizeof(struct pch_dma)+
- sizeof(struct pch_dma_chan) * nr_channels, GFP_KERNEL);
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
@@ -926,7 +925,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
}
pd->dma.dev = &pdev->dev;
- pd->dma.chancnt = nr_channels;
INIT_LIST_HEAD(&pd->dma.channels);
@@ -935,7 +933,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
pd_chan->chan.device = &pd->dma;
pd_chan->chan.cookie = 1;
- pd_chan->chan.chan_id = i;
pd_chan->membase = &regs->desc[i];
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 00eee59e8b33..571041477ab2 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -17,6 +17,8 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
+#include <linux/pm_runtime.h>
+#include <linux/scatterlist.h>
#define NR_DEFAULT_DESC 16
@@ -68,6 +70,14 @@ struct dma_pl330_chan {
* NULL if the channel is available to be acquired.
*/
void *pl330_chid;
+
+ /* For D-to-M and M-to-D channels */
+ int burst_sz; /* the peripheral fifo width */
+ int burst_len; /* the number of burst */
+ dma_addr_t fifo_addr;
+
+ /* for cyclic capability */
+ bool cyclic;
};
struct dma_pl330_dmac {
@@ -83,6 +93,8 @@ struct dma_pl330_dmac {
/* Peripheral channels connected to this DMAC */
struct dma_pl330_chan *peripherals; /* keep at end */
+
+ struct clk *clk;
};
struct dma_pl330_desc {
@@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list)
spin_unlock_irqrestore(&pdmac->pool_lock, flags);
}
+static inline void handle_cyclic_desc_list(struct list_head *list)
+{
+ struct dma_pl330_desc *desc;
+ struct dma_pl330_chan *pch;
+ unsigned long flags;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry(desc, list, node) {
+ dma_async_tx_callback callback;
+
+ /* Change status to reload it */
+ desc->status = PREP;
+ pch = desc->pchan;
+ callback = desc->txd.callback;
+ if (callback)
+ callback(desc->txd.callback_param);
+ }
+
+ spin_lock_irqsave(&pch->lock, flags);
+ list_splice_tail_init(list, &pch->work_list);
+ spin_unlock_irqrestore(&pch->lock, flags);
+}
+
static inline void fill_queue(struct dma_pl330_chan *pch)
{
struct dma_pl330_desc *desc;
@@ -205,7 +242,10 @@ static void pl330_tasklet(unsigned long data)
spin_unlock_irqrestore(&pch->lock, flags);
- free_desc_list(&list);
+ if (pch->cyclic)
+ handle_cyclic_desc_list(&list);
+ else
+ free_desc_list(&list);
}
static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
@@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&pch->lock, flags);
pch->completed = chan->cookie = 1;
+ pch->cyclic = false;
pch->pl330_chid = pl330_request_channel(&pdmac->pif);
if (!pch->pl330_chid) {
@@ -253,25 +294,52 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
{
struct dma_pl330_chan *pch = to_pchan(chan);
- struct dma_pl330_desc *desc;
+ struct dma_pl330_desc *desc, *_dt;
unsigned long flags;
+ struct dma_pl330_dmac *pdmac = pch->dmac;
+ struct dma_slave_config *slave_config;
+ LIST_HEAD(list);
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
-
- spin_lock_irqsave(&pch->lock, flags);
-
- /* FLUSH the PL330 Channel thread */
- pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ spin_lock_irqsave(&pch->lock, flags);
- /* Mark all desc done */
- list_for_each_entry(desc, &pch->work_list, node)
- desc->status = DONE;
+ /* FLUSH the PL330 Channel thread */
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
- spin_unlock_irqrestore(&pch->lock, flags);
+ /* Mark all desc done */
+ list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
+ desc->status = DONE;
+ pch->completed = desc->txd.cookie;
+ list_move_tail(&desc->node, &list);
+ }
- pl330_tasklet((unsigned long) pch);
+ list_splice_tail_init(&list, &pdmac->desc_pool);
+ spin_unlock_irqrestore(&pch->lock, flags);
+ break;
+ case DMA_SLAVE_CONFIG:
+ slave_config = (struct dma_slave_config *)arg;
+
+ if (slave_config->direction == DMA_TO_DEVICE) {
+ if (slave_config->dst_addr)
+ pch->fifo_addr = slave_config->dst_addr;
+ if (slave_config->dst_addr_width)
+ pch->burst_sz = __ffs(slave_config->dst_addr_width);
+ if (slave_config->dst_maxburst)
+ pch->burst_len = slave_config->dst_maxburst;
+ } else if (slave_config->direction == DMA_FROM_DEVICE) {
+ if (slave_config->src_addr)
+ pch->fifo_addr = slave_config->src_addr;
+ if (slave_config->src_addr_width)
+ pch->burst_sz = __ffs(slave_config->src_addr_width);
+ if (slave_config->src_maxburst)
+ pch->burst_len = slave_config->src_maxburst;
+ }
+ break;
+ default:
+ dev_err(pch->dmac->pif.dev, "Not supported command.\n");
+ return -ENXIO;
+ }
return 0;
}
@@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
pl330_release_channel(pch->pl330_chid);
pch->pl330_chid = NULL;
+ if (pch->cyclic)
+ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
+
spin_unlock_irqrestore(&pch->lock, flags);
}
@@ -453,7 +524,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
if (peri) {
desc->req.rqtype = peri->rqtype;
- desc->req.peri = peri->peri_id;
+ desc->req.peri = pch->chan.chan_id;
} else {
desc->req.rqtype = MEMTOMEM;
desc->req.peri = 0;
@@ -524,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
return burst_len;
}
+static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
+ size_t period_len, enum dma_data_direction direction)
+{
+ struct dma_pl330_desc *desc;
+ struct dma_pl330_chan *pch = to_pchan(chan);
+ dma_addr_t dst;
+ dma_addr_t src;
+
+ desc = pl330_get_desc(pch);
+ if (!desc) {
+ dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ desc->rqcfg.src_inc = 1;
+ desc->rqcfg.dst_inc = 0;
+ src = dma_addr;
+ dst = pch->fifo_addr;
+ break;
+ case DMA_FROM_DEVICE:
+ desc->rqcfg.src_inc = 0;
+ desc->rqcfg.dst_inc = 1;
+ src = pch->fifo_addr;
+ dst = dma_addr;
+ break;
+ default:
+ dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ desc->rqcfg.brst_size = pch->burst_sz;
+ desc->rqcfg.brst_len = 1;
+
+ pch->cyclic = true;
+
+ fill_px(&desc->px, dst, src, period_len);
+
+ return &desc->txd;
+}
+
static struct dma_async_tx_descriptor *
pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
dma_addr_t src, size_t len, unsigned long flags)
@@ -579,7 +695,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dma_pl330_peri *peri = chan->private;
struct scatterlist *sg;
unsigned long flags;
- int i, burst_size;
+ int i;
dma_addr_t addr;
if (unlikely(!pch || !sgl || !sg_len || !peri))
@@ -595,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL;
}
- addr = peri->fifo_addr;
- burst_size = peri->burst_sz;
+ addr = pch->fifo_addr;
first = NULL;
@@ -644,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
sg_dma_address(sg), addr, sg_dma_len(sg));
}
- desc->rqcfg.brst_size = burst_size;
+ desc->rqcfg.brst_size = pch->burst_sz;
desc->rqcfg.brst_len = 1;
}
@@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err1;
}
+ pdmac->clk = clk_get(&adev->dev, "dma");
+ if (IS_ERR(pdmac->clk)) {
+ dev_err(&adev->dev, "Cannot get operation clock.\n");
+ ret = -EINVAL;
+ goto probe_err1;
+ }
+
+ amba_set_drvdata(adev, pdmac);
+
+#ifdef CONFIG_PM_RUNTIME
+ /* to use the runtime PM helper functions */
+ pm_runtime_enable(&adev->dev);
+
+ /* enable the power domain */
+ if (pm_runtime_get_sync(&adev->dev)) {
+ dev_err(&adev->dev, "failed to get runtime pm\n");
+ ret = -ENODEV;
+ goto probe_err1;
+ }
+#else
+ /* enable dma clk */
+ clk_enable(pdmac->clk);
+#endif
+
irq = adev->irq[0];
ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi);
@@ -732,6 +871,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
case MEMTODEV:
case DEVTOMEM:
dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, pd->cap_mask);
break;
default:
dev_err(&adev->dev, "DEVTODEV Not Supported\n");
@@ -747,11 +887,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&pch->lock);
pch->pl330_chid = NULL;
pch->chan.device = pd;
- pch->chan.chan_id = i;
pch->dmac = pdmac;
/* Add the channel to the DMAC list */
- pd->chancnt++;
list_add_tail(&pch->chan.device_node, &pd->channels);
}
@@ -760,6 +898,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+ pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control;
@@ -771,8 +910,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err4;
}
- amba_set_drvdata(adev, pdmac);
-
dev_info(&adev->dev,
"Loaded driver for PL330 DMAC-%d\n", adev->periphid);
dev_info(&adev->dev,
@@ -833,6 +970,13 @@ static int __devexit pl330_remove(struct amba_device *adev)
res = &adev->res;
release_mem_region(res->start, resource_size(res));
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_put(&adev->dev);
+ pm_runtime_disable(&adev->dev);
+#else
+ clk_disable(pdmac->clk);
+#endif
+
kfree(pdmac);
return 0;
@@ -846,10 +990,49 @@ static struct amba_id pl330_ids[] = {
{ 0, 0 },
};
+#ifdef CONFIG_PM_RUNTIME
+static int pl330_runtime_suspend(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_disable(pdmac->clk);
+
+ return 0;
+}
+
+static int pl330_runtime_resume(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_enable(pdmac->clk);
+
+ return 0;
+}
+#else
+#define pl330_runtime_suspend NULL
+#define pl330_runtime_resume NULL
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops pl330_pm_ops = {
+ .runtime_suspend = pl330_runtime_suspend,
+ .runtime_resume = pl330_runtime_resume,
+};
+
static struct amba_driver pl330_driver = {
.drv = {
.owner = THIS_MODULE,
.name = "dma-pl330",
+ .pm = &pl330_pm_ops,
},
.id_table = pl330_ids,
.probe = pl330_probe,
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 7f49235d14b9..81809c2b46ab 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -259,14 +259,23 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
return 0;
}
+static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan);
+
static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c;
struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan);
+ struct sh_dmae_slave *param = tx->chan->private;
dma_async_tx_callback callback = tx->callback;
dma_cookie_t cookie;
+ bool power_up;
+
+ spin_lock_irq(&sh_chan->desc_lock);
- spin_lock_bh(&sh_chan->desc_lock);
+ if (list_empty(&sh_chan->ld_queue))
+ power_up = true;
+ else
+ power_up = false;
cookie = sh_chan->common.cookie;
cookie++;
@@ -302,7 +311,38 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
tx->cookie, &last->async_tx, sh_chan->id,
desc->hw.sar, desc->hw.tcr, desc->hw.dar);
- spin_unlock_bh(&sh_chan->desc_lock);
+ if (power_up) {
+ sh_chan->pm_state = DMAE_PM_BUSY;
+
+ pm_runtime_get(sh_chan->dev);
+
+ spin_unlock_irq(&sh_chan->desc_lock);
+
+ pm_runtime_barrier(sh_chan->dev);
+
+ spin_lock_irq(&sh_chan->desc_lock);
+
+ /* Have we been reset, while waiting? */
+ if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) {
+ dev_dbg(sh_chan->dev, "Bring up channel %d\n",
+ sh_chan->id);
+ if (param) {
+ const struct sh_dmae_slave_config *cfg =
+ param->config;
+
+ dmae_set_dmars(sh_chan, cfg->mid_rid);
+ dmae_set_chcr(sh_chan, cfg->chcr);
+ } else {
+ dmae_init(sh_chan);
+ }
+
+ if (sh_chan->pm_state == DMAE_PM_PENDING)
+ sh_chan_xfer_ld_queue(sh_chan);
+ sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+ }
+ }
+
+ spin_unlock_irq(&sh_chan->desc_lock);
return cookie;
}
@@ -346,8 +386,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
struct sh_dmae_slave *param = chan->private;
int ret;
- pm_runtime_get_sync(sh_chan->dev);
-
/*
* This relies on the guarantee from dmaengine that alloc_chan_resources
* never runs concurrently with itself or free_chan_resources.
@@ -367,31 +405,20 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
}
param->config = cfg;
-
- dmae_set_dmars(sh_chan, cfg->mid_rid);
- dmae_set_chcr(sh_chan, cfg->chcr);
- } else {
- dmae_init(sh_chan);
}
- spin_lock_bh(&sh_chan->desc_lock);
while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) {
- spin_unlock_bh(&sh_chan->desc_lock);
desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL);
- if (!desc) {
- spin_lock_bh(&sh_chan->desc_lock);
+ if (!desc)
break;
- }
dma_async_tx_descriptor_init(&desc->async_tx,
&sh_chan->common);
desc->async_tx.tx_submit = sh_dmae_tx_submit;
desc->mark = DESC_IDLE;
- spin_lock_bh(&sh_chan->desc_lock);
list_add(&desc->node, &sh_chan->ld_free);
sh_chan->descs_allocated++;
}
- spin_unlock_bh(&sh_chan->desc_lock);
if (!sh_chan->descs_allocated) {
ret = -ENOMEM;
@@ -405,7 +432,7 @@ edescalloc:
clear_bit(param->slave_id, sh_dmae_slave_used);
etestused:
efindslave:
- pm_runtime_put(sh_chan->dev);
+ chan->private = NULL;
return ret;
}
@@ -417,7 +444,6 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
struct sh_desc *desc, *_desc;
LIST_HEAD(list);
- int descs = sh_chan->descs_allocated;
/* Protect against ISR */
spin_lock_irq(&sh_chan->desc_lock);
@@ -437,15 +463,12 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
chan->private = NULL;
}
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irq(&sh_chan->desc_lock);
list_splice_init(&sh_chan->ld_free, &list);
sh_chan->descs_allocated = 0;
- spin_unlock_bh(&sh_chan->desc_lock);
-
- if (descs > 0)
- pm_runtime_put(sh_chan->dev);
+ spin_unlock_irq(&sh_chan->desc_lock);
list_for_each_entry_safe(desc, _desc, &list, node)
kfree(desc);
@@ -534,6 +557,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
struct sh_desc *first = NULL, *new = NULL /* compiler... */;
LIST_HEAD(tx_list);
int chunks = 0;
+ unsigned long irq_flags;
int i;
if (!sg_len)
@@ -544,7 +568,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
(SH_DMA_TCR_MAX + 1);
/* Have to lock the whole loop to protect against concurrent release */
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, irq_flags);
/*
* Chaining:
@@ -590,7 +614,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c
/* Put them back on the free list, so, they don't get lost */
list_splice_tail(&tx_list, &sh_chan->ld_free);
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
return &first->async_tx;
@@ -599,7 +623,7 @@ err_get_desc:
new->mark = DESC_IDLE;
list_splice(&tx_list, &sh_chan->ld_free);
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
return NULL;
}
@@ -661,6 +685,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
+ unsigned long flags;
/* Only supports DMA_TERMINATE_ALL */
if (cmd != DMA_TERMINATE_ALL)
@@ -669,7 +694,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
if (!chan)
return -EINVAL;
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
dmae_halt(sh_chan);
if (!list_empty(&sh_chan->ld_queue)) {
@@ -678,9 +703,8 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct sh_desc, node);
desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
sh_chan->xmit_shift;
-
}
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
sh_dmae_chan_ld_cleanup(sh_chan, true);
@@ -695,8 +719,9 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
dma_cookie_t cookie = 0;
dma_async_tx_callback callback = NULL;
void *param = NULL;
+ unsigned long flags;
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) {
struct dma_async_tx_descriptor *tx = &desc->async_tx;
@@ -762,7 +787,13 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
async_tx_test_ack(&desc->async_tx)) || all) {
/* Remove from ld_queue list */
desc->mark = DESC_IDLE;
+
list_move(&desc->node, &sh_chan->ld_free);
+
+ if (list_empty(&sh_chan->ld_queue)) {
+ dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
+ pm_runtime_put(sh_chan->dev);
+ }
}
}
@@ -773,7 +804,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
*/
sh_chan->completed_cookie = sh_chan->common.cookie;
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
if (callback)
callback(param);
@@ -792,14 +823,14 @@ static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
;
}
+/* Called under spin_lock_irq(&sh_chan->desc_lock) */
static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
{
struct sh_desc *desc;
- spin_lock_bh(&sh_chan->desc_lock);
/* DMA work check */
if (dmae_is_busy(sh_chan))
- goto sh_chan_xfer_ld_queue_end;
+ return;
/* Find the first not transferred descriptor */
list_for_each_entry(desc, &sh_chan->ld_queue, node)
@@ -812,15 +843,18 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
dmae_start(sh_chan);
break;
}
-
-sh_chan_xfer_ld_queue_end:
- spin_unlock_bh(&sh_chan->desc_lock);
}
static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- sh_chan_xfer_ld_queue(sh_chan);
+
+ spin_lock_irq(&sh_chan->desc_lock);
+ if (sh_chan->pm_state == DMAE_PM_ESTABLISHED)
+ sh_chan_xfer_ld_queue(sh_chan);
+ else
+ sh_chan->pm_state = DMAE_PM_PENDING;
+ spin_unlock_irq(&sh_chan->desc_lock);
}
static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
@@ -831,6 +865,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
dma_cookie_t last_used;
dma_cookie_t last_complete;
enum dma_status status;
+ unsigned long flags;
sh_dmae_chan_ld_cleanup(sh_chan, false);
@@ -841,7 +876,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
BUG_ON(last_complete < 0);
dma_set_tx_state(txstate, last_complete, last_used, 0);
- spin_lock_bh(&sh_chan->desc_lock);
+ spin_lock_irqsave(&sh_chan->desc_lock, flags);
status = dma_async_is_complete(cookie, last_complete, last_used);
@@ -859,7 +894,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
}
}
- spin_unlock_bh(&sh_chan->desc_lock);
+ spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
return status;
}
@@ -912,6 +947,12 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev)
list_splice_init(&sh_chan->ld_queue, &dl);
+ if (!list_empty(&dl)) {
+ dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
+ pm_runtime_put(sh_chan->dev);
+ }
+ sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+
spin_unlock(&sh_chan->desc_lock);
/* Complete all */
@@ -952,7 +993,7 @@ static void dmae_do_tasklet(unsigned long data)
u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
- spin_lock(&sh_chan->desc_lock);
+ spin_lock_irq(&sh_chan->desc_lock);
list_for_each_entry(desc, &sh_chan->ld_queue, node) {
if (desc->mark == DESC_SUBMITTED &&
((desc->direction == DMA_FROM_DEVICE &&
@@ -965,10 +1006,10 @@ static void dmae_do_tasklet(unsigned long data)
break;
}
}
- spin_unlock(&sh_chan->desc_lock);
-
/* Next desc */
sh_chan_xfer_ld_queue(sh_chan);
+ spin_unlock_irq(&sh_chan->desc_lock);
+
sh_dmae_chan_ld_cleanup(sh_chan, false);
}
@@ -1036,7 +1077,9 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
return -ENOMEM;
}
- /* copy struct dma_device */
+ new_sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+
+ /* reference struct dma_device */
new_sh_chan->common.device = &shdev->common;
new_sh_chan->dev = shdev->common.dev;
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index dc56576f9fdb..2b55a276dc5b 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -23,6 +23,12 @@
struct device;
+enum dmae_pm_state {
+ DMAE_PM_ESTABLISHED,
+ DMAE_PM_BUSY,
+ DMAE_PM_PENDING,
+};
+
struct sh_dmae_chan {
dma_cookie_t completed_cookie; /* The maximum cookie completed */
spinlock_t desc_lock; /* Descriptor operation lock */
@@ -38,6 +44,7 @@ struct sh_dmae_chan {
u32 __iomem *base;
char dev_id[16]; /* unique name per DMAC of channel */
int pm_error;
+ enum dmae_pm_state pm_state;
};
struct sh_dmae_device {
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index f69f90a61873..a4a398f2ef61 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -753,7 +753,7 @@ static int __devinit td_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&td->dma.channels);
- for (i = 0; i < pdata->nr_channels; i++, td->dma.chancnt++) {
+ for (i = 0; i < pdata->nr_channels; i++) {
struct timb_dma_chan *td_chan = &td->channels[i];
struct timb_dma_platform_data_channel *pchan =
pdata->channels + i;
@@ -762,12 +762,11 @@ static int __devinit td_probe(struct platform_device *pdev)
if ((i % 2) == pchan->rx) {
dev_err(&pdev->dev, "Wrong channel configuration\n");
err = -EINVAL;
- goto err_tasklet_kill;
+ goto err_free_irq;
}
td_chan->chan.device = &td->dma;
td_chan->chan.cookie = 1;
- td_chan->chan.chan_id = i;
spin_lock_init(&td_chan->lock);
INIT_LIST_HEAD(&td_chan->active_list);
INIT_LIST_HEAD(&td_chan->queue);
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index af1a17d42bd7..5948a2194f50 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -41,7 +41,7 @@ config EDAC_DEBUG
config EDAC_DECODE_MCE
tristate "Decode MCEs in human-readable form (only on AMD for now)"
- depends on CPU_SUP_AMD && X86_MCE
+ depends on CPU_SUP_AMD && X86_MCE_AMD
default y
---help---
Enable this option if you want to decode Machine Check Exceptions
@@ -71,9 +71,6 @@ config EDAC_MM_EDAC
occurred so that a particular failing memory module can be
replaced. If unsure, select 'Y'.
-config EDAC_MCE
- bool
-
config EDAC_AMD64
tristate "AMD64 (Opteron, Athlon64) K8, F10h"
depends on EDAC_MM_EDAC && AMD_NB && X86_64 && EDAC_DECODE_MCE
@@ -173,8 +170,7 @@ config EDAC_I5400
config EDAC_I7CORE
tristate "Intel i7 Core (Nehalem) processors"
- depends on EDAC_MM_EDAC && PCI && X86
- select EDAC_MCE
+ depends on EDAC_MM_EDAC && PCI && X86 && X86_MCE_INTEL
help
Support for error detection and correction the Intel
i7 Core (Nehalem) Integrated Memory Controller that exists on
@@ -216,6 +212,14 @@ config EDAC_I7300
Support for error detection and correction the Intel
Clarksboro MCH (Intel 7300 chipset).
+config EDAC_SBRIDGE
+ tristate "Intel Sandy-Bridge Integrated MC"
+ depends on EDAC_MM_EDAC && PCI && X86_64 && X86_MCE_INTEL
+ depends on EXPERIMENTAL
+ help
+ Support for error detection and correction the Intel
+ Sandy Bridge Integrated Memory Controller.
+
config EDAC_MPC85XX
tristate "Freescale MPC83xx / MPC85xx"
depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || PPC_85xx)
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 3e239133e29e..196a63dd37c5 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -8,7 +8,6 @@
obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
-obj-$(CONFIG_EDAC_MCE) += edac_mce.o
edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o
edac_core-y += edac_module.o edac_device_sysfs.o
@@ -29,6 +28,7 @@ obj-$(CONFIG_EDAC_I5100) += i5100_edac.o
obj-$(CONFIG_EDAC_I5400) += i5400_edac.o
obj-$(CONFIG_EDAC_I7300) += i7300_edac.o
obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o
+obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o
obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 9a8bebcf6b17..c9eee6d33e9a 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -114,10 +114,22 @@ static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
}
+/*
+ * Select DCT to which PCI cfg accesses are routed
+ */
+static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
+{
+ u32 reg = 0;
+
+ amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
+ reg &= 0xfffffffe;
+ reg |= dct;
+ amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
+}
+
static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
const char *func)
{
- u32 reg = 0;
u8 dct = 0;
if (addr >= 0x140 && addr <= 0x1a0) {
@@ -125,10 +137,7 @@ static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
addr -= 0x100;
}
- amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
- reg &= 0xfffffffe;
- reg |= dct;
- amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
+ f15h_select_dct(pvt, dct);
return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
}
@@ -198,6 +207,10 @@ static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
if (boot_cpu_data.x86 == 0xf)
min_scrubrate = 0x0;
+ /* F15h Erratum #505 */
+ if (boot_cpu_data.x86 == 0x15)
+ f15h_select_dct(pvt, 0);
+
return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
}
@@ -207,6 +220,10 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
u32 scrubval = 0;
int i, retval = -EINVAL;
+ /* F15h Erratum #505 */
+ if (boot_cpu_data.x86 == 0x15)
+ f15h_select_dct(pvt, 0);
+
amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
scrubval = scrubval & 0x001F;
@@ -751,10 +768,10 @@ static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
* Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
* are ECC capable.
*/
-static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
+static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
{
u8 bit;
- enum dev_type edac_cap = EDAC_FLAG_NONE;
+ unsigned long edac_cap = EDAC_FLAG_NONE;
bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
? 19
@@ -1953,11 +1970,9 @@ static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
amd64_handle_ue(mci, m);
}
-void amd64_decode_bus_error(int node_id, struct mce *m, u32 nbcfg)
+void amd64_decode_bus_error(int node_id, struct mce *m)
{
- struct mem_ctl_info *mci = mcis[node_id];
-
- __amd64_decode_bus_error(mci, m);
+ __amd64_decode_bus_error(mcis[node_id], m);
}
/*
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 55b8278bb172..fe90cd4a7ebc 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -34,11 +34,10 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/workqueue.h>
+#include <linux/edac.h>
-#define EDAC_MC_LABEL_LEN 31
#define EDAC_DEVICE_NAME_LEN 31
#define EDAC_ATTRIB_VALUE_LEN 15
-#define MC_PROC_NAME_MAX_LEN 7
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT))
@@ -101,353 +100,6 @@ extern int edac_debug_level;
#define edac_dev_name(dev) (dev)->dev_name
-/* memory devices */
-enum dev_type {
- DEV_UNKNOWN = 0,
- DEV_X1,
- DEV_X2,
- DEV_X4,
- DEV_X8,
- DEV_X16,
- DEV_X32, /* Do these parts exist? */
- DEV_X64 /* Do these parts exist? */
-};
-
-#define DEV_FLAG_UNKNOWN BIT(DEV_UNKNOWN)
-#define DEV_FLAG_X1 BIT(DEV_X1)
-#define DEV_FLAG_X2 BIT(DEV_X2)
-#define DEV_FLAG_X4 BIT(DEV_X4)
-#define DEV_FLAG_X8 BIT(DEV_X8)
-#define DEV_FLAG_X16 BIT(DEV_X16)
-#define DEV_FLAG_X32 BIT(DEV_X32)
-#define DEV_FLAG_X64 BIT(DEV_X64)
-
-/* memory types */
-enum mem_type {
- MEM_EMPTY = 0, /* Empty csrow */
- MEM_RESERVED, /* Reserved csrow type */
- MEM_UNKNOWN, /* Unknown csrow type */
- MEM_FPM, /* Fast page mode */
- MEM_EDO, /* Extended data out */
- MEM_BEDO, /* Burst Extended data out */
- MEM_SDR, /* Single data rate SDRAM */
- MEM_RDR, /* Registered single data rate SDRAM */
- MEM_DDR, /* Double data rate SDRAM */
- MEM_RDDR, /* Registered Double data rate SDRAM */
- MEM_RMBS, /* Rambus DRAM */
- MEM_DDR2, /* DDR2 RAM */
- MEM_FB_DDR2, /* fully buffered DDR2 */
- MEM_RDDR2, /* Registered DDR2 RAM */
- MEM_XDR, /* Rambus XDR */
- MEM_DDR3, /* DDR3 RAM */
- MEM_RDDR3, /* Registered DDR3 RAM */
-};
-
-#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
-#define MEM_FLAG_RESERVED BIT(MEM_RESERVED)
-#define MEM_FLAG_UNKNOWN BIT(MEM_UNKNOWN)
-#define MEM_FLAG_FPM BIT(MEM_FPM)
-#define MEM_FLAG_EDO BIT(MEM_EDO)
-#define MEM_FLAG_BEDO BIT(MEM_BEDO)
-#define MEM_FLAG_SDR BIT(MEM_SDR)
-#define MEM_FLAG_RDR BIT(MEM_RDR)
-#define MEM_FLAG_DDR BIT(MEM_DDR)
-#define MEM_FLAG_RDDR BIT(MEM_RDDR)
-#define MEM_FLAG_RMBS BIT(MEM_RMBS)
-#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
-#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
-#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
-#define MEM_FLAG_XDR BIT(MEM_XDR)
-#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
-#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
-
-/* chipset Error Detection and Correction capabilities and mode */
-enum edac_type {
- EDAC_UNKNOWN = 0, /* Unknown if ECC is available */
- EDAC_NONE, /* Doesn't support ECC */
- EDAC_RESERVED, /* Reserved ECC type */
- EDAC_PARITY, /* Detects parity errors */
- EDAC_EC, /* Error Checking - no correction */
- EDAC_SECDED, /* Single bit error correction, Double detection */
- EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */
- EDAC_S4ECD4ED, /* Chipkill x4 devices */
- EDAC_S8ECD8ED, /* Chipkill x8 devices */
- EDAC_S16ECD16ED, /* Chipkill x16 devices */
-};
-
-#define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN)
-#define EDAC_FLAG_NONE BIT(EDAC_NONE)
-#define EDAC_FLAG_PARITY BIT(EDAC_PARITY)
-#define EDAC_FLAG_EC BIT(EDAC_EC)
-#define EDAC_FLAG_SECDED BIT(EDAC_SECDED)
-#define EDAC_FLAG_S2ECD2ED BIT(EDAC_S2ECD2ED)
-#define EDAC_FLAG_S4ECD4ED BIT(EDAC_S4ECD4ED)
-#define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED)
-#define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED)
-
-/* scrubbing capabilities */
-enum scrub_type {
- SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */
- SCRUB_NONE, /* No scrubber */
- SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */
- SCRUB_SW_SRC, /* Software scrub only errors */
- SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */
- SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */
- SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */
- SCRUB_HW_SRC, /* Hardware scrub only errors */
- SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */
- SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */
-};
-
-#define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG)
-#define SCRUB_FLAG_SW_SRC BIT(SCRUB_SW_SRC)
-#define SCRUB_FLAG_SW_PROG_SRC BIT(SCRUB_SW_PROG_SRC)
-#define SCRUB_FLAG_SW_TUN BIT(SCRUB_SW_SCRUB_TUNABLE)
-#define SCRUB_FLAG_HW_PROG BIT(SCRUB_HW_PROG)
-#define SCRUB_FLAG_HW_SRC BIT(SCRUB_HW_SRC)
-#define SCRUB_FLAG_HW_PROG_SRC BIT(SCRUB_HW_PROG_SRC)
-#define SCRUB_FLAG_HW_TUN BIT(SCRUB_HW_TUNABLE)
-
-/* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */
-
-/* EDAC internal operation states */
-#define OP_ALLOC 0x100
-#define OP_RUNNING_POLL 0x201
-#define OP_RUNNING_INTERRUPT 0x202
-#define OP_RUNNING_POLL_INTR 0x203
-#define OP_OFFLINE 0x300
-
-/*
- * There are several things to be aware of that aren't at all obvious:
- *
- *
- * SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc..
- *
- * These are some of the many terms that are thrown about that don't always
- * mean what people think they mean (Inconceivable!). In the interest of
- * creating a common ground for discussion, terms and their definitions
- * will be established.
- *
- * Memory devices: The individual chip on a memory stick. These devices
- * commonly output 4 and 8 bits each. Grouping several
- * of these in parallel provides 64 bits which is common
- * for a memory stick.
- *
- * Memory Stick: A printed circuit board that aggregates multiple
- * memory devices in parallel. This is the atomic
- * memory component that is purchaseable by Joe consumer
- * and loaded into a memory socket.
- *
- * Socket: A physical connector on the motherboard that accepts
- * a single memory stick.
- *
- * Channel: Set of memory devices on a memory stick that must be
- * grouped in parallel with one or more additional
- * channels from other memory sticks. This parallel
- * grouping of the output from multiple channels are
- * necessary for the smallest granularity of memory access.
- * Some memory controllers are capable of single channel -
- * which means that memory sticks can be loaded
- * individually. Other memory controllers are only
- * capable of dual channel - which means that memory
- * sticks must be loaded as pairs (see "socket set").
- *
- * Chip-select row: All of the memory devices that are selected together.
- * for a single, minimum grain of memory access.
- * This selects all of the parallel memory devices across
- * all of the parallel channels. Common chip-select rows
- * for single channel are 64 bits, for dual channel 128
- * bits.
- *
- * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memory.
- * Motherboards commonly drive two chip-select pins to
- * a memory stick. A single-ranked stick, will occupy
- * only one of those rows. The other will be unused.
- *
- * Double-Ranked stick: A double-ranked stick has two chip-select rows which
- * access different sets of memory devices. The two
- * rows cannot be accessed concurrently.
- *
- * Double-sided stick: DEPRECATED TERM, see Double-Ranked stick.
- * A double-sided stick has two chip-select rows which
- * access different sets of memory devices. The two
- * rows cannot be accessed concurrently. "Double-sided"
- * is irrespective of the memory devices being mounted
- * on both sides of the memory stick.
- *
- * Socket set: All of the memory sticks that are required for
- * a single memory access or all of the memory sticks
- * spanned by a chip-select row. A single socket set
- * has two chip-select rows and if double-sided sticks
- * are used these will occupy those chip-select rows.
- *
- * Bank: This term is avoided because it is unclear when
- * needing to distinguish between chip-select rows and
- * socket sets.
- *
- * Controller pages:
- *
- * Physical pages:
- *
- * Virtual pages:
- *
- *
- * STRUCTURE ORGANIZATION AND CHOICES
- *
- *
- *
- * PS - I enjoyed writing all that about as much as you enjoyed reading it.
- */
-
-struct channel_info {
- int chan_idx; /* channel index */
- u32 ce_count; /* Correctable Errors for this CHANNEL */
- char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */
- struct csrow_info *csrow; /* the parent */
-};
-
-struct csrow_info {
- unsigned long first_page; /* first page number in dimm */
- unsigned long last_page; /* last page number in dimm */
- unsigned long page_mask; /* used for interleaving -
- * 0UL for non intlv
- */
- u32 nr_pages; /* number of pages in csrow */
- u32 grain; /* granularity of reported error in bytes */
- int csrow_idx; /* the chip-select row */
- enum dev_type dtype; /* memory device type */
- u32 ue_count; /* Uncorrectable Errors for this csrow */
- u32 ce_count; /* Correctable Errors for this csrow */
- enum mem_type mtype; /* memory csrow type */
- enum edac_type edac_mode; /* EDAC mode for this csrow */
- struct mem_ctl_info *mci; /* the parent */
-
- struct kobject kobj; /* sysfs kobject for this csrow */
-
- /* channel information for this csrow */
- u32 nr_channels;
- struct channel_info *channels;
-};
-
-struct mcidev_sysfs_group {
- const char *name; /* group name */
- const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
-};
-
-struct mcidev_sysfs_group_kobj {
- struct list_head list; /* list for all instances within a mc */
-
- struct kobject kobj; /* kobj for the group */
-
- const struct mcidev_sysfs_group *grp; /* group description table */
- struct mem_ctl_info *mci; /* the parent */
-};
-
-/* mcidev_sysfs_attribute structure
- * used for driver sysfs attributes and in mem_ctl_info
- * sysfs top level entries
- */
-struct mcidev_sysfs_attribute {
- /* It should use either attr or grp */
- struct attribute attr;
- const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
-
- /* Ops for show/store values at the attribute - not used on group */
- ssize_t (*show)(struct mem_ctl_info *,char *);
- ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
-};
-
-/* MEMORY controller information structure
- */
-struct mem_ctl_info {
- struct list_head link; /* for global list of mem_ctl_info structs */
-
- struct module *owner; /* Module owner of this control struct */
-
- unsigned long mtype_cap; /* memory types supported by mc */
- unsigned long edac_ctl_cap; /* Mem controller EDAC capabilities */
- unsigned long edac_cap; /* configuration capabilities - this is
- * closely related to edac_ctl_cap. The
- * difference is that the controller may be
- * capable of s4ecd4ed which would be listed
- * in edac_ctl_cap, but if channels aren't
- * capable of s4ecd4ed then the edac_cap would
- * not have that capability.
- */
- unsigned long scrub_cap; /* chipset scrub capabilities */
- enum scrub_type scrub_mode; /* current scrub mode */
-
- /* Translates sdram memory scrub rate given in bytes/sec to the
- internal representation and configures whatever else needs
- to be configured.
- */
- int (*set_sdram_scrub_rate) (struct mem_ctl_info * mci, u32 bw);
-
- /* Get the current sdram memory scrub rate from the internal
- representation and converts it to the closest matching
- bandwidth in bytes/sec.
- */
- int (*get_sdram_scrub_rate) (struct mem_ctl_info * mci);
-
-
- /* pointer to edac checking routine */
- void (*edac_check) (struct mem_ctl_info * mci);
-
- /*
- * Remaps memory pages: controller pages to physical pages.
- * For most MC's, this will be NULL.
- */
- /* FIXME - why not send the phys page to begin with? */
- unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
- unsigned long page);
- int mc_idx;
- int nr_csrows;
- struct csrow_info *csrows;
- /*
- * FIXME - what about controllers on other busses? - IDs must be
- * unique. dev pointer should be sufficiently unique, but
- * BUS:SLOT.FUNC numbers may not be unique.
- */
- struct device *dev;
- const char *mod_name;
- const char *mod_ver;
- const char *ctl_name;
- const char *dev_name;
- char proc_name[MC_PROC_NAME_MAX_LEN + 1];
- void *pvt_info;
- u32 ue_noinfo_count; /* Uncorrectable Errors w/o info */
- u32 ce_noinfo_count; /* Correctable Errors w/o info */
- u32 ue_count; /* Total Uncorrectable Errors for this MC */
- u32 ce_count; /* Total Correctable Errors for this MC */
- unsigned long start_time; /* mci load start time (in jiffies) */
-
- struct completion complete;
-
- /* edac sysfs device control */
- struct kobject edac_mci_kobj;
-
- /* list for all grp instances within a mc */
- struct list_head grp_kobj_list;
-
- /* Additional top controller level attributes, but specified
- * by the low level driver.
- *
- * Set by the low level driver to provide attributes at the
- * controller level, same level as 'ue_count' and 'ce_count' above.
- * An array of structures, NULL terminated
- *
- * If attributes are desired, then set to array of attributes
- * If no attributes are desired, leave NULL
- */
- const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes;
-
- /* work struct for this MC */
- struct delayed_work work;
-
- /* the internal state of this controller instance */
- int op_state;
-};
-
/*
* The following are the structures to provide for a generic
* or abstract 'edac_device'. This set of structures and the
diff --git a/drivers/edac/edac_mce.c b/drivers/edac/edac_mce.c
deleted file mode 100644
index 9ccdc5b140e7..000000000000
--- a/drivers/edac/edac_mce.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Provides edac interface to mcelog events
- *
- * This file may be distributed under the terms of the
- * GNU General Public License version 2.
- *
- * Copyright (c) 2009 by:
- * Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * Red Hat Inc. http://www.redhat.com
- */
-
-#include <linux/module.h>
-#include <linux/edac_mce.h>
-#include <asm/mce.h>
-
-int edac_mce_enabled;
-EXPORT_SYMBOL_GPL(edac_mce_enabled);
-
-
-/*
- * Extension interface
- */
-
-static LIST_HEAD(edac_mce_list);
-static DEFINE_MUTEX(edac_mce_lock);
-
-int edac_mce_register(struct edac_mce *edac_mce)
-{
- mutex_lock(&edac_mce_lock);
- list_add_tail(&edac_mce->list, &edac_mce_list);
- mutex_unlock(&edac_mce_lock);
- return 0;
-}
-EXPORT_SYMBOL(edac_mce_register);
-
-void edac_mce_unregister(struct edac_mce *edac_mce)
-{
- mutex_lock(&edac_mce_lock);
- list_del(&edac_mce->list);
- mutex_unlock(&edac_mce_lock);
-}
-EXPORT_SYMBOL(edac_mce_unregister);
-
-int edac_mce_parse(struct mce *mce)
-{
- struct edac_mce *edac_mce;
-
- list_for_each_entry(edac_mce, &edac_mce_list, list) {
- if (edac_mce->check_error(edac_mce->priv, mce))
- return 1;
- }
-
- /* Nobody queued the error */
- return 0;
-}
-EXPORT_SYMBOL_GPL(edac_mce_parse);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
-MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
-MODULE_DESCRIPTION("EDAC Driver for mcelog captured errors");
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index a76fe8366b68..6104dba380b6 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -372,7 +372,7 @@ static const char *get_err_from_table(const char *table[], int size, int pos)
static void i7300_process_error_global(struct mem_ctl_info *mci)
{
struct i7300_pvt *pvt;
- u32 errnum, value;
+ u32 errnum, error_reg;
unsigned long errors;
const char *specific;
bool is_fatal;
@@ -381,9 +381,9 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* read in the 1st FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_HI, &value);
- if (unlikely(value)) {
- errors = value;
+ FERR_GLOBAL_HI, &error_reg);
+ if (unlikely(error_reg)) {
+ errors = error_reg;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_global_hi_name));
specific = GET_ERR_FROM_TABLE(ferr_global_hi_name, errnum);
@@ -391,15 +391,15 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* Clear the error bit */
pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_HI, value);
+ FERR_GLOBAL_HI, error_reg);
goto error_global;
}
pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, &value);
- if (unlikely(value)) {
- errors = value;
+ FERR_GLOBAL_LO, &error_reg);
+ if (unlikely(error_reg)) {
+ errors = error_reg;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_global_lo_name));
specific = GET_ERR_FROM_TABLE(ferr_global_lo_name, errnum);
@@ -407,7 +407,7 @@ static void i7300_process_error_global(struct mem_ctl_info *mci)
/* Clear the error bit */
pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, value);
+ FERR_GLOBAL_LO, error_reg);
goto error_global;
}
@@ -427,7 +427,7 @@ error_global:
static void i7300_process_fbd_error(struct mem_ctl_info *mci)
{
struct i7300_pvt *pvt;
- u32 errnum, value;
+ u32 errnum, value, error_reg;
u16 val16;
unsigned branch, channel, bank, rank, cas, ras;
u32 syndrome;
@@ -440,14 +440,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
/* read in the 1st FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
- FERR_FAT_FBD, &value);
- if (unlikely(value & FERR_FAT_FBD_ERR_MASK)) {
- errors = value & FERR_FAT_FBD_ERR_MASK ;
+ FERR_FAT_FBD, &error_reg);
+ if (unlikely(error_reg & FERR_FAT_FBD_ERR_MASK)) {
+ errors = error_reg & FERR_FAT_FBD_ERR_MASK ;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_fat_fbd_name));
specific = GET_ERR_FROM_TABLE(ferr_fat_fbd_name, errnum);
+ branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0;
- branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
NRECMEMA, &val16);
bank = NRECMEMA_BANK(val16);
@@ -455,11 +455,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
NRECMEMB, &value);
-
is_wr = NRECMEMB_IS_WR(value);
cas = NRECMEMB_CAS(value);
ras = NRECMEMB_RAS(value);
+ /* Clean the error register */
+ pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+ FERR_FAT_FBD, error_reg);
+
snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
"FATAL (Branch=%d DRAM-Bank=%d %s "
"RAS=%d CAS=%d Err=0x%lx (%s))",
@@ -476,21 +479,17 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
/* read in the 1st NON-FATAL error register */
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
- FERR_NF_FBD, &value);
- if (unlikely(value & FERR_NF_FBD_ERR_MASK)) {
- errors = value & FERR_NF_FBD_ERR_MASK;
+ FERR_NF_FBD, &error_reg);
+ if (unlikely(error_reg & FERR_NF_FBD_ERR_MASK)) {
+ errors = error_reg & FERR_NF_FBD_ERR_MASK;
errnum = find_first_bit(&errors,
ARRAY_SIZE(ferr_nf_fbd_name));
specific = GET_ERR_FROM_TABLE(ferr_nf_fbd_name, errnum);
-
- /* Clear the error bit */
- pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
- FERR_GLOBAL_LO, value);
+ branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0;
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
REDMEMA, &syndrome);
- branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
RECMEMA, &val16);
bank = RECMEMA_BANK(val16);
@@ -498,18 +497,20 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
RECMEMB, &value);
-
is_wr = RECMEMB_IS_WR(value);
cas = RECMEMB_CAS(value);
ras = RECMEMB_RAS(value);
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
REDMEMB, &value);
-
channel = (branch << 1);
if (IS_SECOND_CH(value))
channel++;
+ /* Clear the error bit */
+ pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+ FERR_NF_FBD, error_reg);
+
/* Form out message */
snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
"Corrected error (Branch=%d, Channel %d), "
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index f6cf448d69b4..70ad8923f1d7 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -31,11 +31,13 @@
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/edac.h>
#include <linux/mmzone.h>
-#include <linux/edac_mce.h>
#include <linux/smp.h>
+#include <asm/mce.h>
#include <asm/processor.h>
+#include <asm/div64.h>
#include "edac_core.h"
@@ -78,6 +80,8 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
/* OFFSETS for Device 0 Function 0 */
#define MC_CFG_CONTROL 0x90
+ #define MC_CFG_UNLOCK 0x02
+ #define MC_CFG_LOCK 0x00
/* OFFSETS for Device 3 Function 0 */
@@ -98,6 +102,15 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
#define DIMM0_COR_ERR(r) ((r) & 0x7fff)
/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
+#define MC_SSRCONTROL 0x48
+ #define SSR_MODE_DISABLE 0x00
+ #define SSR_MODE_ENABLE 0x01
+ #define SSR_MODE_MASK 0x03
+
+#define MC_SCRUB_CONTROL 0x4c
+ #define STARTSCRUB (1 << 24)
+ #define SCRUBINTERVAL_MASK 0xffffff
+
#define MC_COR_ECC_CNT_0 0x80
#define MC_COR_ECC_CNT_1 0x84
#define MC_COR_ECC_CNT_2 0x88
@@ -253,10 +266,7 @@ struct i7core_pvt {
unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
- unsigned int is_registered;
-
- /* mcelog glue */
- struct edac_mce edac_mce;
+ bool is_registered, enable_scrub;
/* Fifo double buffers */
struct mce mce_entry[MCE_LOG_LEN];
@@ -268,6 +278,9 @@ struct i7core_pvt {
/* Count indicator to show errors not got */
unsigned mce_overrun;
+ /* DCLK Frequency used for computing scrub rate */
+ int dclk_freq;
+
/* Struct to control EDAC polling */
struct edac_pci_ctl_info *i7core_pci;
};
@@ -281,8 +294,7 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
/* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
-
- /* Exists only for RDIMM */
+ /* Exists only for RDIMM */
{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
@@ -303,6 +315,16 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
+
+ /* Generic Non-core registers */
+ /*
+ * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
+ * On Xeon 55xx, however, it has a different id (8086:2c40). So,
+ * the probing code needs to test for the other address in case of
+ * failure of this one
+ */
+ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
+
};
static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
@@ -319,6 +341,12 @@ static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
+
+ /*
+ * This is the PCI device has an alternate address on some
+ * processors like Core i7 860
+ */
+ { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
};
static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
@@ -346,6 +374,10 @@ static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
+
+ /* Generic Non-core registers */
+ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
+
};
#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
@@ -714,6 +746,10 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
csr->edac_mode = mode;
csr->mtype = mtype;
+ snprintf(csr->channels[0].label,
+ sizeof(csr->channels[0].label),
+ "CPU#%uChannel#%u_DIMM#%u",
+ pvt->i7core_dev->socket, i, j);
csrow++;
}
@@ -731,7 +767,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
debugf1("\t\t%#x\t%#x\t%#x\n",
(value[j] >> 27) & 0x1,
(value[j] >> 24) & 0x7,
- (value[j] && ((1 << 24) - 1)));
+ (value[j] & ((1 << 24) - 1)));
}
return 0;
@@ -1324,6 +1360,20 @@ static int i7core_get_onedevice(struct pci_dev **prev,
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
dev_descr->dev_id, *prev);
+ /*
+ * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
+ * is at addr 8086:2c40, instead of 8086:2c41. So, we need
+ * to probe for the alternate address in case of failure
+ */
+ if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
+
+ if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
+ *prev);
+
if (!pdev) {
if (*prev) {
*prev = pdev;
@@ -1444,8 +1494,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
struct i7core_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev;
int i, func, slot;
+ char *family;
- pvt->is_registered = 0;
+ pvt->is_registered = false;
+ pvt->enable_scrub = false;
for (i = 0; i < i7core_dev->n_devs; i++) {
pdev = i7core_dev->pdev[i];
if (!pdev)
@@ -1461,9 +1513,37 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
if (unlikely(func > MAX_CHAN_FUNC))
goto error;
pvt->pci_ch[slot - 4][func] = pdev;
- } else if (!slot && !func)
+ } else if (!slot && !func) {
pvt->pci_noncore = pdev;
- else
+
+ /* Detect the processor family */
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_I7_NONCORE:
+ family = "Xeon 35xx/ i7core";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT:
+ family = "i7-800/i5-700";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE:
+ family = "Xeon 34xx";
+ pvt->enable_scrub = false;
+ break;
+ case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT:
+ family = "Xeon 55xx";
+ pvt->enable_scrub = true;
+ break;
+ case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2:
+ family = "Xeon 56xx / i7-900";
+ pvt->enable_scrub = true;
+ break;
+ default:
+ family = "unknown";
+ pvt->enable_scrub = false;
+ }
+ debugf0("Detected a processor type %s\n", family);
+ } else
goto error;
debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
@@ -1472,7 +1552,7 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
if (PCI_SLOT(pdev->devfn) == 3 &&
PCI_FUNC(pdev->devfn) == 2)
- pvt->is_registered = 1;
+ pvt->is_registered = true;
}
return 0;
@@ -1826,33 +1906,43 @@ check_ce_error:
* WARNING: As this routine should be called at NMI time, extra care should
* be taken to avoid deadlocks, and to be as fast as possible.
*/
-static int i7core_mce_check_error(void *priv, struct mce *mce)
+static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data)
{
- struct mem_ctl_info *mci = priv;
- struct i7core_pvt *pvt = mci->pvt_info;
+ struct mce *mce = (struct mce *)data;
+ struct i7core_dev *i7_dev;
+ struct mem_ctl_info *mci;
+ struct i7core_pvt *pvt;
+
+ i7_dev = get_i7core_dev(mce->socketid);
+ if (!i7_dev)
+ return NOTIFY_BAD;
+
+ mci = i7_dev->mci;
+ pvt = mci->pvt_info;
/*
* Just let mcelog handle it if the error is
* outside the memory controller
*/
if (((mce->status & 0xffff) >> 7) != 1)
- return 0;
+ return NOTIFY_DONE;
/* Bank 8 registers are the only ones that we know how to handle */
if (mce->bank != 8)
- return 0;
+ return NOTIFY_DONE;
#ifdef CONFIG_SMP
/* Only handle if it is the right mc controller */
- if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
- return 0;
+ if (mce->socketid != pvt->i7core_dev->socket)
+ return NOTIFY_DONE;
#endif
smp_rmb();
if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
smp_wmb();
pvt->mce_overrun++;
- return 0;
+ return NOTIFY_DONE;
}
/* Copy memory error at the ringbuffer */
@@ -1865,7 +1955,240 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
i7core_check_error(mci);
/* Advise mcelog that the errors were handled */
- return 1;
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block i7_mce_dec = {
+ .notifier_call = i7core_mce_check_error,
+};
+
+struct memdev_dmi_entry {
+ u8 type;
+ u8 length;
+ u16 handle;
+ u16 phys_mem_array_handle;
+ u16 mem_err_info_handle;
+ u16 total_width;
+ u16 data_width;
+ u16 size;
+ u8 form;
+ u8 device_set;
+ u8 device_locator;
+ u8 bank_locator;
+ u8 memory_type;
+ u16 type_detail;
+ u16 speed;
+ u8 manufacturer;
+ u8 serial_number;
+ u8 asset_tag;
+ u8 part_number;
+ u8 attributes;
+ u32 extended_size;
+ u16 conf_mem_clk_speed;
+} __attribute__((__packed__));
+
+
+/*
+ * Decode the DRAM Clock Frequency, be paranoid, make sure that all
+ * memory devices show the same speed, and if they don't then consider
+ * all speeds to be invalid.
+ */
+static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
+{
+ int *dclk_freq = _dclk_freq;
+ u16 dmi_mem_clk_speed;
+
+ if (*dclk_freq == -1)
+ return;
+
+ if (dh->type == DMI_ENTRY_MEM_DEVICE) {
+ struct memdev_dmi_entry *memdev_dmi_entry =
+ (struct memdev_dmi_entry *)dh;
+ unsigned long conf_mem_clk_speed_offset =
+ (unsigned long)&memdev_dmi_entry->conf_mem_clk_speed -
+ (unsigned long)&memdev_dmi_entry->type;
+ unsigned long speed_offset =
+ (unsigned long)&memdev_dmi_entry->speed -
+ (unsigned long)&memdev_dmi_entry->type;
+
+ /* Check that a DIMM is present */
+ if (memdev_dmi_entry->size == 0)
+ return;
+
+ /*
+ * Pick the configured speed if it's available, otherwise
+ * pick the DIMM speed, or we don't have a speed.
+ */
+ if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) {
+ dmi_mem_clk_speed =
+ memdev_dmi_entry->conf_mem_clk_speed;
+ } else if (memdev_dmi_entry->length > speed_offset) {
+ dmi_mem_clk_speed = memdev_dmi_entry->speed;
+ } else {
+ *dclk_freq = -1;
+ return;
+ }
+
+ if (*dclk_freq == 0) {
+ /* First pass, speed was 0 */
+ if (dmi_mem_clk_speed > 0) {
+ /* Set speed if a valid speed is read */
+ *dclk_freq = dmi_mem_clk_speed;
+ } else {
+ /* Otherwise we don't have a valid speed */
+ *dclk_freq = -1;
+ }
+ } else if (*dclk_freq > 0 &&
+ *dclk_freq != dmi_mem_clk_speed) {
+ /*
+ * If we have a speed, check that all DIMMS are the same
+ * speed, otherwise set the speed as invalid.
+ */
+ *dclk_freq = -1;
+ }
+ }
+}
+
+/*
+ * The default DCLK frequency is used as a fallback if we
+ * fail to find anything reliable in the DMI. The value
+ * is taken straight from the datasheet.
+ */
+#define DEFAULT_DCLK_FREQ 800
+
+static int get_dclk_freq(void)
+{
+ int dclk_freq = 0;
+
+ dmi_walk(decode_dclk, (void *)&dclk_freq);
+
+ if (dclk_freq < 1)
+ return DEFAULT_DCLK_FREQ;
+
+ return dclk_freq;
+}
+
+/*
+ * set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate
+ * to hardware according to SCRUBINTERVAL formula
+ * found in datasheet.
+ */
+static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ u32 dw_scrub;
+ u32 dw_ssr;
+
+ /* Get data from the MC register, function 2 */
+ pdev = pvt->pci_mcr[2];
+ if (!pdev)
+ return -ENODEV;
+
+ pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
+
+ if (new_bw == 0) {
+ /* Prepare to disable petrol scrub */
+ dw_scrub &= ~STARTSCRUB;
+ /* Stop the patrol scrub engine */
+ write_and_test(pdev, MC_SCRUB_CONTROL,
+ dw_scrub & ~SCRUBINTERVAL_MASK);
+
+ /* Get current status of scrub rate and set bit to disable */
+ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
+ dw_ssr &= ~SSR_MODE_MASK;
+ dw_ssr |= SSR_MODE_DISABLE;
+ } else {
+ const int cache_line_size = 64;
+ const u32 freq_dclk_mhz = pvt->dclk_freq;
+ unsigned long long scrub_interval;
+ /*
+ * Translate the desired scrub rate to a register value and
+ * program the corresponding register value.
+ */
+ scrub_interval = (unsigned long long)freq_dclk_mhz *
+ cache_line_size * 1000000;
+ do_div(scrub_interval, new_bw);
+
+ if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK)
+ return -EINVAL;
+
+ dw_scrub = SCRUBINTERVAL_MASK & scrub_interval;
+
+ /* Start the patrol scrub engine */
+ pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
+ STARTSCRUB | dw_scrub);
+
+ /* Get current status of scrub rate and set bit to enable */
+ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
+ dw_ssr &= ~SSR_MODE_MASK;
+ dw_ssr |= SSR_MODE_ENABLE;
+ }
+ /* Disable or enable scrubbing */
+ pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
+
+ return new_bw;
+}
+
+/*
+ * get_sdram_scrub_rate This routine convert current scrub rate value
+ * into byte/sec bandwidth accourding to
+ * SCRUBINTERVAL formula found in datasheet.
+ */
+static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ const u32 cache_line_size = 64;
+ const u32 freq_dclk_mhz = pvt->dclk_freq;
+ unsigned long long scrub_rate;
+ u32 scrubval;
+
+ /* Get data from the MC register, function 2 */
+ pdev = pvt->pci_mcr[2];
+ if (!pdev)
+ return -ENODEV;
+
+ /* Get current scrub control data */
+ pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
+
+ /* Mask highest 8-bits to 0 */
+ scrubval &= SCRUBINTERVAL_MASK;
+ if (!scrubval)
+ return 0;
+
+ /* Calculate scrub rate value into byte/sec bandwidth */
+ scrub_rate = (unsigned long long)freq_dclk_mhz *
+ 1000000 * cache_line_size;
+ do_div(scrub_rate, scrubval);
+ return (int)scrub_rate;
+}
+
+static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ u32 pci_lock;
+
+ /* Unlock writes to pci registers */
+ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
+ pci_lock &= ~0x3;
+ pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
+ pci_lock | MC_CFG_UNLOCK);
+
+ mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
+ mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
+}
+
+static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ u32 pci_lock;
+
+ /* Lock writes to pci registers */
+ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
+ pci_lock &= ~0x3;
+ pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
+ pci_lock | MC_CFG_LOCK);
}
static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
@@ -1874,7 +2197,8 @@ static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
&pvt->i7core_dev->pdev[0]->dev,
EDAC_MOD_STR);
if (unlikely(!pvt->i7core_pci))
- pr_warn("Unable to setup PCI error report via EDAC\n");
+ i7core_printk(KERN_WARNING,
+ "Unable to setup PCI error report via EDAC\n");
}
static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
@@ -1906,8 +2230,11 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
__func__, mci, &i7core_dev->pdev[0]->dev);
- /* Disable MCE NMI handler */
- edac_mce_unregister(&pvt->edac_mce);
+ /* Disable scrubrate setting */
+ if (pvt->enable_scrub)
+ disable_sdram_scrub_setting(mci);
+
+ atomic_notifier_chain_unregister(&x86_mce_decoder_chain, &i7_mce_dec);
/* Disable EDAC polling */
i7core_pci_ctl_release(pvt);
@@ -1979,6 +2306,10 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
/* Set the function pointer to an actual operation function */
mci->edac_check = i7core_check_error;
+ /* Enable scrubrate setting */
+ if (pvt->enable_scrub)
+ enable_sdram_scrub_setting(mci);
+
/* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
debugf0("MC: " __FILE__
@@ -2002,21 +2333,13 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
/* allocating generic PCI control info */
i7core_pci_ctl_create(pvt);
- /* Registers on edac_mce in order to receive memory errors */
- pvt->edac_mce.priv = mci;
- pvt->edac_mce.check_error = i7core_mce_check_error;
- rc = edac_mce_register(&pvt->edac_mce);
- if (unlikely(rc < 0)) {
- debugf0("MC: " __FILE__
- ": %s(): failed edac_mce_register()\n", __func__);
- goto fail1;
- }
+ /* DCLK for scrub rate setting */
+ pvt->dclk_freq = get_dclk_freq();
+
+ atomic_notifier_chain_register(&x86_mce_decoder_chain, &i7_mce_dec);
return 0;
-fail1:
- i7core_pci_ctl_release(pvt);
- edac_mc_del_mc(mci->dev);
fail0:
kfree(mci->ctl_name);
edac_mc_free(mci);
@@ -2035,7 +2358,7 @@ fail0:
static int __devinit i7core_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- int rc;
+ int rc, count = 0;
struct i7core_dev *i7core_dev;
/* get the pci devices we want to reserve for our use */
@@ -2055,12 +2378,28 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
goto fail0;
list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
+ count++;
rc = i7core_register_mci(i7core_dev);
if (unlikely(rc < 0))
goto fail1;
}
- i7core_printk(KERN_INFO, "Driver loaded.\n");
+ /*
+ * Nehalem-EX uses a different memory controller. However, as the
+ * memory controller is not visible on some Nehalem/Nehalem-EP, we
+ * need to indirectly probe via a X58 PCI device. The same devices
+ * are found on (some) Nehalem-EX. So, on those machines, the
+ * probe routine needs to return -ENODEV, as the actual Memory
+ * Controller registers won't be detected.
+ */
+ if (!count) {
+ rc = -ENODEV;
+ goto fail1;
+ }
+
+ i7core_printk(KERN_INFO,
+ "Driver loaded, %d memory controller(s) found.\n",
+ count);
mutex_unlock(&i7core_edac_lock);
return 0;
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index 795cfbc0bf50..d0864d9c38ad 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -9,7 +9,7 @@ static u8 xec_mask = 0xf;
static u8 nb_err_cpumask = 0xf;
static bool report_gart_errors;
-static void (*nb_bus_decoder)(int node_id, struct mce *m, u32 nbcfg);
+static void (*nb_bus_decoder)(int node_id, struct mce *m);
void amd_report_gart_errors(bool v)
{
@@ -17,13 +17,13 @@ void amd_report_gart_errors(bool v)
}
EXPORT_SYMBOL_GPL(amd_report_gart_errors);
-void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32))
+void amd_register_ecc_decoder(void (*f)(int, struct mce *))
{
nb_bus_decoder = f;
}
EXPORT_SYMBOL_GPL(amd_register_ecc_decoder);
-void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32))
+void amd_unregister_ecc_decoder(void (*f)(int, struct mce *))
{
if (nb_bus_decoder) {
WARN_ON(nb_bus_decoder != f);
@@ -592,31 +592,14 @@ static bool nb_noop_mce(u16 ec, u8 xec)
return false;
}
-void amd_decode_nb_mce(int node_id, struct mce *m, u32 nbcfg)
+void amd_decode_nb_mce(struct mce *m)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
- u16 ec = EC(m->status);
- u8 xec = XEC(m->status, 0x1f);
- u32 nbsh = (u32)(m->status >> 32);
- int core = -1;
-
- pr_emerg(HW_ERR "Northbridge Error (node %d", node_id);
-
- /* F10h, revD can disable ErrCpu[3:0] through ErrCpuVal */
- if (c->x86 == 0x10 && c->x86_model > 7) {
- if (nbsh & NBSH_ERR_CPU_VAL)
- core = nbsh & nb_err_cpumask;
- } else {
- u8 assoc_cpus = nbsh & nb_err_cpumask;
-
- if (assoc_cpus > 0)
- core = fls(assoc_cpus) - 1;
- }
+ int node_id = amd_get_nb_id(m->extcpu);
+ u16 ec = EC(m->status);
+ u8 xec = XEC(m->status, 0x1f);
- if (core >= 0)
- pr_cont(", core %d): ", core);
- else
- pr_cont("): ");
+ pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id);
switch (xec) {
case 0x2:
@@ -648,7 +631,7 @@ void amd_decode_nb_mce(int node_id, struct mce *m, u32 nbcfg)
if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x15)
if ((xec == 0x8 || xec == 0x0) && nb_bus_decoder)
- nb_bus_decoder(node_id, m, nbcfg);
+ nb_bus_decoder(node_id, m);
return;
@@ -764,13 +747,13 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
{
struct mce *m = (struct mce *)data;
struct cpuinfo_x86 *c = &boot_cpu_data;
- int node, ecc;
+ int ecc;
if (amd_filter_mce(m))
return NOTIFY_STOP;
- pr_emerg(HW_ERR "MC%d_STATUS[%s|%s|%s|%s|%s",
- m->bank,
+ pr_emerg(HW_ERR "CPU:%d\tMC%d_STATUS[%s|%s|%s|%s|%s",
+ m->extcpu, m->bank,
((m->status & MCI_STATUS_OVER) ? "Over" : "-"),
((m->status & MCI_STATUS_UC) ? "UE" : "CE"),
((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"),
@@ -789,6 +772,8 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
pr_cont("]: 0x%016llx\n", m->status);
+ if (m->status & MCI_STATUS_ADDRV)
+ pr_emerg(HW_ERR "\tMC%d_ADDR: 0x%016llx\n", m->bank, m->addr);
switch (m->bank) {
case 0:
@@ -811,8 +796,7 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
break;
case 4:
- node = amd_get_nb_id(m->extcpu);
- amd_decode_nb_mce(node, m, 0);
+ amd_decode_nb_mce(m);
break;
case 5:
diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h
index 795a3206acf5..0106747e240c 100644
--- a/drivers/edac/mce_amd.h
+++ b/drivers/edac/mce_amd.h
@@ -86,9 +86,9 @@ struct amd_decoder_ops {
};
void amd_report_gart_errors(bool);
-void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32));
-void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32));
-void amd_decode_nb_mce(int, struct mce *, u32);
+void amd_register_ecc_decoder(void (*f)(int, struct mce *));
+void amd_unregister_ecc_decoder(void (*f)(int, struct mce *));
+void amd_decode_nb_mce(struct mce *);
int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data);
#endif /* _EDAC_MCE_AMD_H */
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
new file mode 100644
index 000000000000..7a402bfbee7d
--- /dev/null
+++ b/drivers/edac/sb_edac.c
@@ -0,0 +1,1893 @@
+/* Intel Sandy Bridge -EN/-EP/-EX Memory Controller kernel module
+ *
+ * This driver supports the memory controllers found on the Intel
+ * processor family Sandy Bridge.
+ *
+ * This file may be distributed under the terms of the
+ * GNU General Public License version 2 only.
+ *
+ * Copyright (c) 2011 by:
+ * Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/edac.h>
+#include <linux/mmzone.h>
+#include <linux/smp.h>
+#include <linux/bitmap.h>
+#include <asm/processor.h>
+#include <asm/mce.h>
+
+#include "edac_core.h"
+
+/* Static vars */
+static LIST_HEAD(sbridge_edac_list);
+static DEFINE_MUTEX(sbridge_edac_lock);
+static int probed;
+
+/*
+ * Alter this version for the module when modifications are made
+ */
+#define SBRIDGE_REVISION " Ver: 1.0.0 "
+#define EDAC_MOD_STR "sbridge_edac"
+
+/*
+ * Debug macros
+ */
+#define sbridge_printk(level, fmt, arg...) \
+ edac_printk(level, "sbridge", fmt, ##arg)
+
+#define sbridge_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "sbridge", fmt, ##arg)
+
+/*
+ * Get a bit field at register value <v>, from bit <lo> to bit <hi>
+ */
+#define GET_BITFIELD(v, lo, hi) \
+ (((v) & ((1ULL << ((hi) - (lo) + 1)) - 1) << (lo)) >> (lo))
+
+/*
+ * sbridge Memory Controller Registers
+ */
+
+/*
+ * FIXME: For now, let's order by device function, as it makes
+ * easier for driver's development proccess. This table should be
+ * moved to pci_id.h when submitted upstream
+ */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0 0x3cf4 /* 12.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1 0x3cf6 /* 12.7 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR 0x3cf5 /* 13.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0 0x3ca0 /* 14.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA 0x3ca8 /* 15.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS 0x3c71 /* 15.1 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0 0x3caa /* 15.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1 0x3cab /* 15.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2 0x3cac /* 15.4 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3 0x3cad /* 15.5 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO 0x3cb8 /* 17.0 */
+
+ /*
+ * Currently, unused, but will be needed in the future
+ * implementations, as they hold the error counters
+ */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR0 0x3c72 /* 16.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR1 0x3c73 /* 16.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR2 0x3c76 /* 16.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3 0x3c77 /* 16.7 */
+
+/* Devices 12 Function 6, Offsets 0x80 to 0xcc */
+static const u32 dram_rule[] = {
+ 0x80, 0x88, 0x90, 0x98, 0xa0,
+ 0xa8, 0xb0, 0xb8, 0xc0, 0xc8,
+};
+#define MAX_SAD ARRAY_SIZE(dram_rule)
+
+#define SAD_LIMIT(reg) ((GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff)
+#define DRAM_ATTR(reg) GET_BITFIELD(reg, 2, 3)
+#define INTERLEAVE_MODE(reg) GET_BITFIELD(reg, 1, 1)
+#define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0)
+
+static char *get_dram_attr(u32 reg)
+{
+ switch(DRAM_ATTR(reg)) {
+ case 0:
+ return "DRAM";
+ case 1:
+ return "MMCFG";
+ case 2:
+ return "NXM";
+ default:
+ return "unknown";
+ }
+}
+
+static const u32 interleave_list[] = {
+ 0x84, 0x8c, 0x94, 0x9c, 0xa4,
+ 0xac, 0xb4, 0xbc, 0xc4, 0xcc,
+};
+#define MAX_INTERLEAVE ARRAY_SIZE(interleave_list)
+
+#define SAD_PKG0(reg) GET_BITFIELD(reg, 0, 2)
+#define SAD_PKG1(reg) GET_BITFIELD(reg, 3, 5)
+#define SAD_PKG2(reg) GET_BITFIELD(reg, 8, 10)
+#define SAD_PKG3(reg) GET_BITFIELD(reg, 11, 13)
+#define SAD_PKG4(reg) GET_BITFIELD(reg, 16, 18)
+#define SAD_PKG5(reg) GET_BITFIELD(reg, 19, 21)
+#define SAD_PKG6(reg) GET_BITFIELD(reg, 24, 26)
+#define SAD_PKG7(reg) GET_BITFIELD(reg, 27, 29)
+
+static inline int sad_pkg(u32 reg, int interleave)
+{
+ switch (interleave) {
+ case 0:
+ return SAD_PKG0(reg);
+ case 1:
+ return SAD_PKG1(reg);
+ case 2:
+ return SAD_PKG2(reg);
+ case 3:
+ return SAD_PKG3(reg);
+ case 4:
+ return SAD_PKG4(reg);
+ case 5:
+ return SAD_PKG5(reg);
+ case 6:
+ return SAD_PKG6(reg);
+ case 7:
+ return SAD_PKG7(reg);
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Devices 12 Function 7 */
+
+#define TOLM 0x80
+#define TOHM 0x84
+
+#define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff)
+#define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff)
+
+/* Device 13 Function 6 */
+
+#define SAD_TARGET 0xf0
+
+#define SOURCE_ID(reg) GET_BITFIELD(reg, 9, 11)
+
+#define SAD_CONTROL 0xf4
+
+#define NODE_ID(reg) GET_BITFIELD(reg, 0, 2)
+
+/* Device 14 function 0 */
+
+static const u32 tad_dram_rule[] = {
+ 0x40, 0x44, 0x48, 0x4c,
+ 0x50, 0x54, 0x58, 0x5c,
+ 0x60, 0x64, 0x68, 0x6c,
+};
+#define MAX_TAD ARRAY_SIZE(tad_dram_rule)
+
+#define TAD_LIMIT(reg) ((GET_BITFIELD(reg, 12, 31) << 26) | 0x3ffffff)
+#define TAD_SOCK(reg) GET_BITFIELD(reg, 10, 11)
+#define TAD_CH(reg) GET_BITFIELD(reg, 8, 9)
+#define TAD_TGT3(reg) GET_BITFIELD(reg, 6, 7)
+#define TAD_TGT2(reg) GET_BITFIELD(reg, 4, 5)
+#define TAD_TGT1(reg) GET_BITFIELD(reg, 2, 3)
+#define TAD_TGT0(reg) GET_BITFIELD(reg, 0, 1)
+
+/* Device 15, function 0 */
+
+#define MCMTR 0x7c
+
+#define IS_ECC_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 2, 2)
+#define IS_LOCKSTEP_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 1, 1)
+#define IS_CLOSE_PG(mcmtr) GET_BITFIELD(mcmtr, 0, 0)
+
+/* Device 15, function 1 */
+
+#define RASENABLES 0xac
+#define IS_MIRROR_ENABLED(reg) GET_BITFIELD(reg, 0, 0)
+
+/* Device 15, functions 2-5 */
+
+static const int mtr_regs[] = {
+ 0x80, 0x84, 0x88,
+};
+
+#define RANK_DISABLE(mtr) GET_BITFIELD(mtr, 16, 19)
+#define IS_DIMM_PRESENT(mtr) GET_BITFIELD(mtr, 14, 14)
+#define RANK_CNT_BITS(mtr) GET_BITFIELD(mtr, 12, 13)
+#define RANK_WIDTH_BITS(mtr) GET_BITFIELD(mtr, 2, 4)
+#define COL_WIDTH_BITS(mtr) GET_BITFIELD(mtr, 0, 1)
+
+static const u32 tad_ch_nilv_offset[] = {
+ 0x90, 0x94, 0x98, 0x9c,
+ 0xa0, 0xa4, 0xa8, 0xac,
+ 0xb0, 0xb4, 0xb8, 0xbc,
+};
+#define CHN_IDX_OFFSET(reg) GET_BITFIELD(reg, 28, 29)
+#define TAD_OFFSET(reg) (GET_BITFIELD(reg, 6, 25) << 26)
+
+static const u32 rir_way_limit[] = {
+ 0x108, 0x10c, 0x110, 0x114, 0x118,
+};
+#define MAX_RIR_RANGES ARRAY_SIZE(rir_way_limit)
+
+#define IS_RIR_VALID(reg) GET_BITFIELD(reg, 31, 31)
+#define RIR_WAY(reg) GET_BITFIELD(reg, 28, 29)
+#define RIR_LIMIT(reg) ((GET_BITFIELD(reg, 1, 10) << 29)| 0x1fffffff)
+
+#define MAX_RIR_WAY 8
+
+static const u32 rir_offset[MAX_RIR_RANGES][MAX_RIR_WAY] = {
+ { 0x120, 0x124, 0x128, 0x12c, 0x130, 0x134, 0x138, 0x13c },
+ { 0x140, 0x144, 0x148, 0x14c, 0x150, 0x154, 0x158, 0x15c },
+ { 0x160, 0x164, 0x168, 0x16c, 0x170, 0x174, 0x178, 0x17c },
+ { 0x180, 0x184, 0x188, 0x18c, 0x190, 0x194, 0x198, 0x19c },
+ { 0x1a0, 0x1a4, 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc },
+};
+
+#define RIR_RNK_TGT(reg) GET_BITFIELD(reg, 16, 19)
+#define RIR_OFFSET(reg) GET_BITFIELD(reg, 2, 14)
+
+/* Device 16, functions 2-7 */
+
+/*
+ * FIXME: Implement the error count reads directly
+ */
+
+static const u32 correrrcnt[] = {
+ 0x104, 0x108, 0x10c, 0x110,
+};
+
+#define RANK_ODD_OV(reg) GET_BITFIELD(reg, 31, 31)
+#define RANK_ODD_ERR_CNT(reg) GET_BITFIELD(reg, 16, 30)
+#define RANK_EVEN_OV(reg) GET_BITFIELD(reg, 15, 15)
+#define RANK_EVEN_ERR_CNT(reg) GET_BITFIELD(reg, 0, 14)
+
+static const u32 correrrthrsld[] = {
+ 0x11c, 0x120, 0x124, 0x128,
+};
+
+#define RANK_ODD_ERR_THRSLD(reg) GET_BITFIELD(reg, 16, 30)
+#define RANK_EVEN_ERR_THRSLD(reg) GET_BITFIELD(reg, 0, 14)
+
+
+/* Device 17, function 0 */
+
+#define RANK_CFG_A 0x0328
+
+#define IS_RDIMM_ENABLED(reg) GET_BITFIELD(reg, 11, 11)
+
+/*
+ * sbridge structs
+ */
+
+#define NUM_CHANNELS 4
+#define MAX_DIMMS 3 /* Max DIMMS per channel */
+
+struct sbridge_info {
+ u32 mcmtr;
+};
+
+struct sbridge_channel {
+ u32 ranks;
+ u32 dimms;
+};
+
+struct pci_id_descr {
+ int dev;
+ int func;
+ int dev_id;
+ int optional;
+};
+
+struct pci_id_table {
+ const struct pci_id_descr *descr;
+ int n_devs;
+};
+
+struct sbridge_dev {
+ struct list_head list;
+ u8 bus, mc;
+ u8 node_id, source_id;
+ struct pci_dev **pdev;
+ int n_devs;
+ struct mem_ctl_info *mci;
+};
+
+struct sbridge_pvt {
+ struct pci_dev *pci_ta, *pci_ddrio, *pci_ras;
+ struct pci_dev *pci_sad0, *pci_sad1, *pci_ha0;
+ struct pci_dev *pci_br;
+ struct pci_dev *pci_tad[NUM_CHANNELS];
+
+ struct sbridge_dev *sbridge_dev;
+
+ struct sbridge_info info;
+ struct sbridge_channel channel[NUM_CHANNELS];
+
+ int csrow_map[NUM_CHANNELS][MAX_DIMMS];
+
+ /* Memory type detection */
+ bool is_mirrored, is_lockstep, is_close_pg;
+
+ /* Fifo double buffers */
+ struct mce mce_entry[MCE_LOG_LEN];
+ struct mce mce_outentry[MCE_LOG_LEN];
+
+ /* Fifo in/out counters */
+ unsigned mce_in, mce_out;
+
+ /* Count indicator to show errors not got */
+ unsigned mce_overrun;
+
+ /* Memory description */
+ u64 tolm, tohm;
+};
+
+#define PCI_DESCR(device, function, device_id) \
+ .dev = (device), \
+ .func = (function), \
+ .dev_id = (device_id)
+
+static const struct pci_id_descr pci_dev_descr_sbridge[] = {
+ /* Processor Home Agent */
+ { PCI_DESCR(14, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0) },
+
+ /* Memory controller */
+ { PCI_DESCR(15, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA) },
+ { PCI_DESCR(15, 1, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS) },
+ { PCI_DESCR(15, 2, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0) },
+ { PCI_DESCR(15, 3, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1) },
+ { PCI_DESCR(15, 4, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2) },
+ { PCI_DESCR(15, 5, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3) },
+ { PCI_DESCR(17, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO) },
+
+ /* System Address Decoder */
+ { PCI_DESCR(12, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0) },
+ { PCI_DESCR(12, 7, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1) },
+
+ /* Broadcast Registers */
+ { PCI_DESCR(13, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_BR) },
+};
+
+#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
+static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
+ PCI_ID_TABLE_ENTRY(pci_dev_descr_sbridge),
+ {0,} /* 0 terminated list. */
+};
+
+/*
+ * pci_device_id table for which devices we are looking for
+ */
+static const struct pci_device_id sbridge_pci_tbl[] __devinitdata = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)},
+ {0,} /* 0 terminated list. */
+};
+
+
+/****************************************************************************
+ Anciliary status routines
+ ****************************************************************************/
+
+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);
+ return -EINVAL;
+ }
+
+ return ranks;
+}
+
+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);
+ return -EINVAL;
+ }
+
+ return 1 << rows;
+}
+
+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);
+ return -EINVAL;
+ }
+
+ return 1 << cols;
+}
+
+static struct sbridge_dev *get_sbridge_dev(u8 bus)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
+ if (sbridge_dev->bus == bus)
+ return sbridge_dev;
+ }
+
+ return NULL;
+}
+
+static struct sbridge_dev *alloc_sbridge_dev(u8 bus,
+ const struct pci_id_table *table)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ sbridge_dev = kzalloc(sizeof(*sbridge_dev), GFP_KERNEL);
+ if (!sbridge_dev)
+ return NULL;
+
+ sbridge_dev->pdev = kzalloc(sizeof(*sbridge_dev->pdev) * table->n_devs,
+ GFP_KERNEL);
+ if (!sbridge_dev->pdev) {
+ kfree(sbridge_dev);
+ return NULL;
+ }
+
+ sbridge_dev->bus = bus;
+ sbridge_dev->n_devs = table->n_devs;
+ list_add_tail(&sbridge_dev->list, &sbridge_edac_list);
+
+ return sbridge_dev;
+}
+
+static void free_sbridge_dev(struct sbridge_dev *sbridge_dev)
+{
+ list_del(&sbridge_dev->list);
+ kfree(sbridge_dev->pdev);
+ kfree(sbridge_dev);
+}
+
+/****************************************************************************
+ Memory check routines
+ ****************************************************************************/
+static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot,
+ unsigned func)
+{
+ struct sbridge_dev *sbridge_dev = get_sbridge_dev(bus);
+ int i;
+
+ if (!sbridge_dev)
+ return NULL;
+
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ if (!sbridge_dev->pdev[i])
+ continue;
+
+ 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]);
+ return sbridge_dev->pdev[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * sbridge_get_active_channels() - gets the number of channels and csrows
+ * bus: Device bus
+ * @channels: Number of channels that will be returned
+ * @csrows: Number of csrows found
+ *
+ * Since EDAC core needs to know in advance the number of available channels
+ * and csrows, in order to allocate memory for csrows/channels, it is needed
+ * to run two similar steps. At the first step, implemented on this function,
+ * it checks the number of csrows/channels present at one socket, identified
+ * by the associated PCI bus.
+ * this is used in order to properly allocate the size of mci components.
+ * Note: one csrow is one dimm.
+ */
+static int sbridge_get_active_channels(const u8 bus, unsigned *channels,
+ unsigned *csrows)
+{
+ struct pci_dev *pdev = NULL;
+ int i, j;
+ u32 mcmtr;
+
+ *channels = 0;
+ *csrows = 0;
+
+ pdev = get_pdev_slot_func(bus, 15, 0);
+ if (!pdev) {
+ sbridge_printk(KERN_ERR, "Couldn't find PCI device "
+ "%2x.%02d.%d!!!\n",
+ bus, 15, 0);
+ return -ENODEV;
+ }
+
+ pci_read_config_dword(pdev, MCMTR, &mcmtr);
+ if (!IS_ECC_ENABLED(mcmtr)) {
+ sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ u32 mtr;
+
+ /* Device 15 functions 2 - 5 */
+ pdev = get_pdev_slot_func(bus, 15, 2 + i);
+ if (!pdev) {
+ sbridge_printk(KERN_ERR, "Couldn't find PCI device "
+ "%2x.%02d.%d!!!\n",
+ bus, 15, 2 + i);
+ return -ENODEV;
+ }
+ (*channels)++;
+
+ for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+ pci_read_config_dword(pdev, mtr_regs[j], &mtr);
+ debugf1("Bus#%02x channel #%d MTR%d = %x\n", bus, i, j, mtr);
+ if (IS_DIMM_PRESENT(mtr))
+ (*csrows)++;
+ }
+ }
+
+ debugf0("Number of active channels: %d, number of active dimms: %d\n",
+ *channels, *csrows);
+
+ return 0;
+}
+
+static int get_dimm_config(const struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ struct csrow_info *csr;
+ int i, j, banks, ranks, rows, cols, size, npages;
+ int csrow = 0;
+ unsigned long last_page = 0;
+ u32 reg;
+ enum edac_type mode;
+ enum mem_type mtype;
+
+ pci_read_config_dword(pvt->pci_br, SAD_TARGET, &reg);
+ pvt->sbridge_dev->source_id = SOURCE_ID(reg);
+
+ 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);
+
+ pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
+ if (IS_MIRROR_ENABLED(reg)) {
+ debugf0("Memory mirror is enabled\n");
+ pvt->is_mirrored = true;
+ } else {
+ debugf0("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");
+ mode = EDAC_S8ECD8ED;
+ pvt->is_lockstep = true;
+ } else {
+ debugf0("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");
+ pvt->is_close_pg = true;
+ } else {
+ debugf0("address map is on open page mode\n");
+ pvt->is_close_pg = false;
+ }
+
+ pci_read_config_dword(pvt->pci_ta, RANK_CFG_A, &reg);
+ if (IS_RDIMM_ENABLED(reg)) {
+ /* FIXME: Can also be LRDIMM */
+ debugf0("Memory is registered\n");
+ mtype = MEM_RDDR3;
+ } else {
+ debugf0("Memory is unregistered\n");
+ mtype = MEM_DDR3;
+ }
+
+ /* On all supported DDR3 DIMM types, there are 8 banks available */
+ banks = 8;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ u32 mtr;
+
+ for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ mtr_regs[j], &mtr);
+ debugf4("Channel #%d MTR%d = %x\n", i, j, mtr);
+ if (IS_DIMM_PRESENT(mtr)) {
+ pvt->channel[i].dimms++;
+
+ ranks = numrank(mtr);
+ rows = numrow(mtr);
+ cols = numcol(mtr);
+
+ /* DDR3 has 8 I/O banks */
+ 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);
+ csr = &mci->csrows[csrow];
+
+ csr->first_page = last_page;
+ csr->last_page = last_page + npages - 1;
+ csr->page_mask = 0UL; /* Unused */
+ csr->nr_pages = npages;
+ csr->grain = 32;
+ csr->csrow_idx = csrow;
+ csr->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
+ csr->ce_count = 0;
+ csr->ue_count = 0;
+ csr->mtype = mtype;
+ csr->edac_mode = mode;
+ csr->nr_channels = 1;
+ csr->channels[0].chan_idx = i;
+ csr->channels[0].ce_count = 0;
+ pvt->csrow_map[i][j] = csrow;
+ snprintf(csr->channels[0].label,
+ sizeof(csr->channels[0].label),
+ "CPU_SrcID#%u_Channel#%u_DIMM#%u",
+ pvt->sbridge_dev->source_id, i, j);
+ last_page += npages;
+ csrow++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void get_memory_layout(const struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ int i, j, k, n_sads, n_tads, sad_interl;
+ u32 reg;
+ u64 limit, prv = 0;
+ u64 tmp_mb;
+ u32 rir_way;
+
+ /*
+ * Step 1) Get TOLM/TOHM ranges
+ */
+
+ /* Address range is 32:28 */
+ pci_read_config_dword(pvt->pci_sad1, TOLM,
+ &reg);
+ pvt->tolm = GET_TOLM(reg);
+ tmp_mb = (1 + pvt->tolm) >> 20;
+
+ debugf0("TOLM: %Lu.%03Lu GB (0x%016Lx)\n",
+ tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tolm);
+
+ /* Address range is already 45:25 */
+ pci_read_config_dword(pvt->pci_sad1, TOHM,
+ &reg);
+ pvt->tohm = GET_TOHM(reg);
+ tmp_mb = (1 + pvt->tohm) >> 20;
+
+ debugf0("TOHM: %Lu.%03Lu GB (0x%016Lx)",
+ tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tohm);
+
+ /*
+ * Step 2) Get SAD range and SAD Interleave list
+ * TAD registers contain the interleave wayness. However, it
+ * seems simpler to just discover it indirectly, with the
+ * algorithm bellow.
+ */
+ prv = 0;
+ for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
+ /* SAD_LIMIT Address range is 45:26 */
+ pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+ &reg);
+ limit = SAD_LIMIT(reg);
+
+ if (!DRAM_RULE_ENABLE(reg))
+ continue;
+
+ if (limit <= prv)
+ break;
+
+ tmp_mb = (limit + 1) >> 20;
+ debugf0("SAD#%d %s up to %Lu.%03Lu GB (0x%016Lx) %s reg=0x%08x\n",
+ n_sads,
+ get_dram_attr(reg),
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
+ reg);
+ prv = limit;
+
+ pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+ &reg);
+ sad_interl = sad_pkg(reg, 0);
+ for (j = 0; j < 8; j++) {
+ if (j > 0 && sad_interl == sad_pkg(reg, j))
+ break;
+
+ debugf0("SAD#%d, interleave #%d: %d\n",
+ n_sads, j, sad_pkg(reg, j));
+ }
+ }
+
+ /*
+ * Step 3) Get TAD range
+ */
+ prv = 0;
+ for (n_tads = 0; n_tads < MAX_TAD; n_tads++) {
+ pci_read_config_dword(pvt->pci_ha0, tad_dram_rule[n_tads],
+ &reg);
+ limit = TAD_LIMIT(reg);
+ if (limit <= prv)
+ break;
+ tmp_mb = (limit + 1) >> 20;
+
+ debugf0("TAD#%d: up to %Lu.%03Lu GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
+ n_tads, tmp_mb / 1000, tmp_mb % 1000,
+ ((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 = tmp_mb;
+ }
+
+ /*
+ * Step 4) Get TAD offsets, per each channel
+ */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->channel[i].dimms)
+ continue;
+ for (j = 0; j < n_tads; j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ tad_ch_nilv_offset[j],
+ &reg);
+ tmp_mb = TAD_OFFSET(reg) >> 20;
+ debugf0("TAD CH#%d, offset #%d: %Lu.%03Lu GB (0x%016Lx), reg=0x%08x\n",
+ i, j,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ reg);
+ }
+ }
+
+ /*
+ * Step 6) Get RIR Wayness/Limit, per each channel
+ */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->channel[i].dimms)
+ continue;
+ for (j = 0; j < MAX_RIR_RANGES; j++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ rir_way_limit[j],
+ &reg);
+
+ if (!IS_RIR_VALID(reg))
+ continue;
+
+ tmp_mb = RIR_LIMIT(reg) >> 20;
+ rir_way = 1 << RIR_WAY(reg);
+ debugf0("CH#%d RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d, reg=0x%08x\n",
+ i, j,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ rir_way,
+ reg);
+
+ for (k = 0; k < rir_way; k++) {
+ pci_read_config_dword(pvt->pci_tad[i],
+ rir_offset[j][k],
+ &reg);
+ tmp_mb = RIR_OFFSET(reg) << 6;
+
+ debugf0("CH#%d RIR#%d INTL#%d, offset %Lu.%03Lu GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
+ i, j, k,
+ tmp_mb / 1000, tmp_mb % 1000,
+ ((u64)tmp_mb) << 20L,
+ (u32)RIR_RNK_TGT(reg),
+ reg);
+ }
+ }
+ }
+}
+
+struct mem_ctl_info *get_mci_for_node_id(u8 node_id)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
+ if (sbridge_dev->node_id == node_id)
+ return sbridge_dev->mci;
+ }
+ return NULL;
+}
+
+static int get_memory_error_data(struct mem_ctl_info *mci,
+ u64 addr,
+ u8 *socket,
+ long *channel_mask,
+ u8 *rank,
+ char *area_type)
+{
+ struct mem_ctl_info *new_mci;
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ char msg[256];
+ int n_rir, n_sads, n_tads, sad_way, sck_xch;
+ int sad_interl, idx, base_ch;
+ int interleave_mode;
+ unsigned sad_interleave[MAX_INTERLEAVE];
+ u32 reg;
+ u8 ch_way,sck_way;
+ u32 tad_offset;
+ u32 rir_way;
+ u64 ch_addr, offset, limit, prv = 0;
+
+
+ /*
+ * Step 0) Check if the address is at special memory ranges
+ * The check bellow is probably enough to fill all cases where
+ * the error is not inside a memory, except for the legacy
+ * range (e. g. VGA addresses). It is unlikely, however, that the
+ * memory controller would generate an error on that range.
+ */
+ if ((addr > (u64) pvt->tolm) && (addr < (1L << 32))) {
+ sprintf(msg, "Error at TOLM area, on addr 0x%08Lx", addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr >= (u64)pvt->tohm) {
+ sprintf(msg, "Error at MMIOH area, on addr 0x%016Lx", addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+
+ /*
+ * Step 1) Get socket
+ */
+ for (n_sads = 0; n_sads < MAX_SAD; n_sads++) {
+ pci_read_config_dword(pvt->pci_sad0, dram_rule[n_sads],
+ &reg);
+
+ if (!DRAM_RULE_ENABLE(reg))
+ continue;
+
+ limit = SAD_LIMIT(reg);
+ if (limit <= prv) {
+ sprintf(msg, "Can't discover the memory socket");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr <= limit)
+ break;
+ prv = limit;
+ }
+ if (n_sads == MAX_SAD) {
+ sprintf(msg, "Can't discover the memory socket");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ area_type = get_dram_attr(reg);
+ interleave_mode = INTERLEAVE_MODE(reg);
+
+ pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
+ &reg);
+ sad_interl = sad_pkg(reg, 0);
+ for (sad_way = 0; sad_way < 8; sad_way++) {
+ 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]);
+ }
+ 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(reg) ? "" : "XOR[18:16]");
+ if (interleave_mode)
+ idx = ((addr >> 6) ^ (addr >> 16)) & 7;
+ else
+ idx = (addr >> 6) & 7;
+ switch (sad_way) {
+ case 1:
+ idx = 0;
+ break;
+ case 2:
+ idx = idx & 1;
+ break;
+ case 4:
+ idx = idx & 3;
+ break;
+ case 8:
+ break;
+ default:
+ sprintf(msg, "Can't discover socket interleave");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ *socket = sad_interleave[idx];
+ debugf0("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
+ * right PCI registers
+ */
+ new_mci = get_mci_for_node_id(*socket);
+ if (!new_mci) {
+ sprintf(msg, "Struct for socket #%u wasn't initialized",
+ *socket);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ mci = new_mci;
+ pvt = mci->pvt_info;
+
+ /*
+ * Step 2) Get memory channel
+ */
+ prv = 0;
+ for (n_tads = 0; n_tads < MAX_TAD; n_tads++) {
+ pci_read_config_dword(pvt->pci_ha0, tad_dram_rule[n_tads],
+ &reg);
+ limit = TAD_LIMIT(reg);
+ if (limit <= prv) {
+ sprintf(msg, "Can't discover the memory channel");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ if (addr <= limit)
+ break;
+ prv = limit;
+ }
+ ch_way = TAD_CH(reg) + 1;
+ sck_way = TAD_SOCK(reg) + 1;
+ /*
+ * FIXME: Is it right to always use channel 0 for offsets?
+ */
+ pci_read_config_dword(pvt->pci_tad[0],
+ tad_ch_nilv_offset[n_tads],
+ &tad_offset);
+
+ if (ch_way == 3)
+ idx = addr >> 6;
+ else
+ idx = addr >> (6 + sck_way);
+ idx = idx % ch_way;
+
+ /*
+ * FIXME: Shouldn't we use CHN_IDX_OFFSET() here, when ch_way == 3 ???
+ */
+ switch (idx) {
+ case 0:
+ base_ch = TAD_TGT0(reg);
+ break;
+ case 1:
+ base_ch = TAD_TGT1(reg);
+ break;
+ case 2:
+ base_ch = TAD_TGT2(reg);
+ break;
+ case 3:
+ base_ch = TAD_TGT3(reg);
+ break;
+ default:
+ sprintf(msg, "Can't discover the TAD target");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ *channel_mask = 1 << base_ch;
+
+ if (pvt->is_mirrored) {
+ *channel_mask |= 1 << ((base_ch + 2) % 4);
+ switch(ch_way) {
+ case 2:
+ case 4:
+ sck_xch = 1 << sck_way * (ch_way >> 1);
+ break;
+ default:
+ sprintf(msg, "Invalid mirror set. Can't decode addr");
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ } else
+ sck_xch = (1 << sck_way) * ch_way;
+
+ if (pvt->is_lockstep)
+ *channel_mask |= 1 << ((base_ch + 1) % 4);
+
+ 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);
+
+ /* Calculate channel address */
+ /* Remove the TAD offset */
+
+ if (offset > addr) {
+ sprintf(msg, "Can't calculate ch addr: TAD offset 0x%08Lx is too high for addr 0x%08Lx!",
+ offset, addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ addr -= offset;
+ /* Store the low bits [0:6] of the addr */
+ ch_addr = addr & 0x7f;
+ /* Remove socket wayness and remove 6 bits */
+ addr >>= 6;
+ addr /= sck_xch;
+#if 0
+ /* Divide by channel way */
+ addr = addr / ch_way;
+#endif
+ /* Recover the last 6 bits */
+ ch_addr |= addr << 6;
+
+ /*
+ * Step 3) Decode rank
+ */
+ for (n_rir = 0; n_rir < MAX_RIR_RANGES; n_rir++) {
+ pci_read_config_dword(pvt->pci_tad[base_ch],
+ rir_way_limit[n_rir],
+ &reg);
+
+ if (!IS_RIR_VALID(reg))
+ continue;
+
+ limit = RIR_LIMIT(reg);
+
+ debugf0("RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d\n",
+ n_rir,
+ (limit >> 20) / 1000, (limit >> 20) % 1000,
+ limit,
+ 1 << RIR_WAY(reg));
+ if (ch_addr <= limit)
+ break;
+ }
+ if (n_rir == MAX_RIR_RANGES) {
+ sprintf(msg, "Can't discover the memory rank for ch addr 0x%08Lx",
+ ch_addr);
+ edac_mc_handle_ce_no_info(mci, msg);
+ return -EINVAL;
+ }
+ rir_way = RIR_WAY(reg);
+ if (pvt->is_close_pg)
+ idx = (ch_addr >> 6);
+ else
+ idx = (ch_addr >> 13); /* FIXME: Datasheet says to shift by 15 */
+ idx %= 1 << rir_way;
+
+ pci_read_config_dword(pvt->pci_tad[base_ch],
+ rir_offset[n_rir][idx],
+ &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);
+
+ return 0;
+}
+
+/****************************************************************************
+ Device initialization routines: put/get, init/exit
+ ****************************************************************************/
+
+/*
+ * sbridge_put_all_devices 'put' all the devices that we have
+ * reserved via 'get'
+ */
+static void sbridge_put_devices(struct sbridge_dev *sbridge_dev)
+{
+ int i;
+
+ debugf0(__FILE__ ": %s()\n", __func__);
+ 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));
+ pci_dev_put(pdev);
+ }
+}
+
+static void sbridge_put_all_devices(void)
+{
+ struct sbridge_dev *sbridge_dev, *tmp;
+
+ list_for_each_entry_safe(sbridge_dev, tmp, &sbridge_edac_list, list) {
+ sbridge_put_devices(sbridge_dev);
+ free_sbridge_dev(sbridge_dev);
+ }
+}
+
+/*
+ * sbridge_get_all_devices Find and perform 'get' operation on the MCH's
+ * device/functions we want to reference for this driver
+ *
+ * Need to 'get' device 16 func 1 and func 2
+ */
+static int sbridge_get_onedevice(struct pci_dev **prev,
+ u8 *num_mc,
+ const struct pci_id_table *table,
+ const unsigned devno)
+{
+ struct sbridge_dev *sbridge_dev;
+ const struct pci_id_descr *dev_descr = &table->descr[devno];
+
+ struct pci_dev *pdev = NULL;
+ u8 bus = 0;
+
+ sbridge_printk(KERN_INFO,
+ "Seeking for: dev %02x.%d PCI ID %04x:%04x\n",
+ dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ dev_descr->dev_id, *prev);
+
+ if (!pdev) {
+ if (*prev) {
+ *prev = pdev;
+ return 0;
+ }
+
+ if (dev_descr->optional)
+ return 0;
+
+ if (devno == 0)
+ return -ENODEV;
+
+ sbridge_printk(KERN_INFO,
+ "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
+ dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+
+ /* End of list, leave */
+ return -ENODEV;
+ }
+ bus = pdev->bus->number;
+
+ sbridge_dev = get_sbridge_dev(bus);
+ if (!sbridge_dev) {
+ sbridge_dev = alloc_sbridge_dev(bus, table);
+ if (!sbridge_dev) {
+ pci_dev_put(pdev);
+ return -ENOMEM;
+ }
+ (*num_mc)++;
+ }
+
+ if (sbridge_dev->pdev[devno]) {
+ sbridge_printk(KERN_ERR,
+ "Duplicated device for "
+ "dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ pci_dev_put(pdev);
+ return -ENODEV;
+ }
+
+ sbridge_dev->pdev[devno] = pdev;
+
+ /* Sanity check */
+ if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
+ PCI_FUNC(pdev->devfn) != dev_descr->func)) {
+ sbridge_printk(KERN_ERR,
+ "Device PCI ID %04x:%04x "
+ "has dev %02x:%d.%d instead of dev %02x:%02x.%d\n",
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
+ bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ bus, dev_descr->dev, dev_descr->func);
+ return -ENODEV;
+ }
+
+ /* Be sure that the device is enabled */
+ if (unlikely(pci_enable_device(pdev) < 0)) {
+ sbridge_printk(KERN_ERR,
+ "Couldn't enable "
+ "dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ 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);
+
+ /*
+ * As stated on drivers/pci/search.c, the reference count for
+ * @from is always decremented if it is not %NULL. So, as we need
+ * to get all devices up to null, we need to do a get for the device
+ */
+ pci_dev_get(pdev);
+
+ *prev = pdev;
+
+ return 0;
+}
+
+static int sbridge_get_all_devices(u8 *num_mc)
+{
+ int i, rc;
+ struct pci_dev *pdev = NULL;
+ const struct pci_id_table *table = pci_dev_descr_sbridge_table;
+
+ while (table && table->descr) {
+ for (i = 0; i < table->n_devs; i++) {
+ pdev = NULL;
+ do {
+ rc = sbridge_get_onedevice(&pdev, num_mc,
+ table, i);
+ if (rc < 0) {
+ if (i == 0) {
+ i = table->n_devs;
+ break;
+ }
+ sbridge_put_all_devices();
+ return -ENODEV;
+ }
+ } while (pdev);
+ }
+ table++;
+ }
+
+ return 0;
+}
+
+static int mci_bind_devs(struct mem_ctl_info *mci,
+ struct sbridge_dev *sbridge_dev)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ int i, func, slot;
+
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ pdev = sbridge_dev->pdev[i];
+ if (!pdev)
+ continue;
+ slot = PCI_SLOT(pdev->devfn);
+ func = PCI_FUNC(pdev->devfn);
+ switch (slot) {
+ case 12:
+ switch (func) {
+ case 6:
+ pvt->pci_sad0 = pdev;
+ break;
+ case 7:
+ pvt->pci_sad1 = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 13:
+ switch (func) {
+ case 6:
+ pvt->pci_br = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 14:
+ switch (func) {
+ case 0:
+ pvt->pci_ha0 = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 15:
+ switch (func) {
+ case 0:
+ pvt->pci_ta = pdev;
+ break;
+ case 1:
+ pvt->pci_ras = pdev;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ pvt->pci_tad[func - 2] = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case 17:
+ switch (func) {
+ case 0:
+ pvt->pci_ddrio = pdev;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+
+ debugf0("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 */
+ if (!pvt->pci_sad0 || !pvt->pci_sad1 || !pvt->pci_ha0 ||
+ !pvt-> pci_tad || !pvt->pci_ras || !pvt->pci_ta ||
+ !pvt->pci_ddrio)
+ goto enodev;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!pvt->pci_tad[i])
+ goto enodev;
+ }
+ return 0;
+
+enodev:
+ sbridge_printk(KERN_ERR, "Some needed devices are missing\n");
+ return -ENODEV;
+
+error:
+ sbridge_printk(KERN_ERR, "Device %d, function %d "
+ "is out of the expected range\n",
+ slot, func);
+ return -EINVAL;
+}
+
+/****************************************************************************
+ Error check routines
+ ****************************************************************************/
+
+/*
+ * While Sandy Bridge has error count registers, SMI BIOS read values from
+ * and resets the counters. So, they are not reliable for the OS to read
+ * from them. So, we have no option but to just trust on whatever MCE is
+ * telling us about the errors.
+ */
+static void sbridge_mce_output_error(struct mem_ctl_info *mci,
+ const struct mce *m)
+{
+ struct mem_ctl_info *new_mci;
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ char *type, *optype, *msg, *recoverable_msg;
+ bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
+ bool overflow = GET_BITFIELD(m->status, 62, 62);
+ bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
+ bool recoverable = GET_BITFIELD(m->status, 56, 56);
+ u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
+ u32 mscod = GET_BITFIELD(m->status, 16, 31);
+ u32 errcode = GET_BITFIELD(m->status, 0, 15);
+ u32 channel = GET_BITFIELD(m->status, 0, 3);
+ u32 optypenum = GET_BITFIELD(m->status, 4, 6);
+ long channel_mask, first_channel;
+ u8 rank, socket;
+ int csrow, rc, dimm;
+ char *area_type = "Unknown";
+
+ if (ripv)
+ type = "NON_FATAL";
+ else
+ type = "FATAL";
+
+ /*
+ * According with Table 15-9 of the Intel Archictecture spec vol 3A,
+ * memory errors should fit in this mask:
+ * 000f 0000 1mmm cccc (binary)
+ * where:
+ * f = Correction Report Filtering Bit. If 1, subsequent errors
+ * won't be shown
+ * mmm = error type
+ * cccc = channel
+ * If the mask doesn't match, report an error to the parsing logic
+ */
+ if (! ((errcode & 0xef80) == 0x80)) {
+ optype = "Can't parse: it is not a mem";
+ } else {
+ switch (optypenum) {
+ case 0:
+ optype = "generic undef request";
+ break;
+ case 1:
+ optype = "memory read";
+ break;
+ case 2:
+ optype = "memory write";
+ break;
+ case 3:
+ optype = "addr/cmd";
+ break;
+ case 4:
+ optype = "memory scrubbing";
+ break;
+ default:
+ optype = "reserved";
+ break;
+ }
+ }
+
+ rc = get_memory_error_data(mci, m->addr, &socket,
+ &channel_mask, &rank, area_type);
+ if (rc < 0)
+ return;
+ new_mci = get_mci_for_node_id(socket);
+ if (!new_mci) {
+ edac_mc_handle_ce_no_info(mci, "Error: socket got corrupted!");
+ return;
+ }
+ mci = new_mci;
+ pvt = mci->pvt_info;
+
+ first_channel = find_first_bit(&channel_mask, NUM_CHANNELS);
+
+ if (rank < 4)
+ dimm = 0;
+ else if (rank < 8)
+ dimm = 1;
+ else
+ dimm = 2;
+
+ csrow = pvt->csrow_map[first_channel][dimm];
+
+ if (uncorrected_error && recoverable)
+ recoverable_msg = " recoverable";
+ else
+ recoverable_msg = "";
+
+ /*
+ * FIXME: What should we do with "channel" information on mcelog?
+ * Probably, we can just discard it, as the channel information
+ * comes from the get_memory_error_data() address decoding
+ */
+ msg = kasprintf(GFP_ATOMIC,
+ "%d %s error(s): %s on %s area %s%s: cpu=%d Err=%04x:%04x (ch=%d), "
+ "addr = 0x%08llx => socket=%d, Channel=%ld(mask=%ld), rank=%d\n",
+ core_err_cnt,
+ area_type,
+ optype,
+ type,
+ recoverable_msg,
+ overflow ? "OVERFLOW" : "",
+ m->cpu,
+ mscod, errcode,
+ channel, /* 1111b means not specified */
+ (long long) m->addr,
+ socket,
+ first_channel, /* This is the real channel on SB */
+ channel_mask,
+ rank);
+
+ debugf0("%s", msg);
+
+ /* Call the helper to output message */
+ if (uncorrected_error)
+ edac_mc_handle_fbd_ue(mci, csrow, 0, 0, msg);
+ else
+ edac_mc_handle_fbd_ce(mci, csrow, 0, msg);
+
+ kfree(msg);
+}
+
+/*
+ * sbridge_check_error Retrieve and process errors reported by the
+ * hardware. Called by the Core module.
+ */
+static void sbridge_check_error(struct mem_ctl_info *mci)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ int i;
+ unsigned count = 0;
+ struct mce *m;
+
+ /*
+ * MCE first step: Copy all mce errors into a temporary buffer
+ * We use a double buffering here, to reduce the risk of
+ * loosing an error.
+ */
+ smp_rmb();
+ count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
+ % MCE_LOG_LEN;
+ if (!count)
+ return;
+
+ m = pvt->mce_outentry;
+ if (pvt->mce_in + count > MCE_LOG_LEN) {
+ unsigned l = MCE_LOG_LEN - pvt->mce_in;
+
+ memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
+ smp_wmb();
+ pvt->mce_in = 0;
+ count -= l;
+ m += l;
+ }
+ memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
+ smp_wmb();
+ pvt->mce_in += count;
+
+ smp_rmb();
+ if (pvt->mce_overrun) {
+ sbridge_printk(KERN_ERR, "Lost %d memory errors\n",
+ pvt->mce_overrun);
+ smp_wmb();
+ pvt->mce_overrun = 0;
+ }
+
+ /*
+ * MCE second step: parse errors and display
+ */
+ for (i = 0; i < count; i++)
+ sbridge_mce_output_error(mci, &pvt->mce_outentry[i]);
+}
+
+/*
+ * sbridge_mce_check_error Replicates mcelog routine to get errors
+ * This routine simply queues mcelog errors, and
+ * return. The error itself should be handled later
+ * by sbridge_check_error.
+ * WARNING: As this routine should be called at NMI time, extra care should
+ * be taken to avoid deadlocks, and to be as fast as possible.
+ */
+static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct mce *mce = (struct mce *)data;
+ struct mem_ctl_info *mci;
+ struct sbridge_pvt *pvt;
+
+ mci = get_mci_for_node_id(mce->socketid);
+ if (!mci)
+ return NOTIFY_BAD;
+ pvt = mci->pvt_info;
+
+ /*
+ * Just let mcelog handle it if the error is
+ * outside the memory controller. A memory error
+ * is indicated by bit 7 = 1 and bits = 8-11,13-15 = 0.
+ * bit 12 has an special meaning.
+ */
+ if ((mce->status & 0xefff) >> 7 != 1)
+ return NOTIFY_DONE;
+
+ printk("sbridge: HANDLING MCE MEMORY ERROR\n");
+
+ printk("CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
+ mce->extcpu, mce->mcgstatus, mce->bank, mce->status);
+ printk("TSC %llx ", mce->tsc);
+ printk("ADDR %llx ", mce->addr);
+ printk("MISC %llx ", mce->misc);
+
+ printk("PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
+ mce->cpuvendor, mce->cpuid, mce->time,
+ mce->socketid, mce->apicid);
+
+#ifdef CONFIG_SMP
+ /* Only handle if it is the right mc controller */
+ if (cpu_data(mce->cpu).phys_proc_id != pvt->sbridge_dev->mc)
+ return NOTIFY_DONE;
+#endif
+
+ smp_rmb();
+ if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
+ smp_wmb();
+ pvt->mce_overrun++;
+ return NOTIFY_DONE;
+ }
+
+ /* Copy memory error at the ringbuffer */
+ memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
+ smp_wmb();
+ pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
+
+ /* Handle fatal errors immediately */
+ if (mce->mcgstatus & 1)
+ sbridge_check_error(mci);
+
+ /* Advice mcelog that the error were handled */
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block sbridge_mce_dec = {
+ .notifier_call = sbridge_mce_check_error,
+};
+
+/****************************************************************************
+ EDAC register/unregister logic
+ ****************************************************************************/
+
+static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
+{
+ struct mem_ctl_info *mci = sbridge_dev->mci;
+ struct sbridge_pvt *pvt;
+
+ if (unlikely(!mci || !mci->pvt_info)) {
+ debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
+ __func__, &sbridge_dev->pdev[0]->dev);
+
+ sbridge_printk(KERN_ERR, "Couldn't find mci handler\n");
+ return;
+ }
+
+ pvt = mci->pvt_info;
+
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &sbridge_dev->pdev[0]->dev);
+
+ atomic_notifier_chain_unregister(&x86_mce_decoder_chain,
+ &sbridge_mce_dec);
+
+ /* Remove MC sysfs nodes */
+ edac_mc_del_mc(mci->dev);
+
+ debugf1("%s: free mci struct\n", mci->ctl_name);
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ sbridge_dev->mci = NULL;
+}
+
+static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
+{
+ struct mem_ctl_info *mci;
+ struct sbridge_pvt *pvt;
+ int rc, channels, csrows;
+
+ /* Check the number of active and not disabled channels */
+ rc = sbridge_get_active_channels(sbridge_dev->bus, &channels, &csrows);
+ if (unlikely(rc < 0))
+ return rc;
+
+ /* allocate a new MC control structure */
+ mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, sbridge_dev->mc);
+ if (unlikely(!mci))
+ return -ENOMEM;
+
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &sbridge_dev->pdev[0]->dev);
+
+ pvt = mci->pvt_info;
+ memset(pvt, 0, sizeof(*pvt));
+
+ /* Associate sbridge_dev and mci for future usage */
+ pvt->sbridge_dev = sbridge_dev;
+ sbridge_dev->mci = mci;
+
+ mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE;
+ mci->edac_cap = EDAC_FLAG_NONE;
+ mci->mod_name = "sbridge_edac.c";
+ mci->mod_ver = SBRIDGE_REVISION;
+ mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx);
+ mci->dev_name = pci_name(sbridge_dev->pdev[0]);
+ mci->ctl_page_to_phys = NULL;
+
+ /* Set the function pointer to an actual operation function */
+ mci->edac_check = sbridge_check_error;
+
+ /* Store pci devices at mci for faster access */
+ rc = mci_bind_devs(mci, sbridge_dev);
+ if (unlikely(rc < 0))
+ goto fail0;
+
+ /* Get dimm basic config and the memory layout */
+ get_dimm_config(mci);
+ get_memory_layout(mci);
+
+ /* record ptr to the generic device */
+ mci->dev = &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__);
+ rc = -EINVAL;
+ goto fail0;
+ }
+
+ atomic_notifier_chain_register(&x86_mce_decoder_chain,
+ &sbridge_mce_dec);
+ return 0;
+
+fail0:
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ sbridge_dev->mci = NULL;
+ return rc;
+}
+
+/*
+ * sbridge_probe Probe for ONE instance of device to see if it is
+ * present.
+ * return:
+ * 0 for FOUND a device
+ * < 0 for error code
+ */
+
+static int __devinit sbridge_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int rc;
+ u8 mc, num_mc = 0;
+ struct sbridge_dev *sbridge_dev;
+
+ /* get the pci devices we want to reserve for our use */
+ mutex_lock(&sbridge_edac_lock);
+
+ /*
+ * All memory controllers are allocated at the first pass.
+ */
+ if (unlikely(probed >= 1)) {
+ mutex_unlock(&sbridge_edac_lock);
+ return -ENODEV;
+ }
+ probed++;
+
+ rc = sbridge_get_all_devices(&num_mc);
+ if (unlikely(rc < 0))
+ goto fail0;
+ 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);
+ sbridge_dev->mc = mc++;
+ rc = sbridge_register_mci(sbridge_dev);
+ if (unlikely(rc < 0))
+ goto fail1;
+ }
+
+ sbridge_printk(KERN_INFO, "Driver loaded.\n");
+
+ mutex_unlock(&sbridge_edac_lock);
+ return 0;
+
+fail1:
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list)
+ sbridge_unregister_mci(sbridge_dev);
+
+ sbridge_put_all_devices();
+fail0:
+ mutex_unlock(&sbridge_edac_lock);
+ return rc;
+}
+
+/*
+ * sbridge_remove destructor for one instance of device
+ *
+ */
+static void __devexit sbridge_remove(struct pci_dev *pdev)
+{
+ struct sbridge_dev *sbridge_dev;
+
+ debugf0(__FILE__ ": %s()\n", __func__);
+
+ /*
+ * we have a trouble here: pdev value for removal will be wrong, since
+ * it will point to the X58 register used to detect that the machine
+ * is a Nehalem or upper design. However, due to the way several PCI
+ * devices are grouped together to provide MC functionality, we need
+ * to use a different method for releasing the devices
+ */
+
+ mutex_lock(&sbridge_edac_lock);
+
+ if (unlikely(!probed)) {
+ mutex_unlock(&sbridge_edac_lock);
+ return;
+ }
+
+ list_for_each_entry(sbridge_dev, &sbridge_edac_list, list)
+ sbridge_unregister_mci(sbridge_dev);
+
+ /* Release PCI resources */
+ sbridge_put_all_devices();
+
+ probed--;
+
+ mutex_unlock(&sbridge_edac_lock);
+}
+
+MODULE_DEVICE_TABLE(pci, sbridge_pci_tbl);
+
+/*
+ * sbridge_driver pci_driver structure for this module
+ *
+ */
+static struct pci_driver sbridge_driver = {
+ .name = "sbridge_edac",
+ .probe = sbridge_probe,
+ .remove = __devexit_p(sbridge_remove),
+ .id_table = sbridge_pci_tbl,
+};
+
+/*
+ * sbridge_init Module entry function
+ * Try to initialize this module for its devices
+ */
+static int __init sbridge_init(void)
+{
+ int pci_rc;
+
+ debugf2("MC: " __FILE__ ": %s()\n", __func__);
+
+ /* Ensure that the OPSTATE is set correctly for POLL or NMI */
+ opstate_init();
+
+ pci_rc = pci_register_driver(&sbridge_driver);
+
+ if (pci_rc >= 0)
+ return 0;
+
+ sbridge_printk(KERN_ERR, "Failed to register device with error %d.\n",
+ pci_rc);
+
+ return pci_rc;
+}
+
+/*
+ * sbridge_exit() Module exit function
+ * Unregister the driver
+ */
+static void __exit sbridge_exit(void)
+{
+ debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ pci_unregister_driver(&sbridge_driver);
+}
+
+module_init(sbridge_init);
+module_exit(sbridge_exit);
+
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("MC Driver for Intel Sandy Bridge memory controllers - "
+ SBRIDGE_REVISION);
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 334b82a3542c..855ab3f5936f 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -1046,8 +1046,8 @@ static void update_split_timeout(struct fw_card *card)
cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19);
- cycles = max(cycles, 800u); /* minimum as per the spec */
- cycles = min(cycles, 3u * 8000u); /* maximum OHCI timeout */
+ /* minimum per IEEE 1394, maximum which doesn't overflow OHCI */
+ cycles = clamp(cycles, 800u, 3u * 8000u);
card->split_timeout_cycles = cycles;
card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000);
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 03a7a85d0424..a20f45b1e7e5 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -502,11 +502,7 @@ static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
{
max_rec = min(max_rec, speed + 8);
- max_rec = min(max_rec, 0xbU); /* <= 4096 */
- if (max_rec < 8) {
- fw_notify("max_rec %x out of range\n", max_rec);
- max_rec = 8;
- }
+ max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */
return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
}
@@ -1125,17 +1121,12 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
unsigned u;
if (dev->local_fifo == FWNET_NO_FIFO_ADDR) {
- /* outside OHCI posted write area? */
- static const struct fw_address_region region = {
- .start = 0xffff00000000ULL,
- .end = CSR_REGISTER_BASE,
- };
-
dev->handler.length = 4096;
dev->handler.address_callback = fwnet_receive_packet;
dev->handler.callback_data = dev;
- retval = fw_core_add_address_handler(&dev->handler, &region);
+ retval = fw_core_add_address_handler(&dev->handler,
+ &fw_high_memory_region);
if (retval < 0)
goto failed_initial;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index fd7170a9ad2c..6628feaa7622 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -42,6 +42,7 @@
#include <linux/string.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
#include <asm/byteorder.h>
#include <asm/page.h>
@@ -125,6 +126,7 @@ struct context {
struct fw_ohci *ohci;
u32 regs;
int total_allocation;
+ u32 current_bus;
bool running;
bool flushing;
@@ -226,7 +228,7 @@ struct fw_ohci {
__le32 *self_id_cpu;
dma_addr_t self_id_bus;
- struct tasklet_struct bus_reset_tasklet;
+ struct work_struct bus_reset_work;
u32 self_id_buffer[512];
};
@@ -263,6 +265,8 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
#define PCI_DEVICE_ID_AGERE_FW643 0x5901
#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380
#define PCI_DEVICE_ID_TI_TSB12LV22 0x8009
+#define PCI_DEVICE_ID_TI_TSB12LV26 0x8020
+#define PCI_DEVICE_ID_TI_TSB82AA2 0x8025
#define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd
#define QUIRK_CYCLE_TIMER 1
@@ -270,6 +274,7 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
#define QUIRK_BE_HEADERS 4
#define QUIRK_NO_1394A 8
#define QUIRK_NO_MSI 16
+#define QUIRK_TI_SLLZ059 32
/* In case of multiple matches in ohci_quirks[], only the first one is used. */
static const struct {
@@ -299,6 +304,12 @@ static const struct {
{PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID,
QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A},
+ {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV26, PCI_ANY_ID,
+ QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
+
+ {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB82AA2, PCI_ANY_ID,
+ QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
+
{PCI_VENDOR_ID_TI, PCI_ANY_ID, PCI_ANY_ID,
QUIRK_RESET_PACKET},
@@ -315,6 +326,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0"
", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS)
", no 1394a enhancements = " __stringify(QUIRK_NO_1394A)
", disable MSI = " __stringify(QUIRK_NO_MSI)
+ ", TI SLLZ059 erratum = " __stringify(QUIRK_TI_SLLZ059)
")");
#define OHCI_PARAM_DEBUG_AT_AR 1
@@ -859,7 +871,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
*
* Alas some chips sometimes emit bus reset packets with a
* wrong generation. We set the correct generation for these
- * at a slightly incorrect time (in bus_reset_tasklet).
+ * at a slightly incorrect time (in bus_reset_work).
*/
if (evt == OHCI1394_evt_bus_reset) {
if (!(ohci->quirks & QUIRK_RESET_PACKET))
@@ -1046,6 +1058,7 @@ static void context_tasklet(unsigned long data)
address = le32_to_cpu(last->branch_address);
z = address & 0xf;
address &= ~0xf;
+ ctx->current_bus = address;
/* If the branch address points to a buffer outside of the
* current buffer, advance to the next buffer. */
@@ -1713,9 +1726,94 @@ static u32 update_bus_time(struct fw_ohci *ohci)
return ohci->bus_time | cycle_time_seconds;
}
-static void bus_reset_tasklet(unsigned long data)
+static int get_status_for_port(struct fw_ohci *ohci, int port_index)
+{
+ int reg;
+
+ mutex_lock(&ohci->phy_reg_mutex);
+ reg = write_phy_reg(ohci, 7, port_index);
+ if (reg >= 0)
+ reg = read_phy_reg(ohci, 8);
+ mutex_unlock(&ohci->phy_reg_mutex);
+ if (reg < 0)
+ return reg;
+
+ switch (reg & 0x0f) {
+ case 0x06:
+ return 2; /* is child node (connected to parent node) */
+ case 0x0e:
+ return 3; /* is parent node (connected to child node) */
+ }
+ return 1; /* not connected */
+}
+
+static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id,
+ int self_id_count)
+{
+ int i;
+ u32 entry;
+
+ for (i = 0; i < self_id_count; i++) {
+ entry = ohci->self_id_buffer[i];
+ if ((self_id & 0xff000000) == (entry & 0xff000000))
+ return -1;
+ if ((self_id & 0xff000000) < (entry & 0xff000000))
+ return i;
+ }
+ return i;
+}
+
+/*
+ * TI TSB82AA2B and TSB12LV26 do not receive the selfID of a locally
+ * attached TSB41BA3D phy; see http://www.ti.com/litv/pdf/sllz059.
+ * Construct the selfID from phy register contents.
+ * FIXME: How to determine the selfID.i flag?
+ */
+static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
+{
+ int reg, i, pos, status;
+ /* link active 1, speed 3, bridge 0, contender 1, more packets 0 */
+ u32 self_id = 0x8040c800;
+
+ reg = reg_read(ohci, OHCI1394_NodeID);
+ if (!(reg & OHCI1394_NodeID_idValid)) {
+ fw_notify("node ID not valid, new bus reset in progress\n");
+ return -EBUSY;
+ }
+ self_id |= ((reg & 0x3f) << 24); /* phy ID */
+
+ reg = ohci_read_phy_reg(&ohci->card, 4);
+ if (reg < 0)
+ return reg;
+ self_id |= ((reg & 0x07) << 8); /* power class */
+
+ reg = ohci_read_phy_reg(&ohci->card, 1);
+ if (reg < 0)
+ return reg;
+ self_id |= ((reg & 0x3f) << 16); /* gap count */
+
+ for (i = 0; i < 3; i++) {
+ status = get_status_for_port(ohci, i);
+ if (status < 0)
+ return status;
+ self_id |= ((status & 0x3) << (6 - (i * 2)));
+ }
+
+ pos = get_self_id_pos(ohci, self_id, self_id_count);
+ if (pos >= 0) {
+ memmove(&(ohci->self_id_buffer[pos+1]),
+ &(ohci->self_id_buffer[pos]),
+ (self_id_count - pos) * sizeof(*ohci->self_id_buffer));
+ ohci->self_id_buffer[pos] = self_id;
+ self_id_count++;
+ }
+ return self_id_count;
+}
+
+static void bus_reset_work(struct work_struct *work)
{
- struct fw_ohci *ohci = (struct fw_ohci *)data;
+ struct fw_ohci *ohci =
+ container_of(work, struct fw_ohci, bus_reset_work);
int self_id_count, i, j, reg;
int generation, new_generation;
unsigned long flags;
@@ -1753,21 +1851,50 @@ static void bus_reset_tasklet(unsigned long data)
* bit extra to get the actual number of self IDs.
*/
self_id_count = (reg >> 3) & 0xff;
- if (self_id_count == 0 || self_id_count > 252) {
+
+ if (self_id_count > 252) {
fw_notify("inconsistent self IDs\n");
return;
}
+
generation = (cond_le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff;
rmb();
for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1]) {
- fw_notify("inconsistent self IDs\n");
- return;
+ /*
+ * If the invalid data looks like a cycle start packet,
+ * it's likely to be the result of the cycle master
+ * having a wrong gap count. In this case, the self IDs
+ * so far are valid and should be processed so that the
+ * bus manager can then correct the gap count.
+ */
+ if (cond_le32_to_cpu(ohci->self_id_cpu[i])
+ == 0xffff008f) {
+ fw_notify("ignoring spurious self IDs\n");
+ self_id_count = j;
+ break;
+ } else {
+ fw_notify("inconsistent self IDs\n");
+ return;
+ }
}
ohci->self_id_buffer[j] =
cond_le32_to_cpu(ohci->self_id_cpu[i]);
}
+
+ if (ohci->quirks & QUIRK_TI_SLLZ059) {
+ self_id_count = find_and_insert_self_id(ohci, self_id_count);
+ if (self_id_count < 0) {
+ fw_notify("could not construct local self ID\n");
+ return;
+ }
+ }
+
+ if (self_id_count == 0) {
+ fw_notify("inconsistent self IDs\n");
+ return;
+ }
rmb();
/*
@@ -1887,7 +2014,7 @@ static irqreturn_t irq_handler(int irq, void *data)
log_irqs(event);
if (event & OHCI1394_selfIDComplete)
- tasklet_schedule(&ohci->bus_reset_tasklet);
+ queue_work(fw_workqueue, &ohci->bus_reset_work);
if (event & OHCI1394_RQPkt)
tasklet_schedule(&ohci->ar_request_ctx.tasklet);
@@ -1934,7 +2061,8 @@ static irqreturn_t irq_handler(int irq, void *data)
reg_read(ohci, OHCI1394_PostedWriteAddressLo);
reg_write(ohci, OHCI1394_IntEventClear,
OHCI1394_postedWriteErr);
- fw_error("PCI posted write error\n");
+ if (printk_ratelimit())
+ fw_error("PCI posted write error\n");
}
if (unlikely(event & OHCI1394_cycleTooLong)) {
@@ -2048,6 +2176,28 @@ static int configure_1394a_enhancements(struct fw_ohci *ohci)
return 0;
}
+static int probe_tsb41ba3d(struct fw_ohci *ohci)
+{
+ /* TI vendor ID = 0x080028, TSB41BA3D product ID = 0x833005 (sic) */
+ static const u8 id[] = { 0x08, 0x00, 0x28, 0x83, 0x30, 0x05, };
+ int reg, i;
+
+ reg = read_phy_reg(ohci, 2);
+ if (reg < 0)
+ return reg;
+ if ((reg & PHY_EXTENDED_REGISTERS) != PHY_EXTENDED_REGISTERS)
+ return 0;
+
+ for (i = ARRAY_SIZE(id) - 1; i >= 0; i--) {
+ reg = read_paged_phy_reg(ohci, 1, i + 10);
+ if (reg < 0)
+ return reg;
+ if (reg != id[i])
+ return 0;
+ }
+ return 1;
+}
+
static int ohci_enable(struct fw_card *card,
const __be32 *config_rom, size_t length)
{
@@ -2085,6 +2235,16 @@ static int ohci_enable(struct fw_card *card,
return -EIO;
}
+ if (ohci->quirks & QUIRK_TI_SLLZ059) {
+ ret = probe_tsb41ba3d(ohci);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ fw_notify("local TSB41BA3D phy\n");
+ else
+ ohci->quirks &= ~QUIRK_TI_SLLZ059;
+ }
+
reg_write(ohci, OHCI1394_HCControlClear,
OHCI1394_HCControl_noByteSwapData);
@@ -2260,7 +2420,7 @@ static int ohci_set_config_rom(struct fw_card *card,
* then set up the real values for the two registers.
*
* We use ohci->lock to avoid racing with the code that sets
- * ohci->next_config_rom to NULL (see bus_reset_tasklet).
+ * ohci->next_config_rom to NULL (see bus_reset_work).
*/
next_config_rom =
@@ -2539,6 +2699,7 @@ static int handle_ir_packet_per_buffer(struct context *context,
struct iso_context *ctx =
container_of(context, struct iso_context, context);
struct descriptor *pd;
+ u32 buffer_dma;
__le32 *ir_header;
void *p;
@@ -2549,6 +2710,16 @@ static int handle_ir_packet_per_buffer(struct context *context,
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ while (!(d->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))) {
+ d++;
+ buffer_dma = le32_to_cpu(d->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(d->req_count),
+ DMA_FROM_DEVICE);
+ }
+
p = last + 1;
copy_iso_headers(ctx, p);
@@ -2571,11 +2742,19 @@ static int handle_ir_buffer_fill(struct context *context,
{
struct iso_context *ctx =
container_of(context, struct iso_context, context);
+ u32 buffer_dma;
if (!last->transfer_status)
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ buffer_dma = le32_to_cpu(last->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(last->req_count),
+ DMA_FROM_DEVICE);
+
if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS)
ctx->base.callback.mc(&ctx->base,
le32_to_cpu(last->data_address) +
@@ -2586,6 +2765,43 @@ static int handle_ir_buffer_fill(struct context *context,
return 1;
}
+static inline void sync_it_packet_for_cpu(struct context *context,
+ struct descriptor *pd)
+{
+ __le16 control;
+ u32 buffer_dma;
+
+ /* only packets beginning with OUTPUT_MORE* have data buffers */
+ if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
+ return;
+
+ /* skip over the OUTPUT_MORE_IMMEDIATE descriptor */
+ pd += 2;
+
+ /*
+ * If the packet has a header, the first OUTPUT_MORE/LAST descriptor's
+ * data buffer is in the context program's coherent page and must not
+ * be synced.
+ */
+ if ((le32_to_cpu(pd->data_address) & PAGE_MASK) ==
+ (context->current_bus & PAGE_MASK)) {
+ if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
+ return;
+ pd++;
+ }
+
+ do {
+ buffer_dma = le32_to_cpu(pd->data_address);
+ dma_sync_single_range_for_cpu(context->ohci->card.device,
+ buffer_dma & PAGE_MASK,
+ buffer_dma & ~PAGE_MASK,
+ le16_to_cpu(pd->req_count),
+ DMA_TO_DEVICE);
+ control = pd->control;
+ pd++;
+ } while (!(control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS)));
+}
+
static int handle_it_packet(struct context *context,
struct descriptor *d,
struct descriptor *last)
@@ -2602,6 +2818,8 @@ static int handle_it_packet(struct context *context,
/* Descriptor(s) not done yet, stop iteration */
return 0;
+ sync_it_packet_for_cpu(context, d);
+
i = ctx->header_length;
if (i + 4 < PAGE_SIZE) {
/* Present this value as big-endian to match the receive code */
@@ -2971,6 +3189,10 @@ static int queue_iso_transmit(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
pd[i].data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(ctx->context.ohci->card.device,
+ page_bus, offset, length,
+ DMA_TO_DEVICE);
+
payload_index += length;
}
@@ -2995,6 +3217,7 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
struct fw_iso_buffer *buffer,
unsigned long payload)
{
+ struct device *device = ctx->context.ohci->card.device;
struct descriptor *d, *pd;
dma_addr_t d_bus, page_bus;
u32 z, header_z, rest;
@@ -3049,6 +3272,10 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
pd->data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(device, page_bus,
+ offset, length,
+ DMA_FROM_DEVICE);
+
offset = (offset + length) & ~PAGE_MASK;
rest -= length;
if (offset == 0)
@@ -3108,6 +3335,10 @@ static int queue_iso_buffer_fill(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]);
d->data_address = cpu_to_le32(page_bus + offset);
+ dma_sync_single_range_for_device(ctx->context.ohci->card.device,
+ page_bus, offset, length,
+ DMA_FROM_DEVICE);
+
rest -= length;
offset = 0;
page++;
@@ -3239,8 +3470,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
spin_lock_init(&ohci->lock);
mutex_init(&ohci->phy_reg_mutex);
- tasklet_init(&ohci->bus_reset_tasklet,
- bus_reset_tasklet, (unsigned long)ohci);
+ INIT_WORK(&ohci->bus_reset_work, bus_reset_work);
err = pci_request_region(dev, 0, ohci_driver_name);
if (err) {
@@ -3382,6 +3612,7 @@ static void pci_remove(struct pci_dev *dev)
ohci = pci_get_drvdata(dev);
reg_write(ohci, OHCI1394_IntMaskClear, ~0);
flush_writes(ohci);
+ cancel_work_sync(&ohci->bus_reset_work);
fw_core_remove_card(&ohci->card);
/*
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 17cef864506a..68375bc3aef6 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -154,12 +154,16 @@ struct sbp2_logical_unit {
bool blocked;
};
+static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
+{
+ queue_delayed_work(fw_workqueue, &lu->work, delay);
+}
+
/*
* We create one struct sbp2_target per IEEE 1212 Unit Directory
* and one struct Scsi_Host per sbp2_target.
*/
struct sbp2_target {
- struct kref kref;
struct fw_unit *unit;
const char *bus_id;
struct list_head lu_list;
@@ -772,71 +776,6 @@ static int sbp2_lun2int(u16 lun)
return scsilun_to_int(&eight_bytes_lun);
}
-static void sbp2_release_target(struct kref *kref)
-{
- struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref);
- struct sbp2_logical_unit *lu, *next;
- struct Scsi_Host *shost =
- container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
- struct scsi_device *sdev;
- struct fw_device *device = target_device(tgt);
-
- /* prevent deadlocks */
- sbp2_unblock(tgt);
-
- list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
- sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun));
- if (sdev) {
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
- }
- if (lu->login_id != INVALID_LOGIN_ID) {
- int generation, node_id;
- /*
- * tgt->node_id may be obsolete here if we failed
- * during initial login or after a bus reset where
- * the topology changed.
- */
- generation = device->generation;
- smp_rmb(); /* node_id vs. generation */
- node_id = device->node_id;
- sbp2_send_management_orb(lu, node_id, generation,
- SBP2_LOGOUT_REQUEST,
- lu->login_id, NULL);
- }
- fw_core_remove_address_handler(&lu->address_handler);
- list_del(&lu->link);
- kfree(lu);
- }
- scsi_remove_host(shost);
- fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no);
-
- fw_unit_put(tgt->unit);
- scsi_host_put(shost);
- fw_device_put(device);
-}
-
-static void sbp2_target_get(struct sbp2_target *tgt)
-{
- kref_get(&tgt->kref);
-}
-
-static void sbp2_target_put(struct sbp2_target *tgt)
-{
- kref_put(&tgt->kref, sbp2_release_target);
-}
-
-/*
- * Always get the target's kref when scheduling work on one its units.
- * Each workqueue job is responsible to call sbp2_target_put() upon return.
- */
-static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
-{
- sbp2_target_get(lu->tgt);
- if (!queue_delayed_work(fw_workqueue, &lu->work, delay))
- sbp2_target_put(lu->tgt);
-}
-
/*
* Write retransmit retry values into the BUSY_TIMEOUT register.
* - The single-phase retry protocol is supported by all SBP-2 devices, but the
@@ -877,7 +816,7 @@ static void sbp2_login(struct work_struct *work)
int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
- goto out;
+ return;
generation = device->generation;
smp_rmb(); /* node IDs must not be older than generation */
@@ -899,7 +838,7 @@ static void sbp2_login(struct work_struct *work)
/* Let any waiting I/O fail from now on. */
sbp2_unblock(lu->tgt);
}
- goto out;
+ return;
}
tgt->node_id = node_id;
@@ -925,7 +864,8 @@ static void sbp2_login(struct work_struct *work)
if (lu->has_sdev) {
sbp2_cancel_orbs(lu);
sbp2_conditionally_unblock(lu);
- goto out;
+
+ return;
}
if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
@@ -957,7 +897,8 @@ static void sbp2_login(struct work_struct *work)
lu->has_sdev = true;
scsi_device_put(sdev);
sbp2_allow_block(lu);
- goto out;
+
+ return;
out_logout_login:
smp_rmb(); /* generation may have changed */
@@ -971,8 +912,57 @@ static void sbp2_login(struct work_struct *work)
* lu->work already. Reset the work from reconnect to login.
*/
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
- out:
- sbp2_target_put(tgt);
+}
+
+static void sbp2_reconnect(struct work_struct *work)
+{
+ struct sbp2_logical_unit *lu =
+ container_of(work, struct sbp2_logical_unit, work.work);
+ struct sbp2_target *tgt = lu->tgt;
+ struct fw_device *device = target_device(tgt);
+ int generation, node_id, local_node_id;
+
+ if (fw_device_is_shutdown(device))
+ return;
+
+ generation = device->generation;
+ smp_rmb(); /* node IDs must not be older than generation */
+ node_id = device->node_id;
+ local_node_id = device->card->node_id;
+
+ if (sbp2_send_management_orb(lu, node_id, generation,
+ SBP2_RECONNECT_REQUEST,
+ lu->login_id, NULL) < 0) {
+ /*
+ * If reconnect was impossible even though we are in the
+ * current generation, fall back and try to log in again.
+ *
+ * We could check for "Function rejected" status, but
+ * looking at the bus generation as simpler and more general.
+ */
+ smp_rmb(); /* get current card generation */
+ if (generation == device->card->generation ||
+ lu->retries++ >= 5) {
+ fw_error("%s: failed to reconnect\n", tgt->bus_id);
+ lu->retries = 0;
+ PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
+ }
+ sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+
+ return;
+ }
+
+ tgt->node_id = node_id;
+ tgt->address_high = local_node_id << 16;
+ smp_wmb(); /* node IDs must not be older than generation */
+ lu->generation = generation;
+
+ fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
+ tgt->bus_id, lu->lun, lu->retries);
+
+ sbp2_agent_reset(lu);
+ sbp2_cancel_orbs(lu);
+ sbp2_conditionally_unblock(lu);
}
static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
@@ -1120,6 +1110,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
}
static struct scsi_host_template scsi_driver_template;
+static int sbp2_remove(struct device *dev);
static int sbp2_probe(struct device *dev)
{
@@ -1141,7 +1132,6 @@ static int sbp2_probe(struct device *dev)
tgt = (struct sbp2_target *)shost->hostdata;
dev_set_drvdata(&unit->device, tgt);
tgt->unit = unit;
- kref_init(&tgt->kref);
INIT_LIST_HEAD(&tgt->lu_list);
tgt->bus_id = dev_name(&unit->device);
tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
@@ -1154,9 +1144,6 @@ static int sbp2_probe(struct device *dev)
if (scsi_add_host(shost, &unit->device) < 0)
goto fail_shost_put;
- fw_device_get(device);
- fw_unit_get(unit);
-
/* implicit directory ID */
tgt->directory_id = ((unit->directory - device->config_rom) * 4
+ CSR_CONFIG_ROM) & 0xffffff;
@@ -1166,7 +1153,7 @@ static int sbp2_probe(struct device *dev)
if (sbp2_scan_unit_dir(tgt, unit->directory, &model,
&firmware_revision) < 0)
- goto fail_tgt_put;
+ goto fail_remove;
sbp2_clamp_management_orb_timeout(tgt);
sbp2_init_workarounds(tgt, model, firmware_revision);
@@ -1177,16 +1164,17 @@ static int sbp2_probe(struct device *dev)
* specifies the max payload size as 2 ^ (max_payload + 2), so
* if we set this to max_speed + 7, we get the right value.
*/
- tgt->max_payload = min(device->max_speed + 7, 10U);
- tgt->max_payload = min(tgt->max_payload, device->card->max_receive - 1);
+ tgt->max_payload = min3(device->max_speed + 7, 10U,
+ device->card->max_receive - 1);
/* Do the login in a workqueue so we can easily reschedule retries. */
list_for_each_entry(lu, &tgt->lu_list, link)
sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+
return 0;
- fail_tgt_put:
- sbp2_target_put(tgt);
+ fail_remove:
+ sbp2_remove(dev);
return -ENOMEM;
fail_shost_put:
@@ -1194,71 +1182,6 @@ static int sbp2_probe(struct device *dev)
return -ENOMEM;
}
-static int sbp2_remove(struct device *dev)
-{
- struct fw_unit *unit = fw_unit(dev);
- struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
- struct sbp2_logical_unit *lu;
-
- list_for_each_entry(lu, &tgt->lu_list, link)
- cancel_delayed_work_sync(&lu->work);
-
- sbp2_target_put(tgt);
- return 0;
-}
-
-static void sbp2_reconnect(struct work_struct *work)
-{
- struct sbp2_logical_unit *lu =
- container_of(work, struct sbp2_logical_unit, work.work);
- struct sbp2_target *tgt = lu->tgt;
- struct fw_device *device = target_device(tgt);
- int generation, node_id, local_node_id;
-
- if (fw_device_is_shutdown(device))
- goto out;
-
- generation = device->generation;
- smp_rmb(); /* node IDs must not be older than generation */
- node_id = device->node_id;
- local_node_id = device->card->node_id;
-
- if (sbp2_send_management_orb(lu, node_id, generation,
- SBP2_RECONNECT_REQUEST,
- lu->login_id, NULL) < 0) {
- /*
- * If reconnect was impossible even though we are in the
- * current generation, fall back and try to log in again.
- *
- * We could check for "Function rejected" status, but
- * looking at the bus generation as simpler and more general.
- */
- smp_rmb(); /* get current card generation */
- if (generation == device->card->generation ||
- lu->retries++ >= 5) {
- fw_error("%s: failed to reconnect\n", tgt->bus_id);
- lu->retries = 0;
- PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
- }
- sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
- goto out;
- }
-
- tgt->node_id = node_id;
- tgt->address_high = local_node_id << 16;
- smp_wmb(); /* node IDs must not be older than generation */
- lu->generation = generation;
-
- fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
- tgt->bus_id, lu->lun, lu->retries);
-
- sbp2_agent_reset(lu);
- sbp2_cancel_orbs(lu);
- sbp2_conditionally_unblock(lu);
- out:
- sbp2_target_put(tgt);
-}
-
static void sbp2_update(struct fw_unit *unit)
{
struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
@@ -1277,6 +1200,51 @@ static void sbp2_update(struct fw_unit *unit)
}
}
+static int sbp2_remove(struct device *dev)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_device *device = fw_parent_device(unit);
+ struct sbp2_target *tgt = dev_get_drvdata(&unit->device);
+ struct sbp2_logical_unit *lu, *next;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ struct scsi_device *sdev;
+
+ /* prevent deadlocks */
+ sbp2_unblock(tgt);
+
+ list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
+ cancel_delayed_work_sync(&lu->work);
+ sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun));
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+ if (lu->login_id != INVALID_LOGIN_ID) {
+ int generation, node_id;
+ /*
+ * tgt->node_id may be obsolete here if we failed
+ * during initial login or after a bus reset where
+ * the topology changed.
+ */
+ generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ node_id = device->node_id;
+ sbp2_send_management_orb(lu, node_id, generation,
+ SBP2_LOGOUT_REQUEST,
+ lu->login_id, NULL);
+ }
+ fw_core_remove_address_handler(&lu->address_handler);
+ list_del(&lu->link);
+ kfree(lu);
+ }
+ scsi_remove_host(shost);
+ fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no);
+
+ scsi_host_put(shost);
+ return 0;
+}
+
#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
#define SBP2_SW_VERSION_ENTRY 0x00010483
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index eb80b549ed8d..8370f72d87ff 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -490,8 +490,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
return 0;
}
-static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
- size_t size, struct pstore_info *psi)
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+ unsigned int part, size_t size, struct pstore_info *psi)
{
char name[DUMP_NAME_LEN];
char stub_name[DUMP_NAME_LEN];
@@ -499,7 +499,7 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct efivars *efivars = psi->data;
struct efivar_entry *entry, *found = NULL;
- int i;
+ int i, ret = 0;
sprintf(stub_name, "dump-type%u-%u-", type, part);
sprintf(name, "%s%lu", stub_name, get_seconds());
@@ -548,18 +548,19 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
efivar_unregister(found);
if (size)
- efivar_create_sysfs_entry(efivars,
+ ret = efivar_create_sysfs_entry(efivars,
utf16_strsize(efi_name,
DUMP_NAME_LEN * 2),
efi_name, &vendor);
- return part;
+ *id = part;
+ return ret;
};
static int efi_pstore_erase(enum pstore_type_id type, u64 id,
struct pstore_info *psi)
{
- efi_pstore_write(type, id, 0, psi);
+ efi_pstore_write(type, &id, (unsigned int)id, 0, psi);
return 0;
}
@@ -580,8 +581,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
return -1;
}
-static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
- size_t size, struct pstore_info *psi)
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+ unsigned int part, size_t size, struct pstore_info *psi)
{
return 0;
}
@@ -978,7 +979,7 @@ int register_efivars(struct efivars *efivars,
if (efivars->efi_pstore_info.buf) {
efivars->efi_pstore_info.bufsize = 1024;
efivars->efi_pstore_info.data = efivars;
- mutex_init(&efivars->efi_pstore_info.buf_mutex);
+ spin_lock_init(&efivars->efi_pstore_info.buf_lock);
pstore_register(&efivars->efi_pstore_info);
}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4caa3d37bbde..8b3c745b1b05 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -189,7 +189,7 @@ config GPIO_U300
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
- depends on MFD_SUPPORT && PCI
+ depends on PCI
select MFD_CORE
select MFD_VX855
help
@@ -397,6 +397,7 @@ config GPIO_LANGWELL
config GPIO_PCH
tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GPIO"
depends on PCI && X86
+ select GENERIC_IRQ_CHIP
help
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
which is an IOH(Input/Output Hub) for x86 embedded processor.
@@ -411,6 +412,7 @@ config GPIO_PCH
config GPIO_ML_IOH
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
depends on PCI
+ select GENERIC_IRQ_CHIP
help
ML7213 is companion chip for Intel Atom E6xx series.
This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
@@ -426,7 +428,6 @@ config GPIO_TIMBERDALE
config GPIO_RDC321X
tristate "RDC R-321x GPIO support"
depends on PCI
- select MFD_SUPPORT
select MFD_CORE
select MFD_RDC321X
help
diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c
index d2eb57c60e0e..00692e89ef87 100644
--- a/drivers/gpio/gpio-langwell.c
+++ b/drivers/gpio/gpio-langwell.c
@@ -59,6 +59,7 @@ enum GPIO_REG {
GRER, /* rising edge detect */
GFER, /* falling edge detect */
GEDR, /* edge detect result */
+ GAFR, /* alt function */
};
struct lnw_gpio {
@@ -81,6 +82,31 @@ static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
return ptr;
}
+static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
+ enum GPIO_REG reg_type)
+{
+ struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
+ unsigned nreg = chip->ngpio / 32;
+ u8 reg = offset / 16;
+ void __iomem *ptr;
+
+ ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
+ return ptr;
+}
+
+static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR);
+ u32 value = readl(gafr);
+ int shift = (offset % 16) << 1, af = (value >> shift) & 3;
+
+ if (af) {
+ value &= ~(3 << shift);
+ writel(value, gafr);
+ }
+ return 0;
+}
+
static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset)
{
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
@@ -321,6 +347,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
lnw->reg_base = base;
lnw->irq_base = irq_base;
lnw->chip.label = dev_name(&pdev->dev);
+ lnw->chip.request = lnw_gpio_request;
lnw->chip.direction_input = lnw_gpio_direction_input;
lnw->chip.direction_output = lnw_gpio_direction_output;
lnw->chip.get = lnw_gpio_get;
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index a9016f56ed7e..3aa6beec8c1e 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -18,6 +18,17 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#define IOH_EDGE_FALLING 0
+#define IOH_EDGE_RISING BIT(0)
+#define IOH_LEVEL_L BIT(1)
+#define IOH_LEVEL_H (BIT(0) | BIT(1))
+#define IOH_EDGE_BOTH BIT(2)
+#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define IOH_IRQ_BASE 0
#define PCI_VENDOR_ID_ROHM 0x10DB
@@ -46,12 +57,22 @@ struct ioh_regs {
/**
* struct ioh_gpio_reg_data - The register store data.
+ * @ien_reg To store contents of interrupt enable register.
+ * @imask_reg: To store contents of interrupt mask regist
* @po_reg: To store contents of PO register.
* @pm_reg: To store contents of PM register.
+ * @im0_reg: To store contents of interrupt mode regist0
+ * @im1_reg: To store contents of interrupt mode regist1
+ * @use_sel_reg: To store contents of GPIO_USE_SEL0~3
*/
struct ioh_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
u32 po_reg;
u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 use_sel_reg;
};
/**
@@ -62,7 +83,11 @@ struct ioh_gpio_reg_data {
* @gpio: Data for GPIO infrastructure.
* @ioh_gpio_reg: Memory mapped Register data is saved here
* when suspend.
+ * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
* @ch: Indicate GPIO channel
+ * @irq_base: Save base of IRQ number for interrupt
+ * @spinlock: Used for register access protection in
+ * interrupt context ioh_irq_type and PM;
*/
struct ioh_gpio {
void __iomem *base;
@@ -70,8 +95,11 @@ struct ioh_gpio {
struct device *dev;
struct gpio_chip gpio;
struct ioh_gpio_reg_data ioh_gpio_reg;
+ u32 gpio_use_sel;
struct mutex lock;
int ch;
+ int irq_base;
+ spinlock_t spinlock;
};
static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12};
@@ -145,8 +173,25 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
*/
static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
{
- chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po);
- chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm);
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ chip->ioh_gpio_reg.po_reg =
+ ioread32(&chip->reg->regs[chip->ch].po);
+ chip->ioh_gpio_reg.pm_reg =
+ ioread32(&chip->reg->regs[chip->ch].pm);
+ chip->ioh_gpio_reg.ien_reg =
+ ioread32(&chip->reg->regs[chip->ch].ien);
+ chip->ioh_gpio_reg.imask_reg =
+ ioread32(&chip->reg->regs[chip->ch].imask);
+ chip->ioh_gpio_reg.im0_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_0);
+ chip->ioh_gpio_reg.im1_reg =
+ ioread32(&chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ chip->ioh_gpio_reg.use_sel_reg =
+ ioread32(&chip->reg->ioh_sel_reg[i]);
+ }
}
/*
@@ -154,13 +199,34 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
*/
static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
{
- /* to store contents of PO register */
- iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po);
- /* to store contents of PM register */
- iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm);
+ int i;
+
+ for (i = 0; i < 8; i ++, chip++) {
+ iowrite32(chip->ioh_gpio_reg.po_reg,
+ &chip->reg->regs[chip->ch].po);
+ iowrite32(chip->ioh_gpio_reg.pm_reg,
+ &chip->reg->regs[chip->ch].pm);
+ iowrite32(chip->ioh_gpio_reg.ien_reg,
+ &chip->reg->regs[chip->ch].ien);
+ iowrite32(chip->ioh_gpio_reg.imask_reg,
+ &chip->reg->regs[chip->ch].imask);
+ iowrite32(chip->ioh_gpio_reg.im0_reg,
+ &chip->reg->regs[chip->ch].im_0);
+ iowrite32(chip->ioh_gpio_reg.im1_reg,
+ &chip->reg->regs[chip->ch].im_1);
+ if (i < 4)
+ iowrite32(chip->ioh_gpio_reg.use_sel_reg,
+ &chip->reg->ioh_sel_reg[i]);
+ }
}
#endif
+static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ return chip->irq_base + offset;
+}
+
static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
{
struct gpio_chip *gpio = &chip->gpio;
@@ -175,16 +241,148 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
gpio->base = -1;
gpio->ngpio = num_port;
gpio->can_sleep = 0;
+ gpio->to_irq = ioh_gpio_to_irq;
+}
+
+static int ioh_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 im;
+ u32 *im_reg;
+ u32 ien;
+ u32 im_pos;
+ int ch;
+ unsigned long flags;
+ u32 val;
+ int irq = d->irq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->regs[chip->ch].im_0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->regs[chip->ch].im_1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n",
+ __func__, irq, type, ch, im_pos, type);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = IOH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = IOH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = IOH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = IOH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = IOH_LEVEL_L;
+ break;
+ case IRQ_TYPE_PROBE:
+ goto end;
+ default:
+ dev_warn(chip->dev, "%s: unknown type(%dd)",
+ __func__, type);
+ goto end;
+ }
+
+ /* Set interrupt mode */
+ im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4));
+ iowrite32(im | (val << (im_pos * 4)), im_reg);
+
+ /* iclr */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr);
+
+ /* IMASKCLR */
+ iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr);
+
+ /* Enable interrupt */
+ ien = ioread32(&chip->reg->regs[chip->ch].ien);
+ iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien);
+end:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static void ioh_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base),
+ &chip->reg->regs[chip->ch].imaskclr);
+}
+
+static void ioh_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct ioh_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base),
+ &chip->reg->regs[chip->ch].imask);
+}
+
+static irqreturn_t ioh_gpio_handler(int irq, void *dev_id)
+{
+ struct ioh_gpio *chip = dev_id;
+ u32 reg_val;
+ int i, j;
+ int ret = IRQ_NONE;
+
+ for (i = 0; i < 8; i++) {
+ reg_val = ioread32(&chip->reg->regs[i].istatus);
+ for (j = 0; j < num_ports[i]; j++) {
+ if (reg_val & BIT(j)) {
+ dev_dbg(chip->dev,
+ "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, j, irq, reg_val);
+ iowrite32(BIT(j),
+ &chip->reg->regs[chip->ch].iclr);
+ generic_handle_irq(chip->irq_base + j);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+ return ret;
+}
+
+static __devinit void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_mask = ioh_irq_mask;
+ ct->chip.irq_unmask = ioh_irq_unmask;
+ ct->chip.irq_set_type = ioh_irq_type;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}
static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
- int i;
+ int i, j;
struct ioh_gpio *chip;
void __iomem *base;
void __iomem *chip_save;
+ int irq_base;
ret = pci_enable_device(pdev);
if (ret) {
@@ -228,10 +426,41 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
}
chip = chip_save;
+ for (j = 0; j < 8; j++, chip++) {
+ irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j],
+ NUMA_NO_NODE);
+ if (irq_base < 0) {
+ dev_warn(&pdev->dev,
+ "ml_ioh_gpio: Failed to get IRQ base num\n");
+ chip->irq_base = -1;
+ goto err_irq_alloc_descs;
+ }
+ chip->irq_base = irq_base;
+ ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
+ ret = request_irq(pdev->irq, ioh_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+
pci_set_drvdata(pdev, chip);
return 0;
+err_request_irq:
+ chip = chip_save;
+err_irq_alloc_descs:
+ while (--j >= 0) {
+ chip--;
+ irq_free_descs(chip->irq_base, num_ports[j]);
+ }
+
+ chip = chip_save;
err_gpiochip_add:
while (--i >= 0) {
chip--;
@@ -264,7 +493,11 @@ static void __devexit ioh_gpio_remove(struct pci_dev *pdev)
void __iomem *chip_save;
chip_save = chip;
+
+ free_irq(pdev->irq, chip);
+
for (i = 0; i < 8; i++, chip++) {
+ irq_free_descs(chip->irq_base, num_ports[i]);
err = gpiochip_remove(&chip->gpio);
if (err)
dev_err(&pdev->dev, "Failed gpiochip_remove\n");
@@ -282,9 +515,11 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
{
s32 ret;
struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+ spin_lock_irqsave(&chip->spinlock, flags);
ioh_gpio_save_reg_conf(chip);
- ioh_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
ret = pci_save_state(pdev);
if (ret) {
@@ -304,6 +539,7 @@ static int ioh_gpio_resume(struct pci_dev *pdev)
{
s32 ret;
struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
ret = pci_enable_wake(pdev, PCI_D0, 0);
@@ -315,9 +551,11 @@ static int ioh_gpio_resume(struct pci_dev *pdev)
}
pci_restore_state(pdev);
+ spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->srst);
iowrite32(0x00, &chip->reg->srst);
ioh_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 4340acae3bd3..b81c98992114 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -30,6 +30,9 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm-generic/bug.h>
+#include <asm/mach/irq.h>
+
+#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START)
enum mxc_gpio_hwtype {
IMX1_GPIO, /* runs on i.mx1 */
@@ -232,10 +235,15 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
struct mxc_gpio_port *port = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
mxc_gpio_irq_handler(port, irq_stat);
+
+ chained_irq_exit(chip, desc);
}
/* MX2 has one interrupt *for all* gpio ports */
@@ -337,6 +345,15 @@ static void __devinit mxc_gpio_get_hw(struct platform_device *pdev)
mxc_gpio_hwtype = hwtype;
}
+static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct mxc_gpio_port *port =
+ container_of(bgc, struct mxc_gpio_port, bgc);
+
+ return port->virtual_irq_start + offset;
+}
+
static int __devinit mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -403,6 +420,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev)
if (err)
goto out_iounmap;
+ port->bgc.gc.to_irq = mxc_gpio_to_irq;
port->bgc.gc.base = pdev->id * 32;
port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir);
port->bgc.data = port->bgc.read_reg(port->bgc.reg_set);
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index af55a8577c2e..292b50481db9 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -49,6 +49,8 @@
#define GPIO_INT_LEV_MASK (1 << 0)
#define GPIO_INT_POL_MASK (1 << 1)
+#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START)
+
struct mxs_gpio_port {
void __iomem *base;
int id;
diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c
index 740caed2b278..1ebedfb6d46d 100644
--- a/drivers/gpio/gpio-nomadik.c
+++ b/drivers/gpio/gpio-nomadik.c
@@ -59,7 +59,6 @@ struct nmk_gpio_chip {
u32 rwimsc;
u32 fwimsc;
u32 slpm;
- u32 enabled;
u32 pull_up;
};
@@ -277,6 +276,8 @@ static void nmk_gpio_glitch_slpm_init(unsigned int *slpm)
if (!chip)
break;
+ clk_enable(chip->clk);
+
slpm[i] = readl(chip->addr + NMK_GPIO_SLPC);
writel(temp, chip->addr + NMK_GPIO_SLPC);
}
@@ -293,6 +294,8 @@ static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm)
break;
writel(slpm[i], chip->addr + NMK_GPIO_SLPC);
+
+ clk_disable(chip->clk);
}
}
@@ -337,10 +340,12 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
break;
}
+ clk_enable(nmk_chip->clk);
spin_lock(&nmk_chip->lock);
__nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base,
cfgs[i], sleep, glitch ? slpm : NULL);
spin_unlock(&nmk_chip->lock);
+ clk_disable(nmk_chip->clk);
}
if (glitch)
@@ -425,6 +430,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
@@ -432,6 +438,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -458,9 +465,11 @@ int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
__nmk_gpio_set_pull(nmk_chip, gpio - nmk_chip->chip.base, pull);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -484,9 +493,11 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode)
if (!nmk_chip)
return -EINVAL;
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
__nmk_gpio_set_mode(nmk_chip, gpio - nmk_chip->chip.base, gpio_mode);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -503,9 +514,13 @@ int nmk_gpio_get_mode(int gpio)
bit = 1 << (gpio - nmk_chip->chip.base);
+ clk_enable(nmk_chip->clk);
+
afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
+ clk_disable(nmk_chip->clk);
+
return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
}
EXPORT_SYMBOL(nmk_gpio_get_mode);
@@ -526,7 +541,10 @@ static void nmk_gpio_irq_ack(struct irq_data *d)
nmk_chip = irq_data_get_irq_chip_data(d);
if (!nmk_chip)
return;
+
+ clk_enable(nmk_chip->clk);
writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC);
+ clk_disable(nmk_chip->clk);
}
enum nmk_gpio_irq_type {
@@ -587,11 +605,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable)
if (!nmk_chip)
return -EINVAL;
- if (enable)
- nmk_chip->enabled |= bitmask;
- else
- nmk_chip->enabled &= ~bitmask;
-
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
@@ -602,6 +616,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
@@ -629,10 +644,11 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
return -EINVAL;
bitmask = nmk_gpio_get_bitmask(gpio);
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
- if (!(nmk_chip->enabled & bitmask))
+ if (irqd_irq_disabled(d))
__nmk_gpio_set_wake(nmk_chip, gpio, on);
if (on)
@@ -642,13 +658,15 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- bool enabled, wake = irqd_is_wakeup_set(d);
+ bool enabled = !irqd_irq_disabled(d);
+ bool wake = irqd_is_wakeup_set(d);
int gpio;
struct nmk_gpio_chip *nmk_chip;
unsigned long flags;
@@ -665,8 +683,7 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
if (type & IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
- enabled = nmk_chip->enabled & bitmask;
-
+ clk_enable(nmk_chip->clk);
spin_lock_irqsave(&nmk_chip->lock, flags);
if (enabled)
@@ -690,10 +707,28 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type)
__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ clk_disable(nmk_chip->clk);
return 0;
}
+static unsigned int nmk_gpio_irq_startup(struct irq_data *d)
+{
+ struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d);
+
+ clk_enable(nmk_chip->clk);
+ nmk_gpio_irq_unmask(d);
+ return 0;
+}
+
+static void nmk_gpio_irq_shutdown(struct irq_data *d)
+{
+ struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d);
+
+ nmk_gpio_irq_mask(d);
+ clk_disable(nmk_chip->clk);
+}
+
static struct irq_chip nmk_gpio_irq_chip = {
.name = "Nomadik-GPIO",
.irq_ack = nmk_gpio_irq_ack,
@@ -701,6 +736,8 @@ static struct irq_chip nmk_gpio_irq_chip = {
.irq_unmask = nmk_gpio_irq_unmask,
.irq_set_type = nmk_gpio_irq_set_type,
.irq_set_wake = nmk_gpio_irq_set_wake,
+ .irq_startup = nmk_gpio_irq_startup,
+ .irq_shutdown = nmk_gpio_irq_shutdown,
};
static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
@@ -727,7 +764,11 @@ static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq);
- u32 status = readl(nmk_chip->addr + NMK_GPIO_IS);
+ u32 status;
+
+ clk_enable(nmk_chip->clk);
+ status = readl(nmk_chip->addr + NMK_GPIO_IS);
+ clk_disable(nmk_chip->clk);
__nmk_gpio_irq_handler(irq, desc, status);
}
@@ -773,7 +814,12 @@ static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC);
+
+ clk_disable(nmk_chip->clk);
+
return 0;
}
@@ -782,8 +828,15 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset)
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
u32 bit = 1 << offset;
+ int value;
+
+ clk_enable(nmk_chip->clk);
- return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+ value = (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+
+ clk_disable(nmk_chip->clk);
+
+ return value;
}
static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
@@ -792,7 +845,11 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
__nmk_gpio_set_output(nmk_chip, offset, val);
+
+ clk_disable(nmk_chip->clk);
}
static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
@@ -801,8 +858,12 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
struct nmk_gpio_chip *nmk_chip =
container_of(chip, struct nmk_gpio_chip, chip);
+ clk_enable(nmk_chip->clk);
+
__nmk_gpio_make_output(nmk_chip, offset, val);
+ clk_disable(nmk_chip->clk);
+
return 0;
}
@@ -833,6 +894,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
[NMK_GPIO_ALT_C] = "altC",
};
+ clk_enable(nmk_chip->clk);
+
for (i = 0; i < chip->ngpio; i++, gpio++) {
const char *label = gpiochip_is_requested(chip, i);
bool pull;
@@ -877,6 +940,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
seq_printf(s, "\n");
}
+
+ clk_disable(nmk_chip->clk);
}
#else
@@ -894,6 +959,34 @@ static struct gpio_chip nmk_gpio_template = {
.can_sleep = 0,
};
+void nmk_gpio_clocks_enable(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ continue;
+
+ clk_enable(chip->clk);
+ }
+}
+
+void nmk_gpio_clocks_disable(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ continue;
+
+ clk_disable(chip->clk);
+ }
+}
+
/*
* Called from the suspend/resume path to only keep the real wakeup interrupts
* (those that have had set_irq_wake() called on them) as wakeup interrupts,
@@ -913,6 +1006,8 @@ void nmk_gpio_wakeups_suspend(void)
if (!chip)
break;
+ clk_enable(chip->clk);
+
chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC);
chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC);
@@ -927,6 +1022,8 @@ void nmk_gpio_wakeups_suspend(void)
/* 0 -> wakeup enable */
writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC);
}
+
+ clk_disable(chip->clk);
}
}
@@ -940,11 +1037,15 @@ void nmk_gpio_wakeups_resume(void)
if (!chip)
break;
+ clk_enable(chip->clk);
+
writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC);
writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC);
if (chip->sleepmode)
writel(chip->slpm, chip->addr + NMK_GPIO_SLPC);
+
+ clk_disable(chip->clk);
}
}
@@ -1011,8 +1112,6 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
goto out_release;
}
- clk_enable(clk);
-
nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL);
if (!nmk_chip) {
ret = -ENOMEM;
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index 36919e77c495..1e8a4a538810 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -17,9 +17,17 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
-#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */
-#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */
+#define PCH_EDGE_FALLING 0
+#define PCH_EDGE_RISING BIT(0)
+#define PCH_LEVEL_L BIT(1)
+#define PCH_LEVEL_H (BIT(0) | BIT(1))
+#define PCH_EDGE_BOTH BIT(2)
+#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define PCH_IRQ_BASE 24
struct pch_regs {
u32 ien;
@@ -33,18 +41,43 @@ struct pch_regs {
u32 pm;
u32 im0;
u32 im1;
- u32 reserved[4];
+ u32 reserved[3];
+ u32 gpio_use_sel;
u32 reset;
};
+enum pch_type_t {
+ INTEL_EG20T_PCH,
+ OKISEMI_ML7223m_IOH, /* OKISEMI ML7223 IOH PCIe Bus-m */
+ OKISEMI_ML7223n_IOH /* OKISEMI ML7223 IOH PCIe Bus-n */
+};
+
+/* Specifies number of GPIO PINS */
+static int gpio_pins[] = {
+ [INTEL_EG20T_PCH] = 12,
+ [OKISEMI_ML7223m_IOH] = 8,
+ [OKISEMI_ML7223n_IOH] = 8,
+};
+
/**
* struct pch_gpio_reg_data - The register store data.
+ * @ien_reg: To store contents of IEN register.
+ * @imask_reg: To store contents of IMASK register.
* @po_reg: To store contents of PO register.
* @pm_reg: To store contents of PM register.
+ * @im0_reg: To store contents of IM0 register.
+ * @im1_reg: To store contents of IM1 register.
+ * @gpio_use_sel_reg : To store contents of GPIO_USE_SEL register.
+ * (Only ML7223 Bus-n)
*/
struct pch_gpio_reg_data {
+ u32 ien_reg;
+ u32 imask_reg;
u32 po_reg;
u32 pm_reg;
+ u32 im0_reg;
+ u32 im1_reg;
+ u32 gpio_use_sel_reg;
};
/**
@@ -55,6 +88,12 @@ struct pch_gpio_reg_data {
* @gpio: Data for GPIO infrastructure.
* @pch_gpio_reg: Memory mapped Register data is saved here
* when suspend.
+ * @lock: Used for register access protection
+ * @irq_base: Save base of IRQ number for interrupt
+ * @ioh: IOH ID
+ * @spinlock: Used for register access protection in
+ * interrupt context pch_irq_mask,
+ * pch_irq_unmask and pch_irq_type;
*/
struct pch_gpio {
void __iomem *base;
@@ -63,6 +102,9 @@ struct pch_gpio {
struct gpio_chip gpio;
struct pch_gpio_reg_data pch_gpio_reg;
struct mutex lock;
+ int irq_base;
+ enum pch_type_t ioh;
+ spinlock_t spinlock;
};
static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
@@ -96,7 +138,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
u32 reg_val;
mutex_lock(&chip->lock);
- pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS;
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm |= (1 << nr);
iowrite32(pm, &chip->reg->pm);
@@ -118,7 +160,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
u32 pm;
mutex_lock(&chip->lock);
- pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm &= ~(1 << nr);
iowrite32(pm, &chip->reg->pm);
mutex_unlock(&chip->lock);
@@ -131,8 +173,16 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
*/
static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
{
+ chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
+ chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po);
chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm);
+ chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ chip->pch_gpio_reg.gpio_use_sel_reg =\
+ ioread32(&chip->reg->gpio_use_sel);
}
/*
@@ -140,10 +190,24 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
*/
static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
{
+ iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
+ iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
/* to store contents of PO register */
iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po);
/* to store contents of PM register */
iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm);
+ iowrite32(chip->pch_gpio_reg.im0_reg, &chip->reg->im0);
+ if (chip->ioh == INTEL_EG20T_PCH)
+ iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
+ if (chip->ioh == OKISEMI_ML7223n_IOH)
+ iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
+ &chip->reg->gpio_use_sel);
+}
+
+static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ return chip->irq_base + offset;
}
static void pch_gpio_setup(struct pch_gpio *chip)
@@ -158,8 +222,132 @@ static void pch_gpio_setup(struct pch_gpio *chip)
gpio->set = pch_gpio_set;
gpio->dbg_show = NULL;
gpio->base = -1;
- gpio->ngpio = GPIO_NUM_PINS;
+ gpio->ngpio = gpio_pins[chip->ioh];
gpio->can_sleep = 0;
+ gpio->to_irq = pch_gpio_to_irq;
+}
+
+static int pch_irq_type(struct irq_data *d, unsigned int type)
+{
+ u32 im;
+ u32 *im_reg;
+ u32 ien;
+ u32 im_pos;
+ int ch;
+ unsigned long flags;
+ u32 val;
+ int irq = d->irq;
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ ch = irq - chip->irq_base;
+ if (irq <= chip->irq_base + 7) {
+ im_reg = &chip->reg->im0;
+ im_pos = ch;
+ } else {
+ im_reg = &chip->reg->im1;
+ im_pos = ch - 8;
+ }
+ dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n",
+ __func__, irq, type, ch, im_pos);
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ val = PCH_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = PCH_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = PCH_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = PCH_LEVEL_H;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = PCH_LEVEL_L;
+ break;
+ case IRQ_TYPE_PROBE:
+ goto end;
+ default:
+ dev_warn(chip->dev, "%s: unknown type(%dd)",
+ __func__, type);
+ goto end;
+ }
+
+ /* Set interrupt mode */
+ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
+ iowrite32(im | (val << (im_pos * 4)), im_reg);
+
+ /* iclr */
+ iowrite32(BIT(ch), &chip->reg->iclr);
+
+ /* IMASKCLR */
+ iowrite32(BIT(ch), &chip->reg->imaskclr);
+
+ /* Enable interrupt */
+ ien = ioread32(&chip->reg->ien);
+ iowrite32(ien | BIT(ch), &chip->reg->ien);
+end:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static void pch_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr);
+}
+
+static void pch_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct pch_gpio *chip = gc->private;
+
+ iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask);
+}
+
+static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
+{
+ struct pch_gpio *chip = dev_id;
+ u32 reg_val = ioread32(&chip->reg->istatus);
+ int i;
+ int ret = IRQ_NONE;
+
+ for (i = 0; i < gpio_pins[chip->ioh]; i++) {
+ if (reg_val & BIT(i)) {
+ dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n",
+ __func__, i, irq, reg_val);
+ iowrite32(BIT(i), &chip->reg->iclr);
+ generic_handle_irq(chip->irq_base + i);
+ ret = IRQ_HANDLED;
+ }
+ }
+ return ret;
+}
+
+static __devinit void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
+ unsigned int irq_start, unsigned int num)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
+ handle_simple_irq);
+ gc->private = chip;
+ ct = gc->chip_types;
+
+ ct->chip.irq_mask = pch_irq_mask;
+ ct->chip.irq_unmask = pch_irq_unmask;
+ ct->chip.irq_set_type = pch_irq_type;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}
static int __devinit pch_gpio_probe(struct pci_dev *pdev,
@@ -167,6 +355,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
{
s32 ret;
struct pch_gpio *chip;
+ int irq_base;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
@@ -192,6 +381,13 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
goto err_iomap;
}
+ if (pdev->device == 0x8803)
+ chip->ioh = INTEL_EG20T_PCH;
+ else if (pdev->device == 0x8014)
+ chip->ioh = OKISEMI_ML7223m_IOH;
+ else if (pdev->device == 0x8043)
+ chip->ioh = OKISEMI_ML7223n_IOH;
+
chip->reg = chip->base;
pci_set_drvdata(pdev, chip);
mutex_init(&chip->lock);
@@ -202,8 +398,36 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
goto err_gpiochip_add;
}
+ irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE);
+ if (irq_base < 0) {
+ dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
+ chip->irq_base = -1;
+ goto end;
+ }
+ chip->irq_base = irq_base;
+
+ ret = request_irq(pdev->irq, pch_gpio_handler,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+
+ pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
+
+ /* Initialize interrupt ien register */
+ iowrite32(0, &chip->reg->ien);
+end:
return 0;
+err_request_irq:
+ irq_free_descs(irq_base, gpio_pins[chip->ioh]);
+
+ ret = gpiochip_remove(&chip->gpio);
+ if (ret)
+ dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__);
+
err_gpiochip_add:
pci_iounmap(pdev, chip->base);
@@ -224,6 +448,12 @@ static void __devexit pch_gpio_remove(struct pci_dev *pdev)
int err;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ if (chip->irq_base != -1) {
+ free_irq(pdev->irq, chip);
+
+ irq_free_descs(chip->irq_base, gpio_pins[chip->ioh]);
+ }
+
err = gpiochip_remove(&chip->gpio);
if (err)
dev_err(&pdev->dev, "Failed gpiochip_remove\n");
@@ -239,9 +469,11 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
{
s32 ret;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+ spin_lock_irqsave(&chip->spinlock, flags);
pch_gpio_save_reg_conf(chip);
- pch_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
ret = pci_save_state(pdev);
if (ret) {
@@ -261,6 +493,7 @@ static int pch_gpio_resume(struct pci_dev *pdev)
{
s32 ret;
struct pch_gpio *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
ret = pci_enable_wake(pdev, PCI_D0, 0);
@@ -272,9 +505,11 @@ static int pch_gpio_resume(struct pci_dev *pdev)
}
pci_restore_state(pdev);
+ spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->reset);
iowrite32(0x00, &chip->reg->reset);
pch_gpio_restore_reg_conf(chip);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -287,6 +522,7 @@ static int pch_gpio_resume(struct pci_dev *pdev)
static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) },
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id);
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 2c5a18f32bf3..093c90bd3c1d 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -118,7 +118,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
- if (chip->irq_base == (unsigned) -1)
+ if (chip->irq_base == NO_IRQ)
return -EINVAL;
return chip->irq_base + offset;
@@ -246,6 +246,18 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
if (chip == NULL)
return -ENOMEM;
+ pdata = dev->dev.platform_data;
+ if (pdata) {
+ chip->gc.base = pdata->gpio_base;
+ chip->irq_base = pdata->irq_base;
+ } else if (dev->dev.of_node) {
+ chip->gc.base = -1;
+ chip->irq_base = NO_IRQ;
+ } else {
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
if (!request_mem_region(dev->res.start,
resource_size(&dev->res), "pl061")) {
ret = -EBUSY;
@@ -267,14 +279,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
chip->gc.get = pl061_get_value;
chip->gc.set = pl061_set_value;
chip->gc.to_irq = pl061_to_irq;
- chip->gc.base = pdata->gpio_base;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(&dev->dev);
chip->gc.dev = &dev->dev;
chip->gc.owner = THIS_MODULE;
- chip->irq_base = pdata->irq_base;
-
ret = gpiochip_add(&chip->gc);
if (ret)
goto iounmap;
@@ -283,7 +292,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
* irq_chip support
*/
- if (chip->irq_base == (unsigned) -1)
+ if (chip->irq_base == NO_IRQ)
return 0;
writeb(0, chip->base + GPIOIE); /* disable irqs */
@@ -307,11 +316,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
list_add(&chip->list, chip_list);
for (i = 0; i < PL061_GPIO_NR; i++) {
- if (pdata->directions & (1 << i))
- pl061_direction_output(&chip->gc, i,
- pdata->values & (1 << i));
- else
- pl061_direction_input(&chip->gc, i);
+ if (pdata) {
+ if (pdata->directions & (1 << i))
+ pl061_direction_output(&chip->gc, i,
+ pdata->values & (1 << i));
+ else
+ pl061_direction_input(&chip->gc, i);
+ }
irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip,
handle_simple_irq);
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 9052925c6fa2..ee137712f9db 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -122,7 +122,7 @@ static int __init pxa_init_gpio_chip(int gpio_end)
struct gpio_chip *c = &chips[i].chip;
sprintf(chips[i].label, "gpio-%d", i);
- chips[i].regbase = (void __iomem *)GPIO_BANK(i);
+ chips[i].regbase = GPIO_BANK(i);
c->base = gpio;
c->label = chips[i].label;
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 6b65207c8106..61044c889f7f 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -20,10 +20,11 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
#include <asm/mach/irq.h>
@@ -35,9 +36,7 @@
#define GPIO_PORT(x) (((x) >> 3) & 0x3)
#define GPIO_BIT(x) ((x) & 0x7)
-#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \
- GPIO_BANK(x) * 0x80 + \
- GPIO_PORT(x) * 4)
+#define GPIO_REG(x) (GPIO_BANK(x) * 0x80 + GPIO_PORT(x) * 4)
#define GPIO_CNF(x) (GPIO_REG(x) + 0x00)
#define GPIO_OE(x) (GPIO_REG(x) + 0x10)
@@ -76,15 +75,18 @@ struct tegra_gpio_bank {
};
-static struct tegra_gpio_bank tegra_gpio_banks[] = {
- {.bank = 0, .irq = INT_GPIO1},
- {.bank = 1, .irq = INT_GPIO2},
- {.bank = 2, .irq = INT_GPIO3},
- {.bank = 3, .irq = INT_GPIO4},
- {.bank = 4, .irq = INT_GPIO5},
- {.bank = 5, .irq = INT_GPIO6},
- {.bank = 6, .irq = INT_GPIO7},
-};
+static void __iomem *regs;
+static struct tegra_gpio_bank tegra_gpio_banks[7];
+
+static inline void tegra_gpio_writel(u32 val, u32 reg)
+{
+ __raw_writel(val, regs + reg);
+}
+
+static inline u32 tegra_gpio_readl(u32 reg)
+{
+ return __raw_readl(regs + reg);
+}
static int tegra_gpio_compose(int bank, int port, int bit)
{
@@ -98,7 +100,7 @@ static void tegra_gpio_mask_write(u32 reg, int gpio, int value)
val = 0x100 << GPIO_BIT(gpio);
if (value)
val |= 1 << GPIO_BIT(gpio);
- __raw_writel(val, reg);
+ tegra_gpio_writel(val, reg);
}
void tegra_gpio_enable(int gpio)
@@ -118,7 +120,7 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- return (__raw_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
+ return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
}
static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -155,7 +157,7 @@ static void tegra_gpio_irq_ack(struct irq_data *d)
{
int gpio = d->irq - INT_GPIO_BASE;
- __raw_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
+ tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
}
static void tegra_gpio_irq_mask(struct irq_data *d)
@@ -208,10 +210,10 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
spin_lock_irqsave(&bank->lvl_lock[port], flags);
- val = __raw_readl(GPIO_INT_LVL(gpio));
+ val = tegra_gpio_readl(GPIO_INT_LVL(gpio));
val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio));
val |= lvl_type << GPIO_BIT(gpio);
- __raw_writel(val, GPIO_INT_LVL(gpio));
+ tegra_gpio_writel(val, GPIO_INT_LVL(gpio));
spin_unlock_irqrestore(&bank->lvl_lock[port], flags);
@@ -237,12 +239,12 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
for (port = 0; port < 4; port++) {
int gpio = tegra_gpio_compose(bank->bank, port, 0);
- unsigned long sta = __raw_readl(GPIO_INT_STA(gpio)) &
- __raw_readl(GPIO_INT_ENB(gpio));
- u32 lvl = __raw_readl(GPIO_INT_LVL(gpio));
+ unsigned long sta = tegra_gpio_readl(GPIO_INT_STA(gpio)) &
+ tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ u32 lvl = tegra_gpio_readl(GPIO_INT_LVL(gpio));
for_each_set_bit(pin, &sta, 8) {
- __raw_writel(1 << pin, GPIO_INT_CLR(gpio));
+ tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio));
/* if gpio is edge triggered, clear condition
* before executing the hander so that we don't
@@ -276,11 +278,11 @@ void tegra_gpio_resume(void)
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
unsigned int gpio = (b<<5) | (p<<3);
- __raw_writel(bank->cnf[p], GPIO_CNF(gpio));
- __raw_writel(bank->out[p], GPIO_OUT(gpio));
- __raw_writel(bank->oe[p], GPIO_OE(gpio));
- __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
- __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
+ tegra_gpio_writel(bank->cnf[p], GPIO_CNF(gpio));
+ tegra_gpio_writel(bank->out[p], GPIO_OUT(gpio));
+ tegra_gpio_writel(bank->oe[p], GPIO_OE(gpio));
+ tegra_gpio_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
+ tegra_gpio_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
}
}
@@ -299,11 +301,11 @@ void tegra_gpio_suspend(void)
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
unsigned int gpio = (b<<5) | (p<<3);
- bank->cnf[p] = __raw_readl(GPIO_CNF(gpio));
- bank->out[p] = __raw_readl(GPIO_OUT(gpio));
- bank->oe[p] = __raw_readl(GPIO_OE(gpio));
- bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio));
- bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio));
+ bank->cnf[p] = tegra_gpio_readl(GPIO_CNF(gpio));
+ bank->out[p] = tegra_gpio_readl(GPIO_OUT(gpio));
+ bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio));
+ bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio));
+ bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio));
}
}
local_irq_restore(flags);
@@ -333,28 +335,55 @@ static struct irq_chip tegra_gpio_irq_chip = {
*/
static struct lock_class_key gpio_lock_class;
-static int __init tegra_gpio_init(void)
+static int __devinit tegra_gpio_probe(struct platform_device *pdev)
{
+ struct resource *res;
struct tegra_gpio_bank *bank;
int gpio;
int i;
int j;
+ for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing IRQ resource\n");
+ return -ENODEV;
+ }
+
+ bank = &tegra_gpio_banks[i];
+ bank->bank = i;
+ bank->irq = res->start;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing MEM resource\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res),
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "Couldn't request MEM resource\n");
+ return -ENODEV;
+ }
+
+ regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "Couldn't ioremap regs\n");
+ return -ENODEV;
+ }
+
for (i = 0; i < 7; i++) {
for (j = 0; j < 4; j++) {
int gpio = tegra_gpio_compose(i, j, 0);
- __raw_writel(0x00, GPIO_INT_ENB(gpio));
+ tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio));
}
}
#ifdef CONFIG_OF_GPIO
- /*
- * This isn't ideal, but it gets things hooked up until this
- * driver is converted into a platform_device
- */
- tegra_gpio_chip.of_node = of_find_compatible_node(NULL, NULL,
- "nvidia,tegra20-gpio");
-#endif /* CONFIG_OF_GPIO */
+ tegra_gpio_chip.of_node = pdev->dev.of_node;
+#endif
gpiochip_add(&tegra_gpio_chip);
@@ -384,6 +413,24 @@ static int __init tegra_gpio_init(void)
return 0;
}
+static struct of_device_id tegra_gpio_of_match[] __devinitdata = {
+ { .compatible = "nvidia,tegra20-gpio", },
+ { },
+};
+
+static struct platform_driver tegra_gpio_driver = {
+ .driver = {
+ .name = "tegra-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_gpio_of_match,
+ },
+ .probe = tegra_gpio_probe,
+};
+
+static int __init tegra_gpio_init(void)
+{
+ return platform_driver_register(&tegra_gpio_driver);
+}
postcore_initcall(tegra_gpio_init);
void __init tegra_gpio_config(struct tegra_gpio_table *table, int num)
@@ -416,13 +463,13 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
seq_printf(s,
"%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
i, j,
- __raw_readl(GPIO_CNF(gpio)),
- __raw_readl(GPIO_OE(gpio)),
- __raw_readl(GPIO_OUT(gpio)),
- __raw_readl(GPIO_IN(gpio)),
- __raw_readl(GPIO_INT_STA(gpio)),
- __raw_readl(GPIO_INT_ENB(gpio)),
- __raw_readl(GPIO_INT_LVL(gpio)));
+ tegra_gpio_readl(GPIO_CNF(gpio)),
+ tegra_gpio_readl(GPIO_OE(gpio)),
+ tegra_gpio_readl(GPIO_OUT(gpio)),
+ tegra_gpio_readl(GPIO_IN(gpio)),
+ tegra_gpio_readl(GPIO_INT_STA(gpio)),
+ tegra_gpio_readl(GPIO_INT_ENB(gpio)),
+ tegra_gpio_readl(GPIO_INT_LVL(gpio)));
}
}
return 0;
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 9bc7b03269df..8cdb4b45b30a 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -458,6 +458,9 @@ static const struct hid_device_id apple_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_KEYBOARD },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO),
+ .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+ APPLE_ISO_KEYBOARD },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
@@ -508,6 +511,12 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 91adcc5bad28..848a56c0279c 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1362,6 +1362,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS) },
@@ -1374,6 +1377,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },
@@ -1942,6 +1946,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 1680e99b4816..06ce996b8b65 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -112,6 +112,9 @@
#define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f
#define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250
#define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
@@ -121,6 +124,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index fa5d7a1ffa9e..f1c909f1b239 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -291,7 +291,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->last_slot_field = usage->hid;
td->last_field_index = field->index;
td->last_mt_collection = usage->collection_index;
- hdev->quirks &= ~HID_QUIRK_MULTITOUCH;
return 1;
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
@@ -530,44 +529,12 @@ static void mt_set_input_mode(struct hid_device *hdev)
}
}
-/* a list of devices for which there is a specialized multitouch driver */
-static const struct hid_device_id mt_have_special_driver[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0001) },
- { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0006) },
- { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
- USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
- { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
- USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
- { }
-};
-
-static bool mt_match_one_id(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- return id->bus == hdev->bus &&
- (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
- (id->product == HID_ANY_ID || id->product == hdev->product);
-}
-
-static const struct hid_device_id *mt_match_id(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- for (; id->bus; id++)
- if (mt_match_one_id(hdev, id))
- return id;
-
- return NULL;
-}
-
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
- if (mt_match_id(hdev, mt_have_special_driver))
- return -ENODEV;
-
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
mtclass = &(mt_classes[i]);
@@ -575,6 +542,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
+ /* This allows the driver to correctly support devices
+ * that emit events over several HID messages.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
td = kzalloc(sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
@@ -590,16 +561,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret != 0)
goto fail;
- hdev->quirks |= HID_QUIRK_MULTITOUCH;
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
goto fail;
- /* This allows the driver to correctly support devices
- * that emit events over several HID messages.
- */
- hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
-
td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
GFP_KERNEL);
if (!td->slots) {
@@ -793,10 +758,6 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_XAT,
USB_DEVICE_ID_XAT_CSR) },
- /* Rest of the world */
- { .driver_data = MT_CLS_DEFAULT,
- HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
-
{ }
};
MODULE_DEVICE_TABLE(hid, mt_devices);
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index 5666e7587b18..56ce12c23b02 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -162,27 +162,27 @@ static int roccat_open(struct inode *inode, struct file *file)
device = devices[minor];
- mutex_lock(&device->readers_lock);
-
if (!device) {
pr_emerg("roccat device with minor %d doesn't exist\n", minor);
error = -ENODEV;
- goto exit_err;
+ goto exit_err_devices;
}
+ mutex_lock(&device->readers_lock);
+
if (!device->open++) {
/* power on device on adding first reader */
error = hid_hw_power(device->hid, PM_HINT_FULLON);
if (error < 0) {
--device->open;
- goto exit_err;
+ goto exit_err_readers;
}
error = hid_hw_open(device->hid);
if (error < 0) {
hid_hw_power(device->hid, PM_HINT_NORMAL);
--device->open;
- goto exit_err;
+ goto exit_err_readers;
}
}
@@ -193,13 +193,13 @@ static int roccat_open(struct inode *inode, struct file *file)
list_add_tail(&reader->node, &device->readers);
file->private_data = reader;
-exit_unlock:
+exit_err_readers:
mutex_unlock(&device->readers_lock);
+exit_err_devices:
mutex_unlock(&devices_lock);
+ if (error)
+ kfree(reader);
return error;
-exit_err:
- kfree(reader);
- goto exit_unlock;
}
static int roccat_release(struct inode *inode, struct file *file)
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9b347acf1559..9ec854ae118b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -335,6 +335,7 @@ config SENSORS_I5K_AMB
config SENSORS_F71805F
tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG"
+ depends on !PPC
help
If you say yes here you get support for hardware monitoring
features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG
@@ -345,6 +346,7 @@ config SENSORS_F71805F
config SENSORS_F71882FG
tristate "Fintek F71882FG and compatibles"
+ depends on !PPC
help
If you say yes here you get support for hardware monitoring
features of many Fintek Super-I/O (LPC) chips. The currently
@@ -468,6 +470,7 @@ config SENSORS_IBMPEX
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for ITE IT8705F, IT8712F,
@@ -824,6 +827,7 @@ config SENSORS_NTC_THERMISTOR
config SENSORS_PC87360
tristate "National Semiconductor PC87360 family"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get access to the hardware monitoring
@@ -837,6 +841,7 @@ config SENSORS_PC87360
config SENSORS_PC87427
tristate "National Semiconductor PC87427"
+ depends on !PPC
help
If you say yes here you get access to the hardware monitoring
functions of the National Semiconductor PC87427 Super-I/O chip.
@@ -928,7 +933,7 @@ config SENSORS_SMM665
config SENSORS_DME1737
tristate "SMSC DME1737, SCH311x and compatibles"
- depends on I2C && EXPERIMENTAL
+ depends on I2C && EXPERIMENTAL && !PPC
select HWMON_VID
help
If you say yes here you get support for the hardware monitoring
@@ -970,6 +975,7 @@ config SENSORS_EMC6W201
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
+ depends on !PPC
help
If you say yes here you get support for the integrated fan
monitoring and control capabilities of the SMSC LPC47B27x,
@@ -1003,7 +1009,7 @@ config SENSORS_SMSC47M192
config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC"
- depends on EXPERIMENTAL
+ depends on EXPERIMENTAL && !PPC
help
If you say yes here you get support for the SMSC LPC47B397-NC
sensor chip.
@@ -1017,6 +1023,7 @@ config SENSORS_SCH56XX_COMMON
config SENSORS_SCH5627
tristate "SMSC SCH5627"
+ depends on !PPC
select SENSORS_SCH56XX_COMMON
help
If you say yes here you get support for the hardware monitoring
@@ -1027,6 +1034,7 @@ config SENSORS_SCH5627
config SENSORS_SCH5636
tristate "SMSC SCH5636"
+ depends on !PPC
select SENSORS_SCH56XX_COMMON
help
SMSC SCH5636 Super I/O chips include an embedded microcontroller for
@@ -1150,6 +1158,7 @@ config SENSORS_VIA686A
config SENSORS_VT1211
tristate "VIA VT1211"
+ depends on !PPC
select HWMON_VID
help
If you say yes here then you get support for hardware monitoring
@@ -1262,6 +1271,7 @@ config SENSORS_W83L786NG
config SENSORS_W83627HF
tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for the Winbond W836X7 series
@@ -1272,7 +1282,8 @@ config SENSORS_W83627HF
will be called w83627hf.
config SENSORS_W83627EHF
- tristate "Winbond W83627EHF/EHG/DHG, W83667HG, NCT6775F, NCT6776F"
+ tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG, NCT6775F, NCT6776F"
+ depends on !PPC
select HWMON_VID
help
If you say yes here you get support for the hardware
@@ -1281,7 +1292,8 @@ config SENSORS_W83627EHF
This driver also supports the W83627EHG, which is the lead-free
version of the W83627EHF, and the W83627DHG, which is a similar
chip suited for specific Intel processors that use PECI such as
- the Core 2 Duo.
+ the Core 2 Duo. And also the W83627UHG, which is a stripped down
+ version of the W83627DHG (as far as hardware monitoring goes.)
This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F
(also known as W83667HG-I), and NCT6776F.
diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c
index d46c0c758ddf..df29a7fff9e7 100644
--- a/drivers/hwmon/ad7414.c
+++ b/drivers/hwmon/ad7414.c
@@ -58,10 +58,9 @@ static inline int ad7414_temp_from_reg(s16 reg)
static inline int ad7414_read(struct i2c_client *client, u8 reg)
{
- if (reg == AD7414_REG_TEMP) {
- int value = i2c_smbus_read_word_data(client, reg);
- return (value < 0) ? value : swab16(value);
- } else
+ if (reg == AD7414_REG_TEMP)
+ return i2c_smbus_read_word_swapped(client, reg);
+ else
return i2c_smbus_read_byte_data(client, reg);
}
diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
index ffc781fec185..8cb718ce8237 100644
--- a/drivers/hwmon/ad7418.c
+++ b/drivers/hwmon/ad7418.c
@@ -76,20 +76,6 @@ static struct i2c_driver ad7418_driver = {
.id_table = ad7418_id,
};
-/* All registers are word-sized, except for the configuration registers.
- * AD7418 uses a high-byte first convention. Do NOT use those functions to
- * access the configuration registers CONF and CONF2, as they are byte-sized.
- */
-static inline int ad7418_read(struct i2c_client *client, u8 reg)
-{
- return swab16(i2c_smbus_read_word_data(client, reg));
-}
-
-static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ad7418_init_client(struct i2c_client *client)
{
struct ad7418_data *data = i2c_get_clientdata(client);
@@ -128,7 +114,9 @@ static struct ad7418_data *ad7418_update_device(struct device *dev)
udelay(30);
for (i = 0; i < 3; i++) {
- data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
+ data->temp[i] =
+ i2c_smbus_read_word_swapped(client,
+ AD7418_REG_TEMP[i]);
}
for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
@@ -138,11 +126,12 @@ static struct ad7418_data *ad7418_update_device(struct device *dev)
udelay(15);
data->in[data->adc_max - 1 - i] =
- ad7418_read(client, AD7418_REG_ADC);
+ i2c_smbus_read_word_swapped(client,
+ AD7418_REG_ADC);
}
/* restore old configuration value */
- ad7418_write(client, AD7418_REG_CONF, cfg);
+ i2c_smbus_write_word_swapped(client, AD7418_REG_CONF, cfg);
data->last_updated = jiffies;
data->valid = 1;
@@ -182,7 +171,9 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->lock);
data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
- ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client,
+ AD7418_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->lock);
return count;
}
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c
index e9beeda4cbe5..eedca3cf9968 100644
--- a/drivers/hwmon/ads1015.c
+++ b/drivers/hwmon/ads1015.c
@@ -59,19 +59,6 @@ struct ads1015_data {
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
};
-static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg)
-{
- s32 data = i2c_smbus_read_word_data(client, reg);
-
- return (data < 0) ? data : swab16(data);
-}
-
-static s32 ads1015_write_reg(struct i2c_client *client, unsigned int reg,
- u16 val)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(val));
-}
-
static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
int *value)
{
@@ -87,7 +74,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
mutex_lock(&data->update_lock);
/* get channel parameters */
- res = ads1015_read_reg(client, ADS1015_CONFIG);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
@@ -101,13 +88,13 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
config |= (pga & 0x0007) << 9;
config |= (data_rate & 0x0007) << 5;
- res = ads1015_write_reg(client, ADS1015_CONFIG, config);
+ res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config);
if (res < 0)
goto err_unlock;
/* wait until conversion finished */
msleep(conversion_time_ms);
- res = ads1015_read_reg(client, ADS1015_CONFIG);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
@@ -117,7 +104,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
goto err_unlock;
}
- res = ads1015_read_reg(client, ADS1015_CONVERSION);
+ res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION);
if (res < 0)
goto err_unlock;
conversion = res;
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index c42c5a69a664..cfcc3b6fb6bf 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -74,13 +74,6 @@ static int ads7828_detect(struct i2c_client *client,
static int ads7828_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-/* The ADS7828 returns the 12-bit sample in two bytes,
- these are read as a word then byte-swapped */
-static u16 ads7828_read_value(struct i2c_client *client, u8 reg)
-{
- return swab16(i2c_smbus_read_word_data(client, reg));
-}
-
static inline u8 channel_cmd_byte(int ch)
{
/* cmd byte C2,C1,C0 - see datasheet */
@@ -104,7 +97,8 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
for (ch = 0; ch < ADS7828_NCH; ch++) {
u8 cmd = channel_cmd_byte(ch);
- data->adc_input[ch] = ads7828_read_value(client, cmd);
+ data->adc_input[ch] =
+ i2c_smbus_read_word_swapped(client, cmd);
}
data->last_updated = jiffies;
data->valid = 1;
@@ -203,7 +197,7 @@ static int ads7828_detect(struct i2c_client *client,
for (ch = 0; ch < ADS7828_NCH; ch++) {
u16 in_data;
u8 cmd = channel_cmd_byte(ch);
- in_data = ads7828_read_value(client, cmd);
+ in_data = i2c_smbus_read_word_swapped(client, cmd);
if (in_data & 0xF000) {
pr_debug("%s : Doesn't look like an ads7828 device\n",
__func__);
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index c02a052d3085..d7bd1f3f2a31 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -829,17 +829,17 @@ static int asb100_read_value(struct i2c_client *client, u16 reg)
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x50: /* TEMP */
- res = swab16(i2c_smbus_read_word_data(cl, 0));
+ res = i2c_smbus_read_word_swapped(cl, 0);
break;
case 0x52: /* CONFIG */
res = i2c_smbus_read_byte_data(cl, 1);
break;
case 0x53: /* HYST */
- res = swab16(i2c_smbus_read_word_data(cl, 2));
+ res = i2c_smbus_read_word_swapped(cl, 2);
break;
case 0x55: /* MAX */
default:
- res = swab16(i2c_smbus_read_word_data(cl, 3));
+ res = i2c_smbus_read_word_swapped(cl, 3);
break;
}
}
@@ -877,10 +877,10 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value)
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
- i2c_smbus_write_word_data(cl, 2, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 2, value);
break;
case 0x55: /* MAX */
- i2c_smbus_write_word_data(cl, 3, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 3, value);
break;
}
}
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index e11363467a8d..ef1ac996752e 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -80,24 +80,6 @@ struct ds1621_data {
u8 conf; /* Register encoding, combined */
};
-/* Temperature registers are word-sized.
- DS1621 uses a high-byte first convention, which is exactly opposite to
- the SMBus standard. */
-static int ds1621_read_temp(struct i2c_client *client, u8 reg)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ds1621_init_client(struct i2c_client *client)
{
u8 conf, new_conf;
@@ -136,7 +118,7 @@ static struct ds1621_data *ds1621_update_client(struct device *dev)
data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
for (i = 0; i < ARRAY_SIZE(data->temp); i++)
- data->temp[i] = ds1621_read_temp(client,
+ data->temp[i] = i2c_smbus_read_word_swapped(client,
DS1621_REG_TEMP[i]);
/* reset alarms if necessary */
@@ -177,8 +159,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
- ds1621_write_temp(client, DS1621_REG_TEMP[attr->index],
- data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index 4f7c3fc40a89..225ae4f36583 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -75,33 +75,13 @@ struct ds620_data {
s16 temp[3]; /* Register values, word */
};
-/*
- * Temperature registers are word-sized.
- * DS620 uses a high-byte first convention, which is exactly opposite to
- * the SMBus standard.
- */
-static int ds620_read_temp(struct i2c_client *client, u8 reg)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static void ds620_init_client(struct i2c_client *client)
{
struct ds620_platform_data *ds620_info = client->dev.platform_data;
u16 conf, new_conf;
new_conf = conf =
- swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF));
+ i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
/* switch to continuous conversion mode */
new_conf &= ~DS620_REG_CONFIG_1SHOT;
@@ -118,8 +98,7 @@ static void ds620_init_client(struct i2c_client *client)
new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0;
if (conf != new_conf)
- i2c_smbus_write_word_data(client, DS620_REG_CONF,
- swab16(new_conf));
+ i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf);
/* start conversion */
i2c_smbus_write_byte(client, DS620_COM_START);
@@ -141,8 +120,8 @@ static struct ds620_data *ds620_update_client(struct device *dev)
dev_dbg(&client->dev, "Starting ds620 update\n");
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
- res = ds620_read_temp(client,
- DS620_REG_TEMP[i]);
+ res = i2c_smbus_read_word_swapped(client,
+ DS620_REG_TEMP[i]);
if (res < 0) {
ret = ERR_PTR(res);
goto abort;
@@ -191,8 +170,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
- ds620_write_temp(client, DS620_REG_TEMP[attr->index],
- data->temp[attr->index]);
+ i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index],
+ data->temp[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
@@ -210,16 +189,15 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
return PTR_ERR(data);
/* reset alarms if necessary */
- res = i2c_smbus_read_word_data(client, DS620_REG_CONF);
+ res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
if (res < 0)
return res;
- conf = swab16(res);
- new_conf = conf;
+ new_conf = conf = res;
new_conf &= ~attr->index;
if (conf != new_conf) {
- res = i2c_smbus_write_word_data(client, DS620_REG_CONF,
- swab16(new_conf));
+ res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF,
+ new_conf);
if (res < 0)
return res;
}
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index e7ae5743e181..a13e2da97e30 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -591,7 +591,7 @@ static int gl518_remove(struct i2c_client *client)
static int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
else
return i2c_smbus_read_byte_data(client, reg);
}
@@ -599,7 +599,7 @@ static int gl518_read_value(struct i2c_client *client, u8 reg)
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
else
return i2c_smbus_write_byte_data(client, reg, value);
}
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 131ea8625f08..cd6085bbfba7 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -821,7 +821,7 @@ static int gl520_remove(struct i2c_client *client)
static int gl520_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
else
return i2c_smbus_read_byte_data(client, reg);
}
@@ -829,7 +829,7 @@ static int gl520_read_value(struct i2c_client *client, u8 reg)
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
else
return i2c_smbus_write_byte_data(client, reg, value);
}
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index a61e7815a2a9..6460487e41b5 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -27,8 +27,7 @@
static struct class *hwmon_class;
-static DEFINE_IDR(hwmon_idr);
-static DEFINE_SPINLOCK(idr_lock);
+static DEFINE_IDA(hwmon_ida);
/**
* hwmon_device_register - register w/ hwmon
@@ -42,30 +41,17 @@ static DEFINE_SPINLOCK(idr_lock);
struct device *hwmon_device_register(struct device *dev)
{
struct device *hwdev;
- int id, err;
-
-again:
- if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0))
- return ERR_PTR(-ENOMEM);
-
- spin_lock(&idr_lock);
- err = idr_get_new(&hwmon_idr, NULL, &id);
- spin_unlock(&idr_lock);
+ int id;
- if (unlikely(err == -EAGAIN))
- goto again;
- else if (unlikely(err))
- return ERR_PTR(err);
+ id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
- id = id & MAX_ID_MASK;
hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL,
HWMON_ID_FORMAT, id);
- if (IS_ERR(hwdev)) {
- spin_lock(&idr_lock);
- idr_remove(&hwmon_idr, id);
- spin_unlock(&idr_lock);
- }
+ if (IS_ERR(hwdev))
+ ida_simple_remove(&hwmon_ida, id);
return hwdev;
}
@@ -81,9 +67,7 @@ void hwmon_device_unregister(struct device *dev)
if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
device_unregister(dev);
- spin_lock(&idr_lock);
- idr_remove(&hwmon_idr, id);
- spin_unlock(&idr_lock);
+ ida_simple_remove(&hwmon_ida, id);
} else
dev_dbg(dev->parent,
"hwmon_device_unregister() failed: bad class ID!\n");
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index c316294c48b4..6a967d7dbdee 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -88,8 +88,7 @@
#define AEM_MIN_POWER_INTERVAL 200
#define UJ_PER_MJ 1000L
-static DEFINE_IDR(aem_idr);
-static DEFINE_SPINLOCK(aem_idr_lock);
+static DEFINE_IDA(aem_ida);
static struct platform_driver aem_driver = {
.driver = {
@@ -148,8 +147,9 @@ struct aem_data {
int id;
struct aem_ipmi_data ipmi;
- /* Function to update sensors */
+ /* Function and buffer to update sensors */
void (*update)(struct aem_data *data);
+ struct aem_read_sensor_resp *rs_resp;
/*
* AEM 1.x sensors:
@@ -246,8 +246,6 @@ static void aem_bmc_gone(int iface);
static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
static void aem_remove_sensors(struct aem_data *data);
-static int aem_init_aem1(struct aem_ipmi_data *probe);
-static int aem_init_aem2(struct aem_ipmi_data *probe);
static int aem1_find_sensors(struct aem_data *data);
static int aem2_find_sensors(struct aem_data *data);
static void update_aem1_sensors(struct aem_data *data);
@@ -356,47 +354,16 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
complete(&data->read_complete);
}
-/* ID functions */
-
-/* Obtain an id */
-static int aem_idr_get(int *id)
-{
- int i, err;
-
-again:
- if (unlikely(!idr_pre_get(&aem_idr, GFP_KERNEL)))
- return -ENOMEM;
-
- spin_lock(&aem_idr_lock);
- err = idr_get_new(&aem_idr, NULL, &i);
- spin_unlock(&aem_idr_lock);
-
- if (unlikely(err == -EAGAIN))
- goto again;
- else if (unlikely(err))
- return err;
-
- *id = i & MAX_ID_MASK;
- return 0;
-}
-
-/* Release an object ID */
-static void aem_idr_put(int id)
-{
- spin_lock(&aem_idr_lock);
- idr_remove(&aem_idr, id);
- spin_unlock(&aem_idr_lock);
-}
-
/* Sensor support functions */
-/* Read a sensor value */
+/* Read a sensor value; must be called with data->lock held */
static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
void *buf, size_t size)
{
int rs_size, res;
struct aem_read_sensor_req rs_req;
- struct aem_read_sensor_resp *rs_resp;
+ /* Use preallocated rx buffer */
+ struct aem_read_sensor_resp *rs_resp = data->rs_resp;
struct aem_ipmi_data *ipmi = &data->ipmi;
/* AEM registers are 1, 2, 4 or 8 bytes */
@@ -422,10 +389,6 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
ipmi->tx_message.data_len = sizeof(rs_req);
rs_size = sizeof(*rs_resp) + size;
- rs_resp = kzalloc(rs_size, GFP_KERNEL);
- if (!rs_resp)
- return -ENOMEM;
-
ipmi->rx_msg_data = rs_resp;
ipmi->rx_msg_len = rs_size;
@@ -468,7 +431,6 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
res = 0;
out:
- kfree(rs_resp);
return res;
}
@@ -526,11 +488,12 @@ static void aem_delete(struct aem_data *data)
{
list_del(&data->list);
aem_remove_sensors(data);
+ kfree(data->rs_resp);
hwmon_device_unregister(data->hwmon_dev);
ipmi_destroy_user(data->ipmi.user);
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
kfree(data);
}
@@ -587,7 +550,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- if (aem_idr_get(&data->id))
+ data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ if (data->id < 0)
goto id_err;
data->pdev = platform_device_alloc(DRVNAME, data->id);
@@ -602,24 +566,31 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
platform_set_drvdata(data->pdev, data);
/* Set up IPMI interface */
- if (aem_init_ipmi_data(&data->ipmi, probe->interface,
- probe->bmc_device))
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
goto ipmi_err;
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
-
if (IS_ERR(data->hwmon_dev)) {
dev_err(&data->pdev->dev, "Unable to register hwmon "
"device for IPMI interface %d\n",
probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
}
data->update = update_aem1_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
/* Find sensors */
- if (aem1_find_sensors(data))
+ res = aem1_find_sensors(data);
+ if (res)
goto sensor_err;
/* Add to our list of AEM devices */
@@ -631,6 +602,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
return 0;
sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
hwmon_device_unregister(data->hwmon_dev);
hwmon_reg_err:
ipmi_destroy_user(data->ipmi.user);
@@ -638,7 +611,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
id_err:
kfree(data);
@@ -646,7 +619,7 @@ id_err:
}
/* Find and initialize all AEM1 instances */
-static int aem_init_aem1(struct aem_ipmi_data *probe)
+static void aem_init_aem1(struct aem_ipmi_data *probe)
{
int num, i, err;
@@ -657,11 +630,8 @@ static int aem_init_aem1(struct aem_ipmi_data *probe)
dev_err(probe->bmc_device,
"Error %d initializing AEM1 0x%X\n",
err, i);
- return err;
}
}
-
- return 0;
}
/* Probe functions for AEM2 devices */
@@ -720,7 +690,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- if (aem_idr_get(&data->id))
+ data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ if (data->id < 0)
goto id_err;
data->pdev = platform_device_alloc(DRVNAME, data->id);
@@ -735,24 +706,31 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
platform_set_drvdata(data->pdev, data);
/* Set up IPMI interface */
- if (aem_init_ipmi_data(&data->ipmi, probe->interface,
- probe->bmc_device))
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
goto ipmi_err;
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
-
if (IS_ERR(data->hwmon_dev)) {
dev_err(&data->pdev->dev, "Unable to register hwmon "
"device for IPMI interface %d\n",
probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
}
data->update = update_aem2_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
/* Find sensors */
- if (aem2_find_sensors(data))
+ res = aem2_find_sensors(data);
+ if (res)
goto sensor_err;
/* Add to our list of AEM devices */
@@ -764,6 +742,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
return 0;
sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
hwmon_device_unregister(data->hwmon_dev);
hwmon_reg_err:
ipmi_destroy_user(data->ipmi.user);
@@ -771,7 +751,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- aem_idr_put(data->id);
+ ida_simple_remove(&aem_ida, data->id);
id_err:
kfree(data);
@@ -779,7 +759,7 @@ id_err:
}
/* Find and initialize all AEM2 instances */
-static int aem_init_aem2(struct aem_ipmi_data *probe)
+static void aem_init_aem2(struct aem_ipmi_data *probe)
{
struct aem_find_instance_resp fi_resp;
int err;
@@ -798,12 +778,9 @@ static int aem_init_aem2(struct aem_ipmi_data *probe)
dev_err(probe->bmc_device,
"Error %d initializing AEM2 0x%X\n",
err, fi_resp.module_handle);
- return err;
}
i++;
}
-
- return 0;
}
/* Probe a BMC for AEM firmware instances */
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 02cebb74e206..2d3d72805ff4 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -154,8 +154,6 @@ static int jc42_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info);
static int jc42_remove(struct i2c_client *client);
-static int jc42_read_value(struct i2c_client *client, u8 reg);
-static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct jc42_data *jc42_update_device(struct device *dev);
@@ -187,7 +185,7 @@ static int jc42_suspend(struct device *dev)
struct jc42_data *data = i2c_get_clientdata(client);
data->config |= JC42_CFG_SHUTDOWN;
- jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config);
return 0;
}
@@ -197,7 +195,7 @@ static int jc42_resume(struct device *dev)
struct jc42_data *data = i2c_get_clientdata(client);
data->config &= ~JC42_CFG_SHUTDOWN;
- jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config);
return 0;
}
@@ -315,7 +313,7 @@ static ssize_t set_##value(struct device *dev, \
return -EINVAL; \
mutex_lock(&data->update_lock); \
data->value = jc42_temp_to_reg(val, data->extended); \
- err = jc42_write_value(client, reg, data->value); \
+ err = i2c_smbus_write_word_swapped(client, reg, data->value); \
if (err < 0) \
ret = err; \
mutex_unlock(&data->update_lock); \
@@ -357,7 +355,8 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
data->config = (data->config
& ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT))
| (hyst << JC42_CFG_HYST_SHIFT);
- err = jc42_write_value(client, JC42_REG_CONFIG, data->config);
+ err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
+ data->config);
if (err < 0)
ret = err;
mutex_unlock(&data->update_lock);
@@ -452,10 +451,10 @@ static int jc42_detect(struct i2c_client *new_client,
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- cap = jc42_read_value(new_client, JC42_REG_CAP);
- config = jc42_read_value(new_client, JC42_REG_CONFIG);
- manid = jc42_read_value(new_client, JC42_REG_MANID);
- devid = jc42_read_value(new_client, JC42_REG_DEVICEID);
+ cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP);
+ config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG);
+ manid = i2c_smbus_read_word_swapped(new_client, JC42_REG_MANID);
+ devid = i2c_smbus_read_word_swapped(new_client, JC42_REG_DEVICEID);
if (cap < 0 || config < 0 || manid < 0 || devid < 0)
return -ENODEV;
@@ -489,14 +488,14 @@ static int jc42_probe(struct i2c_client *new_client,
i2c_set_clientdata(new_client, data);
mutex_init(&data->update_lock);
- cap = jc42_read_value(new_client, JC42_REG_CAP);
+ cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP);
if (cap < 0) {
err = -EINVAL;
goto exit_free;
}
data->extended = !!(cap & JC42_CAP_RANGE);
- config = jc42_read_value(new_client, JC42_REG_CONFIG);
+ config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG);
if (config < 0) {
err = -EINVAL;
goto exit_free;
@@ -504,7 +503,8 @@ static int jc42_probe(struct i2c_client *new_client,
data->orig_config = config;
if (config & JC42_CFG_SHUTDOWN) {
config &= ~JC42_CFG_SHUTDOWN;
- jc42_write_value(new_client, JC42_REG_CONFIG, config);
+ i2c_smbus_write_word_swapped(new_client, JC42_REG_CONFIG,
+ config);
}
data->config = config;
@@ -535,25 +535,12 @@ static int jc42_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &jc42_group);
if (data->config != data->orig_config)
- jc42_write_value(client, JC42_REG_CONFIG, data->orig_config);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
+ data->orig_config);
kfree(data);
return 0;
}
-/* All registers are word-sized. */
-static int jc42_read_value(struct i2c_client *client, u8 reg)
-{
- int ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- return swab16(ret);
-}
-
-static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(value));
-}
-
static struct jc42_data *jc42_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -564,28 +551,29 @@ static struct jc42_data *jc42_update_device(struct device *dev)
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- val = jc42_read_value(client, JC42_REG_TEMP);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_input = val;
- val = jc42_read_value(client, JC42_REG_TEMP_CRITICAL);
+ val = i2c_smbus_read_word_swapped(client,
+ JC42_REG_TEMP_CRITICAL);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_crit = val;
- val = jc42_read_value(client, JC42_REG_TEMP_LOWER);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_LOWER);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_min = val;
- val = jc42_read_value(client, JC42_REG_TEMP_UPPER);
+ val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_UPPER);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
index 29b9030d42c3..9e64d96620d3 100644
--- a/drivers/hwmon/lm73.c
+++ b/drivers/hwmon/lm73.c
@@ -34,7 +34,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
#define LM73_REG_CTRL 0x04
#define LM73_REG_ID 0x07
-#define LM73_ID 0x9001 /* or 0x190 after a swab16() */
+#define LM73_ID 0x9001 /* 0x0190, byte-swapped */
#define DRVNAME "lm73"
#define LM73_TEMP_MIN (-40)
#define LM73_TEMP_MAX 150
@@ -57,7 +57,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
/* Write value */
value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4),
(LM73_TEMP_MAX*4)) << 5;
- i2c_smbus_write_word_data(client, attr->index, swab16(value));
+ i2c_smbus_write_word_swapped(client, attr->index, value);
return count;
}
@@ -68,8 +68,8 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da,
struct i2c_client *client = to_i2c_client(dev);
/* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */
- int temp = ((s16) (swab16(i2c_smbus_read_word_data(client,
- attr->index)))*250) / 32;
+ int temp = ((s16) (i2c_smbus_read_word_swapped(client,
+ attr->index))*250) / 32;
return sprintf(buf, "%d\n", temp);
}
@@ -150,17 +150,31 @@ static int lm73_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
- u16 id;
- u8 ctrl;
+ int id, ctrl, conf;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
+ /*
+ * Do as much detection as possible with byte reads first, as word
+ * reads can confuse other devices.
+ */
+ ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
+ if (ctrl < 0 || (ctrl & 0x10))
+ return -ENODEV;
+
+ conf = i2c_smbus_read_byte_data(new_client, LM73_REG_CONF);
+ if (conf < 0 || (conf & 0x0c))
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(new_client, LM73_REG_ID);
+ if (id < 0 || id != (LM73_ID & 0xff))
+ return -ENODEV;
+
/* Check device ID */
id = i2c_smbus_read_word_data(new_client, LM73_REG_ID);
- ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
- if ((id != LM73_ID) || (ctrl & 0x10))
+ if (id < 0 || id != LM73_ID)
return -ENODEV;
strlcpy(info->type, "lm73", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 90126a2a1e44..1888dd0fc05f 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -384,13 +384,10 @@ static struct i2c_driver lm75_driver = {
*/
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
- int value;
-
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
-
- value = i2c_smbus_read_word_data(client, reg);
- return (value < 0) ? value : swab16(value);
+ else
+ return i2c_smbus_read_word_swapped(client, reg);
}
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
@@ -398,7 +395,7 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
if (reg == LM75_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
}
static struct lm75_data *lm75_update_device(struct device *dev)
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index b28a297be50c..8dfc6782d596 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -365,7 +365,7 @@ static u16 lm77_read_value(struct i2c_client *client, u8 reg)
if (reg == LM77_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
- return swab16(i2c_smbus_read_word_data(client, reg));
+ return i2c_smbus_read_word_swapped(client, reg);
}
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
@@ -373,7 +373,7 @@ static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
if (reg == LM77_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
- return i2c_smbus_write_word_data(client, reg, swab16(value));
+ return i2c_smbus_write_word_swapped(client, reg, value);
}
static void lm77_init_client(struct i2c_client *client)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 90ddb8774210..615bc4f4e530 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1105,41 +1105,37 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm90_detect(struct i2c_client *new_client,
+static int lm90_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
- struct i2c_adapter *adapter = new_client->adapter;
- int address = new_client->addr;
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
const char *name = NULL;
- int man_id, chip_id, reg_config1, reg_config2, reg_convrate;
+ int man_id, chip_id, config1, config2, convrate;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* detection and identification */
- if ((man_id = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_MAN_ID)) < 0
- || (chip_id = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CHIP_ID)) < 0
- || (reg_config1 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG1)) < 0
- || (reg_convrate = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONVRATE)) < 0)
+ man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID);
+ config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
+ convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE);
+ if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
return -ENODEV;
if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) {
- reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG2);
- if (reg_config2 < 0)
+ config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2);
+ if (config2 < 0)
return -ENODEV;
} else
- reg_config2 = 0; /* Make compiler happy */
+ config2 = 0; /* Make compiler happy */
if ((address == 0x4C || address == 0x4D)
&& man_id == 0x01) { /* National Semiconductor */
- if ((reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xF8) == 0x00
- && reg_convrate <= 0x09) {
+ if ((config1 & 0x2A) == 0x00
+ && (config2 & 0xF8) == 0x00
+ && convrate <= 0x09) {
if (address == 0x4C
&& (chip_id & 0xF0) == 0x20) { /* LM90 */
name = "lm90";
@@ -1163,8 +1159,8 @@ static int lm90_detect(struct i2c_client *new_client,
if ((address == 0x4C || address == 0x4D)
&& man_id == 0x41) { /* Analog Devices */
if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
- && (reg_config1 & 0x3F) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x3F) == 0x00
+ && convrate <= 0x0A) {
name = "adm1032";
/* The ADM1032 supports PEC, but only if combined
transactions are not used. */
@@ -1173,18 +1169,18 @@ static int lm90_detect(struct i2c_client *new_client,
info->flags |= I2C_CLIENT_PEC;
} else
if (chip_id == 0x51 /* ADT7461 */
- && (reg_config1 & 0x1B) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x1B) == 0x00
+ && convrate <= 0x0A) {
name = "adt7461";
} else
if (chip_id == 0x57 /* ADT7461A, NCT1008 */
- && (reg_config1 & 0x1B) == 0x00
- && reg_convrate <= 0x0A) {
+ && (config1 & 0x1B) == 0x00
+ && convrate <= 0x0A) {
name = "adt7461a";
}
} else
if (man_id == 0x4D) { /* Maxim */
- int reg_emerg, reg_emerg2, reg_status2;
+ int emerg, emerg2, status2;
/*
* We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read
@@ -1192,13 +1188,15 @@ static int lm90_detect(struct i2c_client *new_client,
* exists, both readings will reflect the same value. Otherwise,
* the readings will be different.
*/
- if ((reg_emerg = i2c_smbus_read_byte_data(new_client,
- MAX6659_REG_R_REMOTE_EMERG)) < 0
- || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0
- || (reg_emerg2 = i2c_smbus_read_byte_data(new_client,
- MAX6659_REG_R_REMOTE_EMERG)) < 0
- || (reg_status2 = i2c_smbus_read_byte_data(new_client,
- MAX6696_REG_R_STATUS2)) < 0)
+ emerg = i2c_smbus_read_byte_data(client,
+ MAX6659_REG_R_REMOTE_EMERG);
+ man_id = i2c_smbus_read_byte_data(client,
+ LM90_REG_R_MAN_ID);
+ emerg2 = i2c_smbus_read_byte_data(client,
+ MAX6659_REG_R_REMOTE_EMERG);
+ status2 = i2c_smbus_read_byte_data(client,
+ MAX6696_REG_R_STATUS2);
+ if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0)
return -ENODEV;
/*
@@ -1216,8 +1214,8 @@ static int lm90_detect(struct i2c_client *new_client,
*/
if (chip_id == man_id
&& (address == 0x4C || address == 0x4D || address == 0x4E)
- && (reg_config1 & 0x1F) == (man_id & 0x0F)
- && reg_convrate <= 0x09) {
+ && (config1 & 0x1F) == (man_id & 0x0F)
+ && convrate <= 0x09) {
if (address == 0x4C)
name = "max6657";
else
@@ -1235,10 +1233,10 @@ static int lm90_detect(struct i2c_client *new_client,
* one of those registers exists.
*/
if (chip_id == 0x01
- && (reg_config1 & 0x10) == 0x00
- && (reg_status2 & 0x01) == 0x00
- && reg_emerg == reg_emerg2
- && reg_convrate <= 0x07) {
+ && (config1 & 0x10) == 0x00
+ && (status2 & 0x01) == 0x00
+ && emerg == emerg2
+ && convrate <= 0x07) {
name = "max6696";
} else
/*
@@ -1248,8 +1246,8 @@ static int lm90_detect(struct i2c_client *new_client,
* second to last bit of config1 (software reset).
*/
if (chip_id == 0x01
- && (reg_config1 & 0x03) == 0x00
- && reg_convrate <= 0x07) {
+ && (config1 & 0x03) == 0x00
+ && convrate <= 0x07) {
name = "max6680";
} else
/*
@@ -1258,21 +1256,21 @@ static int lm90_detect(struct i2c_client *new_client,
* register are unused and should return zero when read.
*/
if (chip_id == 0x59
- && (reg_config1 & 0x3f) == 0x00
- && reg_convrate <= 0x07) {
+ && (config1 & 0x3f) == 0x00
+ && convrate <= 0x07) {
name = "max6646";
}
} else
if (address == 0x4C
&& man_id == 0x5C) { /* Winbond/Nuvoton */
- if ((reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xF8) == 0x00) {
+ if ((config1 & 0x2A) == 0x00
+ && (config2 & 0xF8) == 0x00) {
if (chip_id == 0x01 /* W83L771W/G */
- && reg_convrate <= 0x09) {
+ && convrate <= 0x09) {
name = "w83l771";
} else
if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
- && reg_convrate <= 0x08) {
+ && convrate <= 0x08) {
name = "w83l771";
}
}
@@ -1280,9 +1278,9 @@ static int lm90_detect(struct i2c_client *new_client,
if (address >= 0x48 && address <= 0x4F
&& man_id == 0xA1) { /* NXP Semiconductor/Philips */
if (chip_id == 0x00
- && (reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xFE) == 0x00
- && reg_convrate <= 0x09) {
+ && (config1 & 0x2A) == 0x00
+ && (config2 & 0xFE) == 0x00
+ && convrate <= 0x09) {
name = "sa56004";
}
}
@@ -1301,19 +1299,18 @@ static int lm90_detect(struct i2c_client *new_client,
static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
{
+ struct device *dev = &client->dev;
+
if (data->flags & LM90_HAVE_TEMP3)
- sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group);
+ sysfs_remove_group(&dev->kobj, &lm90_temp3_group);
if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- sysfs_remove_group(&client->dev.kobj,
- &lm90_emergency_alarm_group);
+ sysfs_remove_group(&dev->kobj, &lm90_emergency_alarm_group);
if (data->flags & LM90_HAVE_EMERGENCY)
- sysfs_remove_group(&client->dev.kobj,
- &lm90_emergency_group);
+ sysfs_remove_group(&dev->kobj, &lm90_emergency_group);
if (data->flags & LM90_HAVE_OFFSET)
- device_remove_file(&client->dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- device_remove_file(&client->dev, &dev_attr_pec);
- sysfs_remove_group(&client->dev.kobj, &lm90_group);
+ device_remove_file(dev, &sensor_dev_attr_temp2_offset.dev_attr);
+ device_remove_file(dev, &dev_attr_pec);
+ sysfs_remove_group(&dev->kobj, &lm90_group);
}
static void lm90_init_client(struct i2c_client *client)
@@ -1362,10 +1359,11 @@ static void lm90_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
}
-static int lm90_probe(struct i2c_client *new_client,
+static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct i2c_adapter *adapter = to_i2c_adapter(new_client->dev.parent);
+ struct device *dev = &client->dev;
+ struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct lm90_data *data;
int err;
@@ -1374,14 +1372,14 @@ static int lm90_probe(struct i2c_client *new_client,
err = -ENOMEM;
goto exit;
}
- i2c_set_clientdata(new_client, data);
+ i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Set the device type */
data->kind = id->driver_data;
if (data->kind == adm1032) {
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- new_client->flags &= ~I2C_CLIENT_PEC;
+ client->flags &= ~I2C_CLIENT_PEC;
}
/* Different devices have different alarm bits triggering the
@@ -1396,43 +1394,41 @@ static int lm90_probe(struct i2c_client *new_client,
data->max_convrate = lm90_params[data->kind].max_convrate;
/* Initialize the LM90 chip */
- lm90_init_client(new_client);
+ lm90_init_client(client);
/* Register sysfs hooks */
- err = sysfs_create_group(&new_client->dev.kobj, &lm90_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_group);
if (err)
goto exit_free;
- if (new_client->flags & I2C_CLIENT_PEC) {
- err = device_create_file(&new_client->dev, &dev_attr_pec);
+ if (client->flags & I2C_CLIENT_PEC) {
+ err = device_create_file(dev, &dev_attr_pec);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_OFFSET) {
- err = device_create_file(&new_client->dev,
+ err = device_create_file(dev,
&sensor_dev_attr_temp2_offset.dev_attr);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm90_emergency_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_emergency_group);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
- err = sysfs_create_group(&new_client->dev.kobj,
+ err = sysfs_create_group(&dev->kobj,
&lm90_emergency_alarm_group);
if (err)
goto exit_remove_files;
}
if (data->flags & LM90_HAVE_TEMP3) {
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm90_temp3_group);
+ err = sysfs_create_group(&dev->kobj, &lm90_temp3_group);
if (err)
goto exit_remove_files;
}
- data->hwmon_dev = hwmon_device_register(&new_client->dev);
+ data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove_files;
@@ -1441,7 +1437,7 @@ static int lm90_probe(struct i2c_client *new_client,
return 0;
exit_remove_files:
- lm90_remove_files(new_client, data);
+ lm90_remove_files(client, data);
exit_free:
kfree(data);
exit:
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 7c31e6205f85..8fcbd4d422c5 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -117,16 +117,16 @@ static struct lm92_data *lm92_update_device(struct device *dev)
if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) {
dev_dbg(&client->dev, "Updating lm92 data\n");
- data->temp1_input = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP));
- data->temp1_hyst = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HYST));
- data->temp1_crit = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_CRIT));
- data->temp1_min = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_LOW));
- data->temp1_max = swab16(i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HIGH));
+ data->temp1_input = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP);
+ data->temp1_hyst = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_HYST);
+ data->temp1_crit = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_CRIT);
+ data->temp1_min = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_LOW);
+ data->temp1_max = i2c_smbus_read_word_swapped(client,
+ LM92_REG_TEMP_HIGH);
data->last_updated = jiffies;
data->valid = 1;
@@ -158,7 +158,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co
\
mutex_lock(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
- i2c_smbus_write_word_data(client, reg, swab16(data->value)); \
+ i2c_smbus_write_word_swapped(client, reg, data->value); \
mutex_unlock(&data->update_lock); \
return count; \
}
@@ -194,8 +194,8 @@ static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *
mutex_lock(&data->update_lock);
data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
- i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST,
- swab16(TEMP_TO_REG(data->temp1_hyst)));
+ i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST,
+ TEMP_TO_REG(data->temp1_hyst));
mutex_unlock(&data->update_lock);
return count;
}
diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c
index dd2d7b9620c2..385886a4f224 100644
--- a/drivers/hwmon/max16065.c
+++ b/drivers/hwmon/max16065.c
@@ -137,10 +137,10 @@ static int max16065_read_adc(struct i2c_client *client, int reg)
{
int rv;
- rv = i2c_smbus_read_word_data(client, reg);
+ rv = i2c_smbus_read_word_swapped(client, reg);
if (unlikely(rv < 0))
return rv;
- return ((rv & 0xff) << 2) | ((rv >> 14) & 0x03);
+ return rv >> 6;
}
static struct max16065_data *max16065_update_device(struct device *dev)
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index d5226c9e1201..ef65ab56b094 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -31,7 +31,7 @@
#define MC13783_ADC_NAME "mc13783-adc"
struct mc13783_adc_priv {
- struct mc13783 *mc13783;
+ struct mc13xxx *mc13xxx;
struct device *hwmon_dev;
};
@@ -51,8 +51,8 @@ static int mc13783_adc_read(struct device *dev,
unsigned int sample[4];
int ret;
- ret = mc13783_adc_do_conversion(priv->mc13783,
- MC13783_ADC_MODE_MULT_CHAN,
+ ret = mc13xxx_adc_do_conversion(priv->mc13xxx,
+ MC13XXX_ADC_MODE_MULT_CHAN,
channel, sample);
if (ret)
return ret;
@@ -147,9 +147,9 @@ static const struct attribute_group mc13783_group_ts = {
static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
- unsigned flags = mc13783_get_flags(priv->mc13783);
+ unsigned flags = mc13xxx_get_flags(priv->mc13xxx);
- return flags & MC13783_USE_TOUCHSCREEN;
+ return flags & MC13XXX_USE_TOUCHSCREEN;
}
static int __init mc13783_adc_probe(struct platform_device *pdev)
@@ -161,7 +161,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, priv);
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 1c8c9812f244..15398780cc00 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -83,25 +83,6 @@ static inline int sht21_rh_ticks_to_per_cent_mille(int ticks)
}
/**
- * sht21_read_word_data() - read word from register
- * @client: I2C client device
- * @reg: I2C command byte
- *
- * Returns value, negative errno on error.
- */
-static inline int sht21_read_word_data(struct i2c_client *client, u8 reg)
-{
- int ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
- return ret;
- /*
- * SMBus specifies low byte first, but the SHT21 returns MSB
- * first, so we have to swab16 the values
- */
- return swab16(ret);
-}
-
-/**
* sht21_update_measurements() - get updated measurements from device
* @client: I2C client device
*
@@ -119,12 +100,13 @@ static int sht21_update_measurements(struct i2c_client *client)
* maximum two measurements per second at 12bit accuracy shall be made.
*/
if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) {
- ret = sht21_read_word_data(client, SHT21_TRIG_T_MEASUREMENT_HM);
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_T_MEASUREMENT_HM);
if (ret < 0)
goto out;
sht21->temperature = sht21_temp_ticks_to_millicelsius(ret);
- ret = sht21_read_word_data(client,
- SHT21_TRIG_RH_MEASUREMENT_HM);
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_RH_MEASUREMENT_HM);
if (ret < 0)
goto out;
sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret);
diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c
index 425df5bccd45..411638181fd8 100644
--- a/drivers/hwmon/smm665.c
+++ b/drivers/hwmon/smm665.c
@@ -214,33 +214,26 @@ static int smm665_read_adc(struct smm665_data *data, int adc)
*
* Neither i2c_smbus_read_byte() nor
* i2c_smbus_read_block_data() worked here,
- * so use i2c_smbus_read_word_data() instead.
+ * so use i2c_smbus_read_word_swapped() instead.
* We could also try to use i2c_master_recv(),
* but that is not always supported.
*/
- rv = i2c_smbus_read_word_data(client, 0);
+ rv = i2c_smbus_read_word_swapped(client, 0);
if (rv < 0) {
dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv);
return -1;
}
/*
* Validate/verify readback adc channel (in bit 11..14).
- * High byte is in lower 8 bit of rv, so only shift by 3.
*/
- radc = (rv >> 3) & 0x0f;
+ radc = (rv >> 11) & 0x0f;
if (radc != adc) {
dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d",
adc, radc);
return -EIO;
}
- /*
- * Chip replies with H/L, while SMBus expects L/H.
- * Thus, byte order is reversed, and we have to swap
- * the result.
- */
- rv = swab16(rv) & SMM665_ADC_MASK;
- return rv;
+ return rv & SMM665_ADC_MASK;
}
static struct smm665_data *smm665_update_device(struct device *dev)
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
index 9fb7516e6f45..65c88ff5645a 100644
--- a/drivers/hwmon/smsc47b397.c
+++ b/drivers/hwmon/smsc47b397.c
@@ -113,7 +113,7 @@ struct smsc47b397_data {
u8 temp[4];
};
-static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg)
+static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg)
{
int res;
@@ -265,7 +265,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev)
return -EBUSY;
}
- if (!(data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL);
+ if (!data) {
err = -ENOMEM;
goto error_release;
}
@@ -276,7 +277,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev)
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
- if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group)))
+ err = sysfs_create_group(&dev->kobj, &smsc47b397_group);
+ if (err)
goto error_free;
data->hwmon_dev = hwmon_device_register(dev);
@@ -345,7 +347,7 @@ static int __init smsc47b397_find(unsigned short *addr)
superio_enter();
id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
- switch(id) {
+ switch (id) {
case 0x81:
name = "SCH5307-NS";
break;
@@ -379,7 +381,8 @@ static int __init smsc47b397_init(void)
unsigned short address;
int ret;
- if ((ret = smsc47b397_find(&address)))
+ ret = smsc47b397_find(&address);
+ if (ret)
return ret;
ret = platform_driver_register(&smsc47b397_driver);
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 5bd194968801..643aa8c94535 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -55,19 +55,6 @@ struct tmp102 {
int temp[3];
};
-/* SMBus specifies low byte first, but the TMP102 returns high byte first,
- * so we have to swab16 the values */
-static inline int tmp102_read_reg(struct i2c_client *client, u8 reg)
-{
- int result = i2c_smbus_read_word_data(client, reg);
- return result < 0 ? result : swab16(result);
-}
-
-static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val)
-{
- return i2c_smbus_write_word_data(client, reg, swab16(val));
-}
-
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
static inline int tmp102_reg_to_mC(s16 val)
{
@@ -94,7 +81,8 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client)
if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
int i;
for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
- int status = tmp102_read_reg(client, tmp102_reg[i]);
+ int status = i2c_smbus_read_word_swapped(client,
+ tmp102_reg[i]);
if (status > -1)
tmp102->temp[i] = tmp102_reg_to_mC(status);
}
@@ -130,8 +118,8 @@ static ssize_t tmp102_set_temp(struct device *dev,
mutex_lock(&tmp102->lock);
tmp102->temp[sda->index] = val;
- status = tmp102_write_reg(client, tmp102_reg[sda->index],
- tmp102_mC_to_reg(val));
+ status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index],
+ tmp102_mC_to_reg(val));
mutex_unlock(&tmp102->lock);
return status ? : count;
}
@@ -178,18 +166,19 @@ static int __devinit tmp102_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, tmp102);
- status = tmp102_read_reg(client, TMP102_CONF_REG);
+ status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
dev_err(&client->dev, "error reading config register\n");
goto fail_free;
}
tmp102->config_orig = status;
- status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG);
+ status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ TMP102_CONFIG);
if (status < 0) {
dev_err(&client->dev, "error writing config register\n");
goto fail_restore_config;
}
- status = tmp102_read_reg(client, TMP102_CONF_REG);
+ status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
dev_err(&client->dev, "error reading config register\n");
goto fail_restore_config;
@@ -222,7 +211,8 @@ static int __devinit tmp102_probe(struct i2c_client *client,
fail_remove_sysfs:
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
fail_restore_config:
- tmp102_write_reg(client, TMP102_CONF_REG, tmp102->config_orig);
+ i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ tmp102->config_orig);
fail_free:
kfree(tmp102);
@@ -240,10 +230,10 @@ static int __devexit tmp102_remove(struct i2c_client *client)
if (tmp102->config_orig & TMP102_CONF_SD) {
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config >= 0)
- tmp102_write_reg(client, TMP102_CONF_REG,
- config | TMP102_CONF_SD);
+ i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
+ config | TMP102_CONF_SD);
}
kfree(tmp102);
@@ -257,12 +247,12 @@ static int tmp102_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
config |= TMP102_CONF_SD;
- return tmp102_write_reg(client, TMP102_CONF_REG, config);
+ return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
}
static int tmp102_resume(struct device *dev)
@@ -270,12 +260,12 @@ static int tmp102_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
int config;
- config = tmp102_read_reg(client, TMP102_CONF_REG);
+ config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
config &= ~TMP102_CONF_SD;
- return tmp102_write_reg(client, TMP102_CONF_REG, config);
+ return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
}
static const struct dev_pm_ops tmp102_dev_pm_ops = {
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 98aab4bea342..483cb268781e 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -1,7 +1,7 @@
/*
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
- Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2005-2011 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2006 Yuan Mu (Winbond),
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
@@ -39,6 +39,7 @@
0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
+ w83627uhg 8 2 2 2 0xa230 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3
nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3
@@ -61,14 +62,17 @@
#include <linux/io.h>
#include "lm75.h"
-enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775,
- nct6776 };
+enum kinds {
+ w83627ehf, w83627dhg, w83627dhg_p, w83627uhg,
+ w83667hg, w83667hg_b, nct6775, nct6776,
+};
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * const w83627ehf_device_names[] = {
"w83627ehf",
"w83627dhg",
"w83627dhg",
+ "w83627uhg",
"w83667hg",
"w83667hg",
"nct6775",
@@ -104,6 +108,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070
+#define SIO_W83627UHG_ID 0xa230
#define SIO_W83667HG_ID 0xa510
#define SIO_W83667HG_B_ID 0xb350
#define SIO_NCT6775_ID 0xb470
@@ -388,18 +393,23 @@ div_from_reg(u8 reg)
return 1 << reg;
}
-/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
-
-static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
+/* Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100 */
+static const u16 scale_in_common[10] = {
+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800
+};
+static const u16 scale_in_w83627uhg[9] = {
+ 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400
+};
-static inline long in_from_reg(u8 reg, u8 nr)
+static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in)
{
- return reg * scale_in[nr];
+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
}
-static inline u8 in_to_reg(u32 val, u8 nr)
+static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in)
{
- return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0,
+ return SENSORS_LIMIT(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0,
255);
}
@@ -430,6 +440,7 @@ struct w83627ehf_data {
const u16 *REG_FAN_STOP_TIME;
const u16 *REG_FAN_MAX_OUTPUT;
const u16 *REG_FAN_STEP_OUTPUT;
+ const u16 *scale_in;
unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
@@ -481,7 +492,8 @@ struct w83627ehf_data {
u8 vrm;
u16 have_temp;
- u8 in6_skip;
+ u8 in6_skip:1;
+ u8 temp3_val_only:1;
};
struct w83627ehf_sio_data {
@@ -907,7 +919,8 @@ show_##reg(struct device *dev, struct device_attribute *attr, \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
int nr = sensor_attr->index; \
- return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
+ return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \
+ data->scale_in)); \
}
show_in_reg(in)
show_in_reg(in_min)
@@ -928,7 +941,7 @@ store_in_##reg(struct device *dev, struct device_attribute *attr, \
if (err < 0) \
return err; \
mutex_lock(&data->update_lock); \
- data->in_##reg[nr] = in_to_reg(val, nr); \
+ data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \
w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \
data->in_##reg[nr]); \
mutex_unlock(&data->update_lock); \
@@ -1617,25 +1630,28 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
store_fan_step_output, 3),
};
+static struct sensor_device_attribute sda_sf3_arrays_fan3[] = {
+ SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 2),
+ SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 2),
+ SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+ store_fan_stop_output, 2),
+};
+
static struct sensor_device_attribute sda_sf3_arrays[] = {
SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 0),
SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 1),
- SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
- store_fan_stop_time, 2),
SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
store_fan_start_output, 0),
SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
store_fan_start_output, 1),
- SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
- store_fan_start_output, 2),
SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 0),
SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 1),
- SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
- store_fan_stop_output, 2),
};
@@ -1728,6 +1744,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
device_remove_file(dev, &attr->dev_attr);
}
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++)
+ device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
for (i = 0; i < data->in_num; i++) {
@@ -1756,6 +1774,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
continue;
device_remove_file(dev, &sda_temp_input[i].dev_attr);
device_remove_file(dev, &sda_temp_label[i].dev_attr);
+ if (i == 2 && data->temp3_val_only)
+ continue;
device_remove_file(dev, &sda_temp_max[i].dev_attr);
device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
if (i > 2)
@@ -1808,11 +1828,21 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data,
case w83627ehf:
diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
break;
+ case w83627uhg:
+ diode = 0x00;
+ break;
default:
diode = 0x70;
}
for (i = 0; i < 3; i++) {
- if ((tmp & (0x02 << i)))
+ const char *label = data->temp_label[data->temp_src[i]];
+
+ /* Digital source overrides analog type */
+ if (strncmp(label, "PECI", 4) == 0)
+ data->temp_type[i] = 6;
+ else if (strncmp(label, "AMD", 3) == 0)
+ data->temp_type[i] = 5;
+ else if ((tmp & (0x02 << i)))
data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3;
else
data->temp_type[i] = 4; /* thermistor */
@@ -1846,11 +1876,31 @@ static void w82627ehf_swap_tempreg(struct w83627ehf_data *data,
}
static void __devinit
+w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp)
+{
+ int i;
+
+ for (i = 0; i < n_temp; i++) {
+ data->reg_temp[i] = W83627EHF_REG_TEMP[i];
+ data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
+ data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
+ data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+ }
+}
+
+static void __devinit
w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data,
struct w83627ehf_data *data)
{
int fan3pin, fan4pin, fan4min, fan5pin, regval;
+ /* The W83627UHG is simple, only two fan inputs, no config */
+ if (sio_data->kind == w83627uhg) {
+ data->has_fan = 0x03; /* fan1 and fan2 */
+ data->has_fan_min = 0x03;
+ return;
+ }
+
superio_enter(sio_data->sioreg);
/* fan4 and fan5 share some pins with the GPIO and serial flash */
@@ -1942,23 +1992,24 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
- /* 667HG, NCT6775F, and NCT6776F have 3 pwms */
- data->pwm_num = (sio_data->kind == w83667hg
- || sio_data->kind == w83667hg_b
- || sio_data->kind == nct6775
- || sio_data->kind == nct6776) ? 3 : 4;
+ /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */
+ switch (sio_data->kind) {
+ default:
+ data->pwm_num = 4;
+ break;
+ case w83667hg:
+ case w83667hg_b:
+ case nct6775:
+ case nct6776:
+ data->pwm_num = 3;
+ break;
+ case w83627uhg:
+ data->pwm_num = 2;
+ break;
+ }
+ /* Default to 3 temperature inputs, code below will adjust as needed */
data->have_temp = 0x07;
- /* Check temp3 configuration bit for 667HG */
- if (sio_data->kind == w83667hg) {
- u8 reg;
-
- reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
- if (reg & 0x01)
- data->have_temp &= ~(1 << 2);
- else
- data->in6_skip = 1; /* either temp3 or in6 */
- }
/* Deal with temperature register setup first. */
if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
@@ -2035,16 +2086,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
} else if (sio_data->kind == w83667hg_b) {
u8 reg;
+ w83627ehf_set_temp_reg_ehf(data, 4);
+
/*
* Temperature sources are selected with bank 0, registers 0x49
* and 0x4a.
*/
- for (i = 0; i < ARRAY_SIZE(W83627EHF_REG_TEMP); i++) {
- data->reg_temp[i] = W83627EHF_REG_TEMP[i];
- data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
- data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
- data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
- }
reg = w83627ehf_read_value(data, 0x4a);
data->temp_src[0] = reg >> 5;
reg = w83627ehf_read_value(data, 0x49);
@@ -2078,13 +2125,60 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->in6_skip = 1;
data->temp_label = w83667hg_b_temp_label;
+ } else if (sio_data->kind == w83627uhg) {
+ u8 reg;
+
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
+ /*
+ * Temperature sources for temp1 and temp2 are selected with
+ * bank 0, registers 0x49 and 0x4a.
+ */
+ data->temp_src[0] = 0; /* SYSTIN */
+ reg = w83627ehf_read_value(data, 0x49) & 0x07;
+ /* Adjust to have the same mapping as other source registers */
+ if (reg == 0)
+ data->temp_src[1]++;
+ else if (reg >= 2 && reg <= 5)
+ data->temp_src[1] += 2;
+ else /* should never happen */
+ data->have_temp &= ~(1 << 1);
+ reg = w83627ehf_read_value(data, 0x4a);
+ data->temp_src[2] = reg >> 5;
+
+ /*
+ * Skip temp3 if source is invalid or the same as temp1
+ * or temp2.
+ */
+ if (data->temp_src[2] == 2 || data->temp_src[2] == 3 ||
+ data->temp_src[2] == data->temp_src[0] ||
+ ((data->have_temp & (1 << 1)) &&
+ data->temp_src[2] == data->temp_src[1]))
+ data->have_temp &= ~(1 << 2);
+ else
+ data->temp3_val_only = 1; /* No limit regs */
+
+ data->in6_skip = 1; /* No VIN3 */
+
+ data->temp_label = w83667hg_b_temp_label;
} else {
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
/* Temperature sources are fixed */
- for (i = 0; i < 3; i++) {
- data->reg_temp[i] = W83627EHF_REG_TEMP[i];
- data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
- data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
- data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+
+ if (sio_data->kind == w83667hg) {
+ u8 reg;
+
+ /*
+ * Chip supports either AUXTIN or VIN3. Try to find
+ * out which one.
+ */
+ reg = w83627ehf_read_value(data,
+ W83627EHF_REG_TEMP_CONFIG[2]);
+ if (reg & 0x01)
+ data->have_temp &= ~(1 << 2);
+ else
+ data->in6_skip = 1;
}
}
@@ -2144,6 +2238,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
}
+ /* Setup input voltage scaling factors */
+ if (sio_data->kind == w83627uhg)
+ data->scale_in = scale_in_w83627uhg;
+ else
+ data->scale_in = scale_in_common;
+
/* Initialize the chip */
w83627ehf_init_device(data, sio_data->kind);
@@ -2160,7 +2260,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
err = device_create_file(dev, &dev_attr_cpu0_vid);
if (err)
goto exit_release;
- } else {
+ } else if (sio_data->kind != w83627uhg) {
superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) {
/* Set VID input sensibility if needed. In theory the
@@ -2250,7 +2350,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
goto exit_remove;
}
}
- /* if fan4 is enabled create the sf3 files for it */
+ /* if fan3 and fan4 are enabled create the sf3 files for them */
+ if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3)
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) {
+ err = device_create_file(dev,
+ &sda_sf3_arrays_fan3[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
err = device_create_file(dev,
@@ -2318,6 +2425,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
if (err)
goto exit_remove;
}
+ if (i == 2 && data->temp3_val_only)
+ continue;
if (data->reg_temp_over[i]) {
err = device_create_file(dev,
&sda_temp_max[i].dev_attr);
@@ -2401,6 +2510,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
+ static const char __initdata sio_name_W83627UHG[] = "W83627UHG";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
static const char __initdata sio_name_NCT6775[] = "NCT6775F";
@@ -2433,6 +2543,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83627dhg_p;
sio_name = sio_name_W83627DHG_P;
break;
+ case SIO_W83627UHG_ID:
+ sio_data->kind = w83627uhg;
+ sio_name = sio_name_W83627UHG;
+ break;
case SIO_W83667HG_ID:
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index eed43a008be1..65b685e2c7b7 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1245,17 +1245,17 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg)
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x50: /* TEMP */
- res = swab16(i2c_smbus_read_word_data(cl, 0));
+ res = i2c_smbus_read_word_swapped(cl, 0);
break;
case 0x52: /* CONFIG */
res = i2c_smbus_read_byte_data(cl, 1);
break;
case 0x53: /* HYST */
- res = swab16(i2c_smbus_read_word_data(cl, 2));
+ res = i2c_smbus_read_word_swapped(cl, 2);
break;
case 0x55: /* OVER */
default:
- res = swab16(i2c_smbus_read_word_data(cl, 3));
+ res = i2c_smbus_read_word_swapped(cl, 3);
break;
}
}
@@ -1289,10 +1289,10 @@ w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value)
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
- i2c_smbus_write_word_data(cl, 2, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 2, value);
break;
case 0x55: /* OVER */
- i2c_smbus_write_word_data(cl, 3, swab16(value));
+ i2c_smbus_write_word_swapped(cl, 3, value);
break;
}
}
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 1f29bab6b3e5..c7c3128393d1 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -2,22 +2,31 @@
# Generic HWSPINLOCK framework
#
+# HWSPINLOCK always gets selected by whoever wants it.
config HWSPINLOCK
- tristate "Generic Hardware Spinlock framework"
- depends on ARCH_OMAP4
- help
- Say y here to support the generic hardware spinlock framework.
- You only need to enable this if you have hardware spinlock module
- on your system (usually only relevant if your system has remote slave
- coprocessors).
+ tristate
- If unsure, say N.
+menu "Hardware Spinlock drivers"
config HWSPINLOCK_OMAP
tristate "OMAP Hardware Spinlock device"
- depends on HWSPINLOCK && ARCH_OMAP4
+ depends on ARCH_OMAP4
+ select HWSPINLOCK
help
Say y here to support the OMAP Hardware Spinlock device (firstly
introduced in OMAP4).
If unsure, say N.
+
+config HSEM_U8500
+ tristate "STE Hardware Semaphore functionality"
+ depends on ARCH_U8500
+ select HWSPINLOCK
+ help
+ Say y here to support the STE Hardware Semaphore functionality, which
+ provides a synchronisation mechanism for the various processor on the
+ SoC.
+
+ If unsure, say N.
+
+endmenu
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index 5729a3f7ed3d..93eb64b66486 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
+obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 43a62714b4fb..61c9cf15fa52 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -26,6 +26,7 @@
#include <linux/radix-tree.h>
#include <linux/hwspinlock.h>
#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
#include "hwspinlock_internal.h"
@@ -52,10 +53,12 @@
static RADIX_TREE(hwspinlock_tree, GFP_KERNEL);
/*
- * Synchronization of access to the tree is achieved using this spinlock,
+ * Synchronization of access to the tree is achieved using this mutex,
* as the radix-tree API requires that users provide all synchronisation.
+ * A mutex is needed because we're using non-atomic radix tree allocations.
*/
-static DEFINE_SPINLOCK(hwspinlock_tree_lock);
+static DEFINE_MUTEX(hwspinlock_tree_lock);
+
/**
* __hwspin_trylock() - attempt to lock a specific hwspinlock
@@ -114,7 +117,7 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
return -EBUSY;
/* try to take the hwspinlock device */
- ret = hwlock->ops->trylock(hwlock);
+ ret = hwlock->bank->ops->trylock(hwlock);
/* if hwlock is already taken, undo spin_trylock_* and exit */
if (!ret) {
@@ -196,8 +199,8 @@ int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to,
* Allow platform-specific relax handlers to prevent
* hogging the interconnect (no sleeping, though)
*/
- if (hwlock->ops->relax)
- hwlock->ops->relax(hwlock);
+ if (hwlock->bank->ops->relax)
+ hwlock->bank->ops->relax(hwlock);
}
return ret;
@@ -242,7 +245,7 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
*/
mb();
- hwlock->ops->unlock(hwlock);
+ hwlock->bank->ops->unlock(hwlock);
/* Undo the spin_trylock{_irq, _irqsave} called while locking */
if (mode == HWLOCK_IRQSTATE)
@@ -254,68 +257,37 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
}
EXPORT_SYMBOL_GPL(__hwspin_unlock);
-/**
- * hwspin_lock_register() - register a new hw spinlock
- * @hwlock: hwspinlock to register.
- *
- * This function should be called from the underlying platform-specific
- * implementation, to register a new hwspinlock instance.
- *
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context.
- *
- * Returns 0 on success, or an appropriate error code on failure
- */
-int hwspin_lock_register(struct hwspinlock *hwlock)
+static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
{
struct hwspinlock *tmp;
int ret;
- if (!hwlock || !hwlock->ops ||
- !hwlock->ops->trylock || !hwlock->ops->unlock) {
- pr_err("invalid parameters\n");
- return -EINVAL;
- }
-
- spin_lock_init(&hwlock->lock);
-
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
- ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock);
- if (ret)
+ ret = radix_tree_insert(&hwspinlock_tree, id, hwlock);
+ if (ret) {
+ if (ret == -EEXIST)
+ pr_err("hwspinlock id %d already exists!\n", id);
goto out;
+ }
/* mark this hwspinlock as available */
- tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
- HWSPINLOCK_UNUSED);
+ tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
/* self-sanity check which should never fail */
WARN_ON(tmp != hwlock);
out:
- spin_unlock(&hwspinlock_tree_lock);
- return ret;
+ mutex_unlock(&hwspinlock_tree_lock);
+ return 0;
}
-EXPORT_SYMBOL_GPL(hwspin_lock_register);
-/**
- * hwspin_lock_unregister() - unregister an hw spinlock
- * @id: index of the specific hwspinlock to unregister
- *
- * This function should be called from the underlying platform-specific
- * implementation, to unregister an existing (and unused) hwspinlock.
- *
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context.
- *
- * Returns the address of hwspinlock @id on success, or NULL on failure
- */
-struct hwspinlock *hwspin_lock_unregister(unsigned int id)
+static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id)
{
struct hwspinlock *hwlock = NULL;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is not in use (tag is set) */
ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
@@ -331,9 +303,91 @@ struct hwspinlock *hwspin_lock_unregister(unsigned int id)
}
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
+
+/**
+ * hwspin_lock_register() - register a new hw spinlock device
+ * @bank: the hwspinlock device, which usually provides numerous hw locks
+ * @dev: the backing device
+ * @ops: hwspinlock handlers for this device
+ * @base_id: id of the first hardware spinlock in this bank
+ * @num_locks: number of hwspinlocks provided by this device
+ *
+ * This function should be called from the underlying platform-specific
+ * implementation, to register a new hwspinlock device instance.
+ *
+ * Should be called from a process context (might sleep)
+ *
+ * Returns 0 on success, or an appropriate error code on failure
+ */
+int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
+ const struct hwspinlock_ops *ops, int base_id, int num_locks)
+{
+ struct hwspinlock *hwlock;
+ int ret = 0, i;
+
+ if (!bank || !ops || !dev || !num_locks || !ops->trylock ||
+ !ops->unlock) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ bank->dev = dev;
+ bank->ops = ops;
+ bank->base_id = base_id;
+ bank->num_locks = num_locks;
+
+ for (i = 0; i < num_locks; i++) {
+ hwlock = &bank->lock[i];
+
+ spin_lock_init(&hwlock->lock);
+ hwlock->bank = bank;
+
+ ret = hwspin_lock_register_single(hwlock, i);
+ if (ret)
+ goto reg_failed;
+ }
+
+ return 0;
+
+reg_failed:
+ while (--i >= 0)
+ hwspin_lock_unregister_single(i);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hwspin_lock_register);
+
+/**
+ * hwspin_lock_unregister() - unregister an hw spinlock device
+ * @bank: the hwspinlock device, which usually provides numerous hw locks
+ *
+ * This function should be called from the underlying platform-specific
+ * implementation, to unregister an existing (and unused) hwspinlock.
+ *
+ * Should be called from a process context (might sleep)
+ *
+ * Returns 0 on success, or an appropriate error code on failure
+ */
+int hwspin_lock_unregister(struct hwspinlock_device *bank)
+{
+ struct hwspinlock *hwlock, *tmp;
+ int i;
+
+ for (i = 0; i < bank->num_locks; i++) {
+ hwlock = &bank->lock[i];
+
+ tmp = hwspin_lock_unregister_single(bank->base_id + i);
+ if (!tmp)
+ return -EBUSY;
+
+ /* self-sanity check that should never fail */
+ WARN_ON(tmp != hwlock);
+ }
+
+ return 0;
+}
EXPORT_SYMBOL_GPL(hwspin_lock_unregister);
/**
@@ -348,24 +402,25 @@ EXPORT_SYMBOL_GPL(hwspin_lock_unregister);
*/
static int __hwspin_lock_request(struct hwspinlock *hwlock)
{
+ struct device *dev = hwlock->bank->dev;
struct hwspinlock *tmp;
int ret;
/* prevent underlying implementation from being removed */
- if (!try_module_get(hwlock->owner)) {
- dev_err(hwlock->dev, "%s: can't get owner\n", __func__);
+ if (!try_module_get(dev->driver->owner)) {
+ dev_err(dev, "%s: can't get owner\n", __func__);
return -EINVAL;
}
/* notify PM core that power is now needed */
- ret = pm_runtime_get_sync(hwlock->dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- dev_err(hwlock->dev, "%s: can't power on device\n", __func__);
+ dev_err(dev, "%s: can't power on device\n", __func__);
return ret;
}
/* mark hwspinlock as used, should not fail */
- tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock->id,
+ tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
/* self-sanity check that should never fail */
@@ -387,7 +442,7 @@ int hwspin_lock_get_id(struct hwspinlock *hwlock)
return -EINVAL;
}
- return hwlock->id;
+ return hwlock_to_id(hwlock);
}
EXPORT_SYMBOL_GPL(hwspin_lock_get_id);
@@ -400,9 +455,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_get_id);
* to the remote core before it can be used for synchronization (to get the
* id of a given hwlock, use hwspin_lock_get_id()).
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns the address of the assigned hwspinlock, or NULL on error
*/
@@ -411,7 +464,7 @@ struct hwspinlock *hwspin_lock_request(void)
struct hwspinlock *hwlock;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* look for an unused lock */
ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock,
@@ -431,7 +484,7 @@ struct hwspinlock *hwspin_lock_request(void)
hwlock = NULL;
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
EXPORT_SYMBOL_GPL(hwspin_lock_request);
@@ -445,9 +498,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request);
* Usually early board code will be calling this function in order to
* reserve specific hwspinlock ids for predefined purposes.
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns the address of the assigned hwspinlock, or NULL on error
*/
@@ -456,7 +507,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
struct hwspinlock *hwlock;
int ret;
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure this hwspinlock exists */
hwlock = radix_tree_lookup(&hwspinlock_tree, id);
@@ -466,7 +517,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
}
/* sanity check (this shouldn't happen) */
- WARN_ON(hwlock->id != id);
+ WARN_ON(hwlock_to_id(hwlock) != id);
/* make sure this hwspinlock is unused */
ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
@@ -482,7 +533,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
hwlock = NULL;
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return hwlock;
}
EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
@@ -495,14 +546,13 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
* Should only be called with an @hwlock that was retrieved from
* an earlier call to omap_hwspin_lock_request{_specific}.
*
- * Can be called from an atomic context (will not sleep) but not from
- * within interrupt context (simply because there is no use case for
- * that yet).
+ * Should be called from a process context (might sleep)
*
* Returns 0 on success, or an appropriate error code on failure
*/
int hwspin_lock_free(struct hwspinlock *hwlock)
{
+ struct device *dev = hwlock->bank->dev;
struct hwspinlock *tmp;
int ret;
@@ -511,34 +561,34 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
return -EINVAL;
}
- spin_lock(&hwspinlock_tree_lock);
+ mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is used */
- ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id,
+ ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
if (ret == 1) {
- dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__);
+ dev_err(dev, "%s: hwlock is already free\n", __func__);
dump_stack();
ret = -EINVAL;
goto out;
}
/* notify the underlying device that power is not needed */
- ret = pm_runtime_put(hwlock->dev);
+ ret = pm_runtime_put(dev);
if (ret < 0)
goto out;
/* mark this hwspinlock as available */
- tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
+ tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock),
HWSPINLOCK_UNUSED);
/* sanity check (this shouldn't happen) */
WARN_ON(tmp != hwlock);
- module_put(hwlock->owner);
+ module_put(dev->driver->owner);
out:
- spin_unlock(&hwspinlock_tree_lock);
+ mutex_unlock(&hwspinlock_tree_lock);
return ret;
}
EXPORT_SYMBOL_GPL(hwspin_lock_free);
diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h
index 69935e6b93e5..d26f78b8f214 100644
--- a/drivers/hwspinlock/hwspinlock_internal.h
+++ b/drivers/hwspinlock/hwspinlock_internal.h
@@ -21,6 +21,8 @@
#include <linux/spinlock.h>
#include <linux/device.h>
+struct hwspinlock_device;
+
/**
* struct hwspinlock_ops - platform-specific hwspinlock handlers
*
@@ -39,23 +41,37 @@ struct hwspinlock_ops {
/**
* struct hwspinlock - this struct represents a single hwspinlock instance
- *
- * @dev: underlying device, will be used to invoke runtime PM api
- * @ops: platform-specific hwspinlock handlers
- * @id: a global, unique, system-wide, index of the lock.
+ * @bank: the hwspinlock_device structure which owns this lock
* @lock: initialized and used by hwspinlock core
- * @owner: underlying implementation module, used to maintain module ref count
- *
- * Note: currently simplicity was opted for, but later we can squeeze some
- * memory bytes by grouping the dev, ops and owner members in a single
- * per-platform struct, and have all hwspinlocks point at it.
+ * @priv: private data, owned by the underlying platform-specific hwspinlock drv
*/
struct hwspinlock {
+ struct hwspinlock_device *bank;
+ spinlock_t lock;
+ void *priv;
+};
+
+/**
+ * struct hwspinlock_device - a device which usually spans numerous hwspinlocks
+ * @dev: underlying device, will be used to invoke runtime PM api
+ * @ops: platform-specific hwspinlock handlers
+ * @base_id: id index of the first lock in this device
+ * @num_locks: number of locks in this device
+ * @lock: dynamically allocated array of 'struct hwspinlock'
+ */
+struct hwspinlock_device {
struct device *dev;
const struct hwspinlock_ops *ops;
- int id;
- spinlock_t lock;
- struct module *owner;
+ int base_id;
+ int num_locks;
+ struct hwspinlock lock[0];
};
+static inline int hwlock_to_id(struct hwspinlock *hwlock)
+{
+ int local_id = hwlock - &hwlock->bank->lock[0];
+
+ return hwlock->bank->base_id + local_id;
+}
+
#endif /* __HWSPINLOCK_HWSPINLOCK_H */
diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c
index a8f02734c026..887d34effb3a 100644
--- a/drivers/hwspinlock/omap_hwspinlock.c
+++ b/drivers/hwspinlock/omap_hwspinlock.c
@@ -41,33 +41,20 @@
#define SPINLOCK_NOTTAKEN (0) /* free */
#define SPINLOCK_TAKEN (1) /* locked */
-#define to_omap_hwspinlock(lock) \
- container_of(lock, struct omap_hwspinlock, lock)
-
-struct omap_hwspinlock {
- struct hwspinlock lock;
- void __iomem *addr;
-};
-
-struct omap_hwspinlock_state {
- int num_locks; /* Total number of locks in system */
- void __iomem *io_base; /* Mapped base address */
-};
-
static int omap_hwspinlock_trylock(struct hwspinlock *lock)
{
- struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock);
+ void __iomem *lock_addr = lock->priv;
/* attempt to acquire the lock by reading its value */
- return (SPINLOCK_NOTTAKEN == readl(omap_lock->addr));
+ return (SPINLOCK_NOTTAKEN == readl(lock_addr));
}
static void omap_hwspinlock_unlock(struct hwspinlock *lock)
{
- struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock);
+ void __iomem *lock_addr = lock->priv;
/* release the lock by writing 0 to it */
- writel(SPINLOCK_NOTTAKEN, omap_lock->addr);
+ writel(SPINLOCK_NOTTAKEN, lock_addr);
}
/*
@@ -93,26 +80,23 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = {
static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
{
- struct omap_hwspinlock *omap_lock;
- struct omap_hwspinlock_state *state;
- struct hwspinlock *lock;
+ struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
+ struct hwspinlock_device *bank;
+ struct hwspinlock *hwlock;
struct resource *res;
void __iomem *io_base;
- int i, ret;
+ int num_locks, i, ret;
+
+ if (!pdata)
+ return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
io_base = ioremap(res->start, resource_size(res));
- if (!io_base) {
- ret = -ENOMEM;
- goto free_state;
- }
+ if (!io_base)
+ return -ENOMEM;
/* Determine number of locks */
i = readl(io_base + SYSSTATUS_OFFSET);
@@ -124,10 +108,18 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
goto iounmap_base;
}
- state->num_locks = i * 32;
- state->io_base = io_base;
+ num_locks = i * 32; /* actual number of locks in this device */
+
+ bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
+ if (!bank) {
+ ret = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ platform_set_drvdata(pdev, bank);
- platform_set_drvdata(pdev, state);
+ for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
+ hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
/*
* runtime PM will make sure the clock of this module is
@@ -135,79 +127,46 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev)
*/
pm_runtime_enable(&pdev->dev);
- for (i = 0; i < state->num_locks; i++) {
- omap_lock = kzalloc(sizeof(*omap_lock), GFP_KERNEL);
- if (!omap_lock) {
- ret = -ENOMEM;
- goto free_locks;
- }
-
- omap_lock->lock.dev = &pdev->dev;
- omap_lock->lock.owner = THIS_MODULE;
- omap_lock->lock.id = i;
- omap_lock->lock.ops = &omap_hwspinlock_ops;
- omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
-
- ret = hwspin_lock_register(&omap_lock->lock);
- if (ret) {
- kfree(omap_lock);
- goto free_locks;
- }
- }
+ ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
+ pdata->base_id, num_locks);
+ if (ret)
+ goto reg_fail;
return 0;
-free_locks:
- while (--i >= 0) {
- lock = hwspin_lock_unregister(i);
- /* this should't happen, but let's give our best effort */
- if (!lock) {
- dev_err(&pdev->dev, "%s: cleanups failed\n", __func__);
- continue;
- }
- omap_lock = to_omap_hwspinlock(lock);
- kfree(omap_lock);
- }
+reg_fail:
pm_runtime_disable(&pdev->dev);
+ kfree(bank);
iounmap_base:
iounmap(io_base);
-free_state:
- kfree(state);
return ret;
}
-static int omap_hwspinlock_remove(struct platform_device *pdev)
+static int __devexit omap_hwspinlock_remove(struct platform_device *pdev)
{
- struct omap_hwspinlock_state *state = platform_get_drvdata(pdev);
- struct hwspinlock *lock;
- struct omap_hwspinlock *omap_lock;
- int i;
-
- for (i = 0; i < state->num_locks; i++) {
- lock = hwspin_lock_unregister(i);
- /* this shouldn't happen at this point. if it does, at least
- * don't continue with the remove */
- if (!lock) {
- dev_err(&pdev->dev, "%s: failed on %d\n", __func__, i);
- return -EBUSY;
- }
-
- omap_lock = to_omap_hwspinlock(lock);
- kfree(omap_lock);
+ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
+ void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET;
+ int ret;
+
+ ret = hwspin_lock_unregister(bank);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+ return ret;
}
pm_runtime_disable(&pdev->dev);
- iounmap(state->io_base);
- kfree(state);
+ iounmap(io_base);
+ kfree(bank);
return 0;
}
static struct platform_driver omap_hwspinlock_driver = {
.probe = omap_hwspinlock_probe,
- .remove = omap_hwspinlock_remove,
+ .remove = __devexit_p(omap_hwspinlock_remove),
.driver = {
.name = "omap_hwspinlock",
+ .owner = THIS_MODULE,
},
};
diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c
new file mode 100644
index 000000000000..143461a95ae4
--- /dev/null
+++ b/drivers/hwspinlock/u8500_hsem.c
@@ -0,0 +1,198 @@
+/*
+ * u8500 HWSEM driver
+ *
+ * Copyright (C) 2010-2011 ST-Ericsson
+ *
+ * Implements u8500 semaphore handling for protocol 1, no interrupts.
+ *
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ * Heavily borrowed from the work of :
+ * Simon Que <sque@ti.com>
+ * Hari Kanigeri <h-kanigeri2@ti.com>
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/hwspinlock.h>
+#include <linux/platform_device.h>
+
+#include "hwspinlock_internal.h"
+
+/*
+ * Implementation of STE's HSem protocol 1 without interrutps.
+ * The only masterID we allow is '0x01' to force people to use
+ * HSems for synchronisation between processors rather than processes
+ * on the ARM core.
+ */
+
+#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */
+#define RESET_SEMAPHORE (0) /* free */
+
+/*
+ * CPU ID for master running u8500 kernel.
+ * Hswpinlocks should only be used to synchonise operations
+ * between the Cortex A9 core and the other CPUs. Hence
+ * forcing the masterID to a preset value.
+ */
+#define HSEM_MASTER_ID 0x01
+
+#define HSEM_REGISTER_OFFSET 0x08
+
+#define HSEM_CTRL_REG 0x00
+#define HSEM_ICRALL 0x90
+#define HSEM_PROTOCOL_1 0x01
+
+static int u8500_hsem_trylock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+
+ writel(HSEM_MASTER_ID, lock_addr);
+
+ /* get only first 4 bit and compare to masterID.
+ * if equal, we have the semaphore, otherwise
+ * someone else has it.
+ */
+ return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
+}
+
+static void u8500_hsem_unlock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+
+ /* release the lock by writing 0 to it */
+ writel(RESET_SEMAPHORE, lock_addr);
+}
+
+/*
+ * u8500: what value is recommended here ?
+ */
+static void u8500_hsem_relax(struct hwspinlock *lock)
+{
+ ndelay(50);
+}
+
+static const struct hwspinlock_ops u8500_hwspinlock_ops = {
+ .trylock = u8500_hsem_trylock,
+ .unlock = u8500_hsem_unlock,
+ .relax = u8500_hsem_relax,
+};
+
+static int __devinit u8500_hsem_probe(struct platform_device *pdev)
+{
+ struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
+ struct hwspinlock_device *bank;
+ struct hwspinlock *hwlock;
+ struct resource *res;
+ void __iomem *io_base;
+ int i, ret, num_locks = U8500_MAX_SEMAPHORE;
+ ulong val;
+
+ if (!pdata)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ io_base = ioremap(res->start, resource_size(res));
+ if (!io_base) {
+ ret = -ENOMEM;
+ goto free_state;
+ }
+
+ /* make sure protocol 1 is selected */
+ val = readl(io_base + HSEM_CTRL_REG);
+ writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
+
+ /* clear all interrupts */
+ writel(0xFFFF, io_base + HSEM_ICRALL);
+
+ bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
+ if (!bank) {
+ ret = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ platform_set_drvdata(pdev, bank);
+
+ for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
+ hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
+
+ /* no pm needed for HSem but required to comply with hwspilock core */
+ pm_runtime_enable(&pdev->dev);
+
+ ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops,
+ pdata->base_id, num_locks);
+ if (ret)
+ goto reg_fail;
+
+ return 0;
+
+reg_fail:
+ pm_runtime_disable(&pdev->dev);
+ kfree(bank);
+iounmap_base:
+ iounmap(io_base);
+ return ret;
+}
+
+static int __devexit u8500_hsem_remove(struct platform_device *pdev)
+{
+ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
+ void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
+ int ret;
+
+ /* clear all interrupts */
+ writel(0xFFFF, io_base + HSEM_ICRALL);
+
+ ret = hwspin_lock_unregister(bank);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ pm_runtime_disable(&pdev->dev);
+ iounmap(io_base);
+ kfree(bank);
+
+ return 0;
+}
+
+static struct platform_driver u8500_hsem_driver = {
+ .probe = u8500_hsem_probe,
+ .remove = __devexit_p(u8500_hsem_remove),
+ .driver = {
+ .name = "u8500_hsem",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init u8500_hsem_init(void)
+{
+ return platform_driver_register(&u8500_hsem_driver);
+}
+/* board init code might need to reserve hwspinlocks for predefined purposes */
+postcore_initcall(u8500_hsem_init);
+
+static void __exit u8500_hsem_exit(void)
+{
+ platform_driver_unregister(&u8500_hsem_driver);
+}
+module_exit(u8500_hsem_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
+MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index d6d58684712b..85584a547c25 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -47,8 +47,8 @@
/* ----- global variables --------------------------------------------- */
static int bit_test; /* see if the line-setting functions work */
-module_param(bit_test, bool, 0);
-MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck");
+module_param(bit_test, int, S_IRUGO);
+MODULE_PARM_DESC(bit_test, "lines testing - 0 off; 1 report; 2 fail if stuck");
#ifdef DEBUG
static int i2c_debug = 1;
@@ -250,7 +250,9 @@ static int test_bus(struct i2c_adapter *i2c_adap)
sda = getsda(adap);
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
if (!scl || !sda) {
- printk(KERN_WARNING "%s: bus seems to be busy\n", name);
+ printk(KERN_WARNING
+ "%s: bus seems to be busy (scl=%d, sda=%d)\n",
+ name, scl, sda);
goto bailout;
}
@@ -441,7 +443,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
acknak(i2c_adap, 0);
dev_err(&i2c_adap->dev, "readbytes: invalid "
"block length (%d)\n", inval);
- return -EREMOTEIO;
+ return -EPROTO;
}
/* The original count value accounts for the extra
bytes, that is, either 1 for a regular transaction,
@@ -470,7 +472,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
* reads, writes as well as 10bit-addresses.
* returns:
* 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
- * -x an error occurred (like: -EREMOTEIO if the device did not answer, or
+ * -x an error occurred (like: -ENXIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
@@ -493,14 +495,14 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at extended address code\n");
- return -EREMOTEIO;
+ return -ENXIO;
}
/* the remaining 8 bit address */
ret = i2c_outb(i2c_adap, msg->addr & 0x7f);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
dev_err(&i2c_adap->dev, "died at 2nd address code\n");
- return -EREMOTEIO;
+ return -ENXIO;
}
if (flags & I2C_M_RD) {
bit_dbg(3, &i2c_adap->dev, "emitting repeated "
@@ -512,7 +514,7 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at repeated address code\n");
- return -EREMOTEIO;
+ return -EIO;
}
}
} else { /* normal 7bit address */
@@ -570,7 +572,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
- ret = -EREMOTEIO;
+ ret = -EIO;
goto bailout;
}
} else {
@@ -581,7 +583,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
- ret = -EREMOTEIO;
+ ret = -EIO;
goto bailout;
}
}
@@ -624,7 +626,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
if (bit_test) {
ret = test_bus(adap);
- if (ret < 0)
+ if (bit_test >= 2 && ret < 0)
return -ENODEV;
}
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 4ca9cf9cde73..beb9ffe2564b 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -196,7 +196,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
} else {
dev_dbg(&i2c_adap->dev, "bus is not idle. status is "
"%#04x\n", state);
- return -EAGAIN;
+ return -EBUSY;
}
}
@@ -224,7 +224,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
}
curmsg = 0;
- ret = -EREMOTEIO;
+ ret = -EIO;
while (curmsg < num) {
state = pca_status(adap);
@@ -259,6 +259,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+W\n");
pca_stop(adap);
+ ret = -ENXIO;
goto out;
case 0x40: /* SLA+R has been transmitted; ACK has been received */
@@ -283,6 +284,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+R\n");
pca_stop(adap);
+ ret = -ENXIO;
goto out;
case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 646068e5100b..a3afac4be734 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -110,7 +110,6 @@ config I2C_I801
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
depends on PCI
- select MFD_CORE
select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
@@ -301,7 +300,7 @@ config I2C_AT91
config I2C_AU1550
tristate "Au1550/Au1200 SMBus interface"
- depends on SOC_AU1550 || SOC_AU1200
+ depends on MIPS_ALCHEMY
help
If you say yes to this option, support will be included for the
Au1550 and Au1200 SMBus interface.
@@ -350,15 +349,25 @@ config I2C_DAVINCI
devices such as DaVinci NIC.
For details please see http://www.ti.com/davinci
-config I2C_DESIGNWARE
- tristate "Synopsys DesignWare"
+config I2C_DESIGNWARE_PLATFORM
+ tristate "Synopsys DesignWare Platfrom"
depends on HAVE_CLK
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.
This driver can also be built as a module. If so, the module
- will be called i2c-designware.
+ will be called i2c-designware-platform.
+
+config I2C_DESIGNWARE_PCI
+ tristate "Synopsys DesignWare PCI"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ Synopsys DesignWare I2C adapter. Only master mode is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-designware-pci.
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
@@ -789,7 +798,7 @@ config I2C_ACORN
config I2C_ELEKTOR
tristate "Elektor ISA card"
- depends on ISA && BROKEN_ON_SMP
+ depends on ISA && HAS_IOPORT && BROKEN_ON_SMP
select I2C_ALGOPCF
help
This supports the PCF8584 ISA bus I2C adapter. Say Y if you own
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index e6cf294d3729..fba6da60aa0e 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -33,7 +33,10 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
-obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o
+obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
+i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
+obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
+i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index 532828bc50e6..f314d7f433d3 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -36,32 +36,44 @@
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
+#define PSC_SEL 0x00
+#define PSC_CTRL 0x04
+#define PSC_SMBCFG 0x08
+#define PSC_SMBMSK 0x0C
+#define PSC_SMBPCR 0x10
+#define PSC_SMBSTAT 0x14
+#define PSC_SMBEVNT 0x18
+#define PSC_SMBTXRX 0x1C
+#define PSC_SMBTMR 0x20
+
struct i2c_au1550_data {
- u32 psc_base;
+ void __iomem *psc_base;
int xfer_timeout;
- int ack_timeout;
struct i2c_adapter adap;
struct resource *ioarea;
};
-static int
-wait_xfer_done(struct i2c_au1550_data *adap)
+static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v)
{
- u32 stat;
- int i;
- volatile psc_smb_t *sp;
+ __raw_writel(v, a->psc_base + r);
+ wmb();
+}
- sp = (volatile psc_smb_t *)(adap->psc_base);
+static inline unsigned long RD(struct i2c_au1550_data *a, int r)
+{
+ return __raw_readl(a->psc_base + r);
+}
- /* Wait for Tx Buffer Empty
- */
+static int wait_xfer_done(struct i2c_au1550_data *adap)
+{
+ int i;
+
+ /* Wait for Tx Buffer Empty */
for (i = 0; i < adap->xfer_timeout; i++) {
- stat = sp->psc_smbstat;
- au_sync();
- if ((stat & PSC_SMBSTAT_TE) != 0)
+ if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE)
return 0;
udelay(1);
@@ -70,41 +82,27 @@ wait_xfer_done(struct i2c_au1550_data *adap)
return -ETIMEDOUT;
}
-static int
-wait_ack(struct i2c_au1550_data *adap)
+static int wait_ack(struct i2c_au1550_data *adap)
{
- u32 stat;
- volatile psc_smb_t *sp;
+ unsigned long stat;
if (wait_xfer_done(adap))
return -ETIMEDOUT;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- stat = sp->psc_smbevnt;
- au_sync();
-
+ stat = RD(adap, PSC_SMBEVNT);
if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
return -ETIMEDOUT;
return 0;
}
-static int
-wait_master_done(struct i2c_au1550_data *adap)
+static int wait_master_done(struct i2c_au1550_data *adap)
{
- u32 stat;
- int i;
- volatile psc_smb_t *sp;
+ int i;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- /* Wait for Master Done.
- */
- for (i = 0; i < adap->xfer_timeout; i++) {
- stat = sp->psc_smbevnt;
- au_sync();
- if ((stat & PSC_SMBEVNT_MD) != 0)
+ /* Wait for Master Done. */
+ for (i = 0; i < 2 * adap->xfer_timeout; i++) {
+ if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0)
return 0;
udelay(1);
}
@@ -115,29 +113,20 @@ wait_master_done(struct i2c_au1550_data *adap)
static int
do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
{
- volatile psc_smb_t *sp;
- u32 stat;
+ unsigned long stat;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
- /* Reset the FIFOs, clear events.
- */
- stat = sp->psc_smbstat;
- sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
- au_sync();
+ /* Reset the FIFOs, clear events. */
+ stat = RD(adap, PSC_SMBSTAT);
+ WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR);
if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) {
- sp->psc_smbpcr = PSC_SMBPCR_DC;
- au_sync();
- do {
- stat = sp->psc_smbpcr;
- au_sync();
- } while ((stat & PSC_SMBPCR_DC) != 0);
+ WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC);
+ while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0)
+ cpu_relax();
udelay(50);
}
- /* Write out the i2c chip address and specify operation
- */
+ /* Write out the i2c chip address and specify operation */
addr <<= 1;
if (rd)
addr |= 1;
@@ -146,56 +135,42 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
if (q)
addr |= PSC_SMBTXRX_STP;
- /* Put byte into fifo, start up master.
- */
- sp->psc_smbtxrx = addr;
- au_sync();
- sp->psc_smbpcr = PSC_SMBPCR_MS;
- au_sync();
+ /* Put byte into fifo, start up master. */
+ WR(adap, PSC_SMBTXRX, addr);
+ WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS);
if (wait_ack(adap))
return -EIO;
return (q) ? wait_master_done(adap) : 0;
}
-static u32
-wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
+static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out)
{
- int j;
- u32 data, stat;
- volatile psc_smb_t *sp;
+ int j;
if (wait_xfer_done(adap))
return -EIO;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
j = adap->xfer_timeout * 100;
do {
j--;
if (j <= 0)
return -EIO;
- stat = sp->psc_smbstat;
- au_sync();
- if ((stat & PSC_SMBSTAT_RE) == 0)
+ if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0)
j = 0;
else
udelay(1);
} while (j > 0);
- data = sp->psc_smbtxrx;
- au_sync();
- *ret_data = data;
+
+ *out = RD(adap, PSC_SMBTXRX);
return 0;
}
-static int
-i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
+static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
- int i;
- u32 data;
- volatile psc_smb_t *sp;
+ int i;
if (len == 0)
return 0;
@@ -204,62 +179,46 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
* zero bytes for timing, waiting for bytes to appear in the
* receive fifo, then reading the bytes.
*/
-
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
i = 0;
- while (i < (len-1)) {
- sp->psc_smbtxrx = 0;
- au_sync();
- if (wait_for_rx_byte(adap, &data))
+ while (i < (len - 1)) {
+ WR(adap, PSC_SMBTXRX, 0);
+ if (wait_for_rx_byte(adap, &buf[i]))
return -EIO;
- buf[i] = data;
i++;
}
- /* The last byte has to indicate transfer done.
- */
- sp->psc_smbtxrx = PSC_SMBTXRX_STP;
- au_sync();
+ /* The last byte has to indicate transfer done. */
+ WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP);
if (wait_master_done(adap))
return -EIO;
- data = sp->psc_smbtxrx;
- au_sync();
- buf[i] = data;
+ buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff);
return 0;
}
-static int
-i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
+static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
- int i;
- u32 data;
- volatile psc_smb_t *sp;
+ int i;
+ unsigned long data;
if (len == 0)
return 0;
- sp = (volatile psc_smb_t *)(adap->psc_base);
-
i = 0;
while (i < (len-1)) {
data = buf[i];
- sp->psc_smbtxrx = data;
- au_sync();
+ WR(adap, PSC_SMBTXRX, data);
if (wait_ack(adap))
return -EIO;
i++;
}
- /* The last byte has to indicate transfer done.
- */
+ /* The last byte has to indicate transfer done. */
data = buf[i];
data |= PSC_SMBTXRX_STP;
- sp->psc_smbtxrx = data;
- au_sync();
+ WR(adap, PSC_SMBTXRX, data);
if (wait_master_done(adap))
return -EIO;
return 0;
@@ -269,12 +228,10 @@ static int
au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct i2c_au1550_data *adap = i2c_adap->algo_data;
- volatile psc_smb_t *sp = (volatile psc_smb_t *)adap->psc_base;
struct i2c_msg *p;
int i, err = 0;
- sp->psc_ctrl = PSC_CTRL_ENABLE;
- au_sync();
+ WR(adap, PSC_CTRL, PSC_CTRL_ENABLE);
for (i = 0; !err && i < num; i++) {
p = &msgs[i];
@@ -293,14 +250,12 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
if (err == 0)
err = num;
- sp->psc_ctrl = PSC_CTRL_SUSPEND;
- au_sync();
+ WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND);
return err;
}
-static u32
-au1550_func(struct i2c_adapter *adap)
+static u32 au1550_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
@@ -312,57 +267,45 @@ static const struct i2c_algorithm au1550_algo = {
static void i2c_au1550_setup(struct i2c_au1550_data *priv)
{
- volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
- u32 stat;
-
- sp->psc_ctrl = PSC_CTRL_DISABLE;
- au_sync();
- sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
- sp->psc_smbcfg = 0;
- au_sync();
- sp->psc_ctrl = PSC_CTRL_ENABLE;
- au_sync();
- do {
- stat = sp->psc_smbstat;
- au_sync();
- } while ((stat & PSC_SMBSTAT_SR) == 0);
+ unsigned long cfg;
- sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
- PSC_SMBCFG_DD_DISABLE);
+ WR(priv, PSC_CTRL, PSC_CTRL_DISABLE);
+ WR(priv, PSC_SEL, PSC_SEL_PS_SMBUSMODE);
+ WR(priv, PSC_SMBCFG, 0);
+ WR(priv, PSC_CTRL, PSC_CTRL_ENABLE);
+ while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0)
+ cpu_relax();
+
+ cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE;
+ WR(priv, PSC_SMBCFG, cfg);
/* Divide by 8 to get a 6.25 MHz clock. The later protocol
* timings are based on this clock.
*/
- sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
- sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
- au_sync();
+ cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
+ WR(priv, PSC_SMBCFG, cfg);
+ WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK);
/* Set the protocol timer values. See Table 71 in the
* Au1550 Data Book for standard timing values.
*/
- sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
+ WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
- PSC_SMBTMR_SET_CH(15);
- au_sync();
+ PSC_SMBTMR_SET_CH(15));
- sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
- do {
- stat = sp->psc_smbstat;
- au_sync();
- } while ((stat & PSC_SMBSTAT_SR) == 0);
+ cfg |= PSC_SMBCFG_DE_ENABLE;
+ WR(priv, PSC_SMBCFG, cfg);
+ while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0)
+ cpu_relax();
- sp->psc_ctrl = PSC_CTRL_SUSPEND;
- au_sync();
+ WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND);
}
static void i2c_au1550_disable(struct i2c_au1550_data *priv)
{
- volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
-
- sp->psc_smbcfg = 0;
- sp->psc_ctrl = PSC_CTRL_DISABLE;
- au_sync();
+ WR(priv, PSC_SMBCFG, 0);
+ WR(priv, PSC_CTRL, PSC_CTRL_DISABLE);
}
/*
@@ -396,9 +339,12 @@ i2c_au1550_probe(struct platform_device *pdev)
goto out_mem;
}
- priv->psc_base = CKSEG1ADDR(r->start);
+ priv->psc_base = ioremap(r->start, resource_size(r));
+ if (!priv->psc_base) {
+ ret = -EIO;
+ goto out_map;
+ }
priv->xfer_timeout = 200;
- priv->ack_timeout = 200;
priv->adap.nr = pdev->id;
priv->adap.algo = &au1550_algo;
@@ -406,8 +352,7 @@ i2c_au1550_probe(struct platform_device *pdev)
priv->adap.dev.parent = &pdev->dev;
strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name));
- /* Now, set up the PSC for SMBus PIO mode.
- */
+ /* Now, set up the PSC for SMBus PIO mode. */
i2c_au1550_setup(priv);
ret = i2c_add_numbered_adapter(&priv->adap);
@@ -417,7 +362,8 @@ i2c_au1550_probe(struct platform_device *pdev)
}
i2c_au1550_disable(priv);
-
+ iounmap(priv->psc_base);
+out_map:
release_resource(priv->ioarea);
kfree(priv->ioarea);
out_mem:
@@ -426,14 +372,14 @@ out:
return ret;
}
-static int __devexit
-i2c_au1550_remove(struct platform_device *pdev)
+static int __devexit i2c_au1550_remove(struct platform_device *pdev)
{
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
i2c_del_adapter(&priv->adap);
i2c_au1550_disable(priv);
+ iounmap(priv->psc_base);
release_resource(priv->ioarea);
kfree(priv->ioarea);
kfree(priv);
@@ -441,49 +387,51 @@ i2c_au1550_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int
-i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state)
+static int i2c_au1550_suspend(struct device *dev)
{
- struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
+ struct i2c_au1550_data *priv = dev_get_drvdata(dev);
i2c_au1550_disable(priv);
return 0;
}
-static int
-i2c_au1550_resume(struct platform_device *pdev)
+static int i2c_au1550_resume(struct device *dev)
{
- struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
+ struct i2c_au1550_data *priv = dev_get_drvdata(dev);
i2c_au1550_setup(priv);
return 0;
}
+
+static const struct dev_pm_ops i2c_au1550_pmops = {
+ .suspend = i2c_au1550_suspend,
+ .resume = i2c_au1550_resume,
+};
+
+#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops)
+
#else
-#define i2c_au1550_suspend NULL
-#define i2c_au1550_resume NULL
+#define AU1XPSC_SMBUS_PMOPS NULL
#endif
static struct platform_driver au1xpsc_smbus_driver = {
.driver = {
.name = "au1xpsc_smbus",
.owner = THIS_MODULE,
+ .pm = AU1XPSC_SMBUS_PMOPS,
},
.probe = i2c_au1550_probe,
.remove = __devexit_p(i2c_au1550_remove),
- .suspend = i2c_au1550_suspend,
- .resume = i2c_au1550_resume,
};
-static int __init
-i2c_au1550_init(void)
+static int __init i2c_au1550_init(void)
{
return platform_driver_register(&au1xpsc_smbus_driver);
}
-static void __exit
-i2c_au1550_exit(void)
+static void __exit i2c_au1550_exit(void)
{
platform_driver_unregister(&au1xpsc_smbus_driver);
}
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index cbc98aea5b09..cdb59e5b23f7 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -631,7 +631,7 @@ static int i2c_bfin_twi_resume(struct platform_device *pdev)
struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- IRQF_DISABLED, pdev->name, iface);
+ 0, pdev->name, iface);
if (rc) {
dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
return -ENODEV;
@@ -702,7 +702,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
}
rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- IRQF_DISABLED, pdev->name, iface);
+ 0, pdev->name, iface);
if (rc) {
dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
rc = -ENODEV;
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware-core.c
index 1b42b50b5992..df8799241009 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -25,18 +25,15 @@
* ----------------------------------------------------------------------------
*
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include "i2c-designware-core.h"
/*
* Registers offset
@@ -68,15 +65,10 @@
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
#define DW_IC_RXFLR 0x78
-#define DW_IC_COMP_PARAM_1 0xf4
#define DW_IC_TX_ABRT_SOURCE 0x80
-
-#define DW_IC_CON_MASTER 0x1
-#define DW_IC_CON_SPEED_STD 0x2
-#define DW_IC_CON_SPEED_FAST 0x4
-#define DW_IC_CON_10BITADDR_MASTER 0x10
-#define DW_IC_CON_RESTART_EN 0x20
-#define DW_IC_CON_SLAVE_DISABLE 0x40
+#define DW_IC_COMP_PARAM_1 0xf4
+#define DW_IC_COMP_TYPE 0xfc
+#define DW_IC_COMP_TYPE_VALUE 0x44570140
#define DW_IC_INTR_RX_UNDER 0x001
#define DW_IC_INTR_RX_OVER 0x002
@@ -170,55 +162,23 @@ static char *abort_sources[] = {
"lost arbitration",
};
-/**
- * struct dw_i2c_dev - private i2c-designware data
- * @dev: driver model device node
- * @base: IO registers pointer
- * @cmd_complete: tx completion indicator
- * @lock: protect this struct and IO registers
- * @clk: input reference clock
- * @cmd_err: run time hadware error code
- * @msgs: points to an array of messages currently being transferred
- * @msgs_num: the number of elements in msgs
- * @msg_write_idx: the element index of the current tx message in the msgs
- * array
- * @tx_buf_len: the length of the current tx buffer
- * @tx_buf: the current tx buffer
- * @msg_read_idx: the element index of the current rx message in the msgs
- * array
- * @rx_buf_len: the length of the current rx buffer
- * @rx_buf: the current rx buffer
- * @msg_err: error status of the current transfer
- * @status: i2c master status, one of STATUS_*
- * @abort_source: copy of the TX_ABRT_SOURCE register
- * @irq: interrupt number for the i2c master
- * @adapter: i2c subsystem adapter node
- * @tx_fifo_depth: depth of the hardware tx fifo
- * @rx_fifo_depth: depth of the hardware rx fifo
- */
-struct dw_i2c_dev {
- struct device *dev;
- void __iomem *base;
- struct completion cmd_complete;
- struct mutex lock;
- struct clk *clk;
- int cmd_err;
- struct i2c_msg *msgs;
- int msgs_num;
- int msg_write_idx;
- u32 tx_buf_len;
- u8 *tx_buf;
- int msg_read_idx;
- u32 rx_buf_len;
- u8 *rx_buf;
- int msg_err;
- unsigned int status;
- u32 abort_source;
- int irq;
- struct i2c_adapter adapter;
- unsigned int tx_fifo_depth;
- unsigned int rx_fifo_depth;
-};
+u32 dw_readl(struct dw_i2c_dev *dev, int offset)
+{
+ u32 value = readl(dev->base + offset);
+
+ if (dev->swab)
+ return swab32(value);
+ else
+ return value;
+}
+
+void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
+{
+ if (dev->swab)
+ b = swab32(b);
+
+ writel(b, dev->base + offset);
+}
static u32
i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
@@ -283,13 +243,29 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
* This function is called during I2C init function, and in case of timeout at
* run time.
*/
-static void i2c_dw_init(struct dw_i2c_dev *dev)
+int i2c_dw_init(struct dw_i2c_dev *dev)
{
- u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
- u32 ic_con, hcnt, lcnt;
+ u32 input_clock_khz;
+ u32 hcnt, lcnt;
+ u32 reg;
+
+ input_clock_khz = dev->get_clk_rate_khz(dev);
+
+ /* Configure register endianess access */
+ reg = dw_readl(dev, DW_IC_COMP_TYPE);
+ if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
+ dev->swab = 1;
+ reg = DW_IC_COMP_TYPE_VALUE;
+ }
+
+ if (reg != DW_IC_COMP_TYPE_VALUE) {
+ dev_err(dev->dev, "Unknown Synopsys component type: "
+ "0x%08x\n", reg);
+ return -ENODEV;
+ }
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
/* set standard and fast speed deviders for high/low periods */
@@ -303,8 +279,8 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
47, /* tLOW = 4.7 us */
3, /* tf = 0.3 us */
0); /* No offset */
- writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
- writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
+ dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
+ dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
/* Fast-mode */
@@ -317,18 +293,17 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
13, /* tLOW = 1.3 us */
3, /* tf = 0.3 us */
0); /* No offset */
- writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
- writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
+ dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
+ dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
/* Configure Tx/Rx FIFO threshold levels */
- writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
- writel(0, dev->base + DW_IC_RX_TL);
+ dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+ dw_writel(dev, 0, DW_IC_RX_TL);
/* configure the i2c master */
- ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
- DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
- writel(ic_con, dev->base + DW_IC_CON);
+ dw_writel(dev, dev->master_cfg , DW_IC_CON);
+ return 0;
}
/*
@@ -338,7 +313,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
- while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+ while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
@@ -356,24 +331,24 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
u32 ic_con;
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
/* set the slave (target) address */
- writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+ dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR);
/* if the slave address is ten bit address, enable 10BITADDR */
- ic_con = readl(dev->base + DW_IC_CON);
+ ic_con = dw_readl(dev, DW_IC_CON);
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
ic_con |= DW_IC_CON_10BITADDR_MASTER;
else
ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
- writel(ic_con, dev->base + DW_IC_CON);
+ dw_writel(dev, ic_con, DW_IC_CON);
/* Enable the adapter */
- writel(1, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 1, DW_IC_ENABLE);
/* Enable interrupts */
- writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
}
/*
@@ -382,7 +357,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
* messages into the tx buffer. Even if the size of i2c_msg data is
* longer than the size of the tx buffer, it handles everything.
*/
-static void
+void
i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
@@ -420,15 +395,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
buf_len = msgs[dev->msg_write_idx].len;
}
- tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
- rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+ tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
+ rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
- writel(0x100, dev->base + DW_IC_DATA_CMD);
+ dw_writel(dev, 0x100, DW_IC_DATA_CMD);
rx_limit--;
} else
- writel(*buf++, dev->base + DW_IC_DATA_CMD);
+ dw_writel(dev, *buf++, DW_IC_DATA_CMD);
tx_limit--; buf_len--;
}
@@ -453,7 +428,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->msg_err)
intr_mask = 0;
- writel(intr_mask, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
}
static void
@@ -477,10 +452,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
buf = dev->rx_buf;
}
- rx_valid = readl(dev->base + DW_IC_RXFLR);
+ rx_valid = dw_readl(dev, DW_IC_RXFLR);
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
- *buf++ = readl(dev->base + DW_IC_DATA_CMD);
+ *buf++ = dw_readl(dev, DW_IC_DATA_CMD);
if (len > 0) {
dev->status |= STATUS_READ_IN_PROGRESS;
@@ -518,7 +493,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg
*/
-static int
+int
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
@@ -527,6 +502,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
mutex_lock(&dev->lock);
+ pm_runtime_get_sync(dev->dev);
INIT_COMPLETION(dev->cmd_complete);
dev->msgs = msgs;
@@ -563,7 +539,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
/* no error */
if (likely(!dev->cmd_err)) {
/* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
+ dw_writel(dev, 0, DW_IC_ENABLE);
ret = num;
goto done;
}
@@ -576,19 +552,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = -EIO;
done:
+ pm_runtime_put(dev->dev);
mutex_unlock(&dev->lock);
return ret;
}
-static u32 i2c_dw_func(struct i2c_adapter *adap)
+u32 i2c_dw_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C |
- I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK;
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ return dev->functionality;
}
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
@@ -601,47 +574,47 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* in the IC_RAW_INTR_STAT register.
*
* That is,
- * stat = readl(IC_INTR_STAT);
+ * stat = dw_readl(IC_INTR_STAT);
* equals to,
- * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
+ * stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
*
* The raw version might be useful for debugging purposes.
*/
- stat = readl(dev->base + DW_IC_INTR_STAT);
+ stat = dw_readl(dev, DW_IC_INTR_STAT);
/*
* Do not use the IC_CLR_INTR register to clear interrupts, or
* you'll miss some interrupts, triggered during the period from
- * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
+ * dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
*
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_RX_UNDER)
- readl(dev->base + DW_IC_CLR_RX_UNDER);
+ dw_readl(dev, DW_IC_CLR_RX_UNDER);
if (stat & DW_IC_INTR_RX_OVER)
- readl(dev->base + DW_IC_CLR_RX_OVER);
+ dw_readl(dev, DW_IC_CLR_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
- readl(dev->base + DW_IC_CLR_TX_OVER);
+ dw_readl(dev, DW_IC_CLR_TX_OVER);
if (stat & DW_IC_INTR_RD_REQ)
- readl(dev->base + DW_IC_CLR_RD_REQ);
+ dw_readl(dev, DW_IC_CLR_RD_REQ);
if (stat & DW_IC_INTR_TX_ABRT) {
/*
* The IC_TX_ABRT_SOURCE register is cleared whenever
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
*/
- dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
- readl(dev->base + DW_IC_CLR_TX_ABRT);
+ dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE);
+ dw_readl(dev, DW_IC_CLR_TX_ABRT);
}
if (stat & DW_IC_INTR_RX_DONE)
- readl(dev->base + DW_IC_CLR_RX_DONE);
+ dw_readl(dev, DW_IC_CLR_RX_DONE);
if (stat & DW_IC_INTR_ACTIVITY)
- readl(dev->base + DW_IC_CLR_ACTIVITY);
+ dw_readl(dev, DW_IC_CLR_ACTIVITY);
if (stat & DW_IC_INTR_STOP_DET)
- readl(dev->base + DW_IC_CLR_STOP_DET);
+ dw_readl(dev, DW_IC_CLR_STOP_DET);
if (stat & DW_IC_INTR_START_DET)
- readl(dev->base + DW_IC_CLR_START_DET);
+ dw_readl(dev, DW_IC_CLR_START_DET);
if (stat & DW_IC_INTR_GEN_CALL)
- readl(dev->base + DW_IC_CLR_GEN_CALL);
+ dw_readl(dev, DW_IC_CLR_GEN_CALL);
return stat;
}
@@ -650,13 +623,19 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* Interrupt service routine. This gets called whenever an I2C interrupt
* occurs.
*/
-static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
- u32 stat;
+ u32 stat, enabled;
+
+ enabled = dw_readl(dev, DW_IC_ENABLE);
+ stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
+ dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__,
+ dev->adapter.name, enabled, stat);
+ if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
+ return IRQ_NONE;
stat = i2c_dw_read_clear_intrbits(dev);
- dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
if (stat & DW_IC_INTR_TX_ABRT) {
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
@@ -666,7 +645,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
* Anytime TX_ABRT is set, the contents of the tx/rx
* buffers are flushed. Make sure to skip them.
*/
- writel(0, dev->base + DW_IC_INTR_MASK);
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
goto tx_aborted;
}
@@ -689,159 +668,38 @@ tx_aborted:
return IRQ_HANDLED;
}
-static struct i2c_algorithm i2c_dw_algo = {
- .master_xfer = i2c_dw_xfer,
- .functionality = i2c_dw_func,
-};
-
-static int __devinit dw_i2c_probe(struct platform_device *pdev)
+void i2c_dw_enable(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev;
- struct i2c_adapter *adap;
- struct resource *mem, *ioarea;
- int irq, r;
-
- /* NOTE: driver uses the static register mapping */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -EINVAL;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
- return irq; /* -ENXIO */
- }
-
- ioarea = request_mem_region(mem->start, resource_size(mem),
- pdev->name);
- if (!ioarea) {
- dev_err(&pdev->dev, "I2C region already claimed\n");
- return -EBUSY;
- }
-
- dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
- if (!dev) {
- r = -ENOMEM;
- goto err_release_region;
- }
-
- init_completion(&dev->cmd_complete);
- mutex_init(&dev->lock);
- dev->dev = get_device(&pdev->dev);
- dev->irq = irq;
- platform_set_drvdata(pdev, dev);
-
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- r = -ENODEV;
- goto err_free_mem;
- }
- clk_enable(dev->clk);
-
- dev->base = ioremap(mem->start, resource_size(mem));
- if (dev->base == NULL) {
- dev_err(&pdev->dev, "failure mapping io resources\n");
- r = -EBUSY;
- goto err_unuse_clocks;
- }
- {
- u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1);
-
- dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
- dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
- }
- i2c_dw_init(dev);
-
- writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
- r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
- if (r) {
- dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
- goto err_iounmap;
- }
-
- adap = &dev->adapter;
- i2c_set_adapdata(adap, dev);
- adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
- strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
- sizeof(adap->name));
- adap->algo = &i2c_dw_algo;
- adap->dev.parent = &pdev->dev;
-
- adap->nr = pdev->id;
- r = i2c_add_numbered_adapter(adap);
- if (r) {
- dev_err(&pdev->dev, "failure adding adapter\n");
- goto err_free_irq;
- }
-
- return 0;
-
-err_free_irq:
- free_irq(dev->irq, dev);
-err_iounmap:
- iounmap(dev->base);
-err_unuse_clocks:
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
-err_free_mem:
- platform_set_drvdata(pdev, NULL);
- put_device(&pdev->dev);
- kfree(dev);
-err_release_region:
- release_mem_region(mem->start, resource_size(mem));
-
- return r;
+ /* Enable the adapter */
+ dw_writel(dev, 1, DW_IC_ENABLE);
}
-static int __devexit dw_i2c_remove(struct platform_device *pdev)
+u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
- struct resource *mem;
-
- platform_set_drvdata(pdev, NULL);
- i2c_del_adapter(&dev->adapter);
- put_device(&pdev->dev);
-
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
-
- writel(0, dev->base + DW_IC_ENABLE);
- free_irq(dev->irq, dev);
- kfree(dev);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem->start, resource_size(mem));
- return 0;
+ return dw_readl(dev, DW_IC_ENABLE);
}
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:i2c_designware");
+void i2c_dw_disable(struct dw_i2c_dev *dev)
+{
+ /* Disable controller */
+ dw_writel(dev, 0, DW_IC_ENABLE);
-static struct platform_driver dw_i2c_driver = {
- .remove = __devexit_p(dw_i2c_remove),
- .driver = {
- .name = "i2c_designware",
- .owner = THIS_MODULE,
- },
-};
+ /* Disable all interupts */
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
+ dw_readl(dev, DW_IC_CLR_INTR);
+}
-static int __init dw_i2c_init_driver(void)
+void i2c_dw_clear_int(struct dw_i2c_dev *dev)
{
- return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+ dw_readl(dev, DW_IC_CLR_INTR);
}
-module_init(dw_i2c_init_driver);
-static void __exit dw_i2c_exit_driver(void)
+void i2c_dw_disable_int(struct dw_i2c_dev *dev)
{
- platform_driver_unregister(&dw_i2c_driver);
+ dw_writel(dev, 0, DW_IC_INTR_MASK);
}
-module_exit(dw_i2c_exit_driver);
-MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
-MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
-MODULE_LICENSE("GPL");
+u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
+{
+ return dw_readl(dev, DW_IC_COMP_PARAM_1);
+}
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
new file mode 100644
index 000000000000..02d1a2ddd853
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -0,0 +1,105 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * 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.
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+
+#define DW_IC_CON_MASTER 0x1
+#define DW_IC_CON_SPEED_STD 0x2
+#define DW_IC_CON_SPEED_FAST 0x4
+#define DW_IC_CON_10BITADDR_MASTER 0x10
+#define DW_IC_CON_RESTART_EN 0x20
+#define DW_IC_CON_SLAVE_DISABLE 0x40
+
+
+/**
+ * struct dw_i2c_dev - private i2c-designware data
+ * @dev: driver model device node
+ * @base: IO registers pointer
+ * @cmd_complete: tx completion indicator
+ * @lock: protect this struct and IO registers
+ * @clk: input reference clock
+ * @cmd_err: run time hadware error code
+ * @msgs: points to an array of messages currently being transfered
+ * @msgs_num: the number of elements in msgs
+ * @msg_write_idx: the element index of the current tx message in the msgs
+ * array
+ * @tx_buf_len: the length of the current tx buffer
+ * @tx_buf: the current tx buffer
+ * @msg_read_idx: the element index of the current rx message in the msgs
+ * array
+ * @rx_buf_len: the length of the current rx buffer
+ * @rx_buf: the current rx buffer
+ * @msg_err: error status of the current transfer
+ * @status: i2c master status, one of STATUS_*
+ * @abort_source: copy of the TX_ABRT_SOURCE register
+ * @irq: interrupt number for the i2c master
+ * @adapter: i2c subsystem adapter node
+ * @tx_fifo_depth: depth of the hardware tx fifo
+ * @rx_fifo_depth: depth of the hardware rx fifo
+ */
+struct dw_i2c_dev {
+ struct device *dev;
+ void __iomem *base;
+ struct completion cmd_complete;
+ struct mutex lock;
+ struct clk *clk;
+ u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
+ struct dw_pci_controller *controller;
+ int cmd_err;
+ struct i2c_msg *msgs;
+ int msgs_num;
+ int msg_write_idx;
+ u32 tx_buf_len;
+ u8 *tx_buf;
+ int msg_read_idx;
+ u32 rx_buf_len;
+ u8 *rx_buf;
+ int msg_err;
+ unsigned int status;
+ u32 abort_source;
+ int irq;
+ int swab;
+ struct i2c_adapter adapter;
+ u32 functionality;
+ u32 master_cfg;
+ unsigned int tx_fifo_depth;
+ unsigned int rx_fifo_depth;
+};
+
+extern u32 dw_readl(struct dw_i2c_dev *dev, int offset);
+extern void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
+extern int i2c_dw_init(struct dw_i2c_dev *dev);
+extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num);
+extern u32 i2c_dw_func(struct i2c_adapter *adap);
+extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id);
+extern void i2c_dw_enable(struct dw_i2c_dev *dev);
+extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev);
+extern void i2c_dw_disable(struct dw_i2c_dev *dev);
+extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
+extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
+extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
new file mode 100644
index 000000000000..9e89e7313d62
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -0,0 +1,392 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ * Copyright (C) 2011 Intel corporation.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include "i2c-designware-core.h"
+
+#define DRIVER_NAME "i2c-designware-pci"
+
+enum dw_pci_ctl_id_t {
+ moorestown_0,
+ moorestown_1,
+ moorestown_2,
+
+ medfield_0,
+ medfield_1,
+ medfield_2,
+ medfield_3,
+ medfield_4,
+ medfield_5,
+};
+
+struct dw_pci_controller {
+ u32 bus_num;
+ u32 bus_cfg;
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
+ u32 clk_khz;
+};
+
+#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
+ DW_IC_CON_SLAVE_DISABLE | \
+ DW_IC_CON_RESTART_EN)
+
+static struct dw_pci_controller dw_pci_controllers[] = {
+ [moorestown_0] = {
+ .bus_num = 0,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [moorestown_1] = {
+ .bus_num = 1,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [moorestown_2] = {
+ .bus_num = 2,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_0] = {
+ .bus_num = 0,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_1] = {
+ .bus_num = 1,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_2] = {
+ .bus_num = 2,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_3] = {
+ .bus_num = 3,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_4] = {
+ .bus_num = 4,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+ [medfield_5] = {
+ .bus_num = 5,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 25000,
+ },
+};
+static struct i2c_algorithm i2c_dw_algo = {
+ .master_xfer = i2c_dw_xfer,
+ .functionality = i2c_dw_func,
+};
+
+static int i2c_dw_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
+ int err;
+
+
+ i2c_dw_disable(i2c);
+
+ err = pci_save_state(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_save_state failed\n");
+ return err;
+ }
+
+ err = pci_set_power_state(pdev, PCI_D3hot);
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_power_state failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int i2c_dw_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
+ int err;
+ u32 enabled;
+
+ enabled = i2c_dw_is_enabled(i2c);
+ if (enabled)
+ return 0;
+
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err) {
+ dev_err(&pdev->dev, "pci_set_power_state() failed\n");
+ return err;
+ }
+
+ pci_restore_state(pdev);
+
+ i2c_dw_init(i2c);
+ i2c_dw_enable(i2c);
+ return 0;
+}
+
+static int i2c_dw_pci_runtime_idle(struct device *dev)
+{
+ int err = pm_schedule_suspend(dev, 500);
+ dev_dbg(dev, "runtime_idle called\n");
+
+ if (err != 0)
+ return 0;
+ return -EBUSY;
+}
+
+static const struct dev_pm_ops i2c_dw_pm_ops = {
+ .resume = i2c_dw_pci_resume,
+ .suspend = i2c_dw_pci_suspend,
+ SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume,
+ i2c_dw_pci_runtime_idle)
+};
+
+static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return dev->controller->clk_khz;
+}
+
+static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev,
+const struct pci_device_id *id)
+{
+ struct dw_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ unsigned long start, len;
+ void __iomem *base;
+ int r;
+ struct dw_pci_controller *controller;
+
+ if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
+ printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n",
+ id->driver_data);
+ return -EINVAL;
+ }
+
+ controller = &dw_pci_controllers[id->driver_data];
+
+ r = pci_enable_device(pdev);
+ if (r) {
+ dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
+ r);
+ goto exit;
+ }
+
+ /* Determine the address of the I2C area */
+ start = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
+ if (!start || len == 0) {
+ dev_err(&pdev->dev, "base address not set\n");
+ r = -ENODEV;
+ goto exit;
+ }
+
+ r = pci_request_region(pdev, 0, DRIVER_NAME);
+ if (r) {
+ dev_err(&pdev->dev, "failed to request I2C region "
+ "0x%lx-0x%lx\n", start,
+ (unsigned long)pci_resource_end(pdev, 0));
+ goto exit;
+ }
+
+ base = ioremap_nocache(start, len);
+ if (!base) {
+ dev_err(&pdev->dev, "I/O memory remapping failed\n");
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+
+ dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ init_completion(&dev->cmd_complete);
+ mutex_init(&dev->lock);
+ dev->clk = NULL;
+ dev->controller = controller;
+ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+ dev->base = base;
+ dev->dev = get_device(&pdev->dev);
+ dev->functionality =
+ I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->master_cfg = controller->bus_cfg;
+
+ pci_set_drvdata(pdev, dev);
+
+ dev->tx_fifo_depth = controller->tx_fifo_depth;
+ dev->rx_fifo_depth = controller->rx_fifo_depth;
+ r = i2c_dw_init(dev);
+ if (r)
+ goto err_iounmap;
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = 0;
+ adap->algo = &i2c_dw_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->nr = controller->bus_num;
+ snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
+ adap->nr);
+
+ r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev);
+ if (r) {
+ dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_iounmap;
+ }
+
+ i2c_dw_disable_int(dev);
+ i2c_dw_clear_int(dev);
+ r = i2c_add_numbered_adapter(adap);
+ if (r) {
+ dev_err(&pdev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(pdev->irq, dev);
+err_iounmap:
+ iounmap(dev->base);
+ pci_set_drvdata(pdev, NULL);
+ put_device(&pdev->dev);
+ kfree(dev);
+err_release_region:
+ pci_release_region(pdev, 0);
+exit:
+ return r;
+}
+
+static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev)
+{
+ struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
+
+ i2c_dw_disable(dev);
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ pci_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&dev->adapter);
+ put_device(&pdev->dev);
+
+ free_irq(dev->irq, dev);
+ kfree(dev);
+ pci_release_region(pdev, 0);
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("i2c_designware-pci");
+
+DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
+ /* Moorestown */
+ { PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
+ { PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
+ { PCI_VDEVICE(INTEL, 0x0804), moorestown_2 },
+ /* Medfield */
+ { PCI_VDEVICE(INTEL, 0x0817), medfield_3,},
+ { PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
+ { PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
+ { PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
+ { PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
+ { PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
+
+static struct pci_driver dw_i2c_driver = {
+ .name = DRIVER_NAME,
+ .id_table = i2_designware_pci_ids,
+ .probe = i2c_dw_pci_probe,
+ .remove = __devexit_p(i2c_dw_pci_remove),
+ .driver = {
+ .pm = &i2c_dw_pm_ops,
+ },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+ return pci_register_driver(&dw_i2c_driver);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+ pci_unregister_driver(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
new file mode 100644
index 000000000000..2d3657ab1258
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -0,0 +1,215 @@
+/*
+ * Synopsys DesignWare I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "i2c-designware-core.h"
+
+static struct i2c_algorithm i2c_dw_algo = {
+ .master_xfer = i2c_dw_xfer,
+ .functionality = i2c_dw_func,
+};
+static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return clk_get_rate(dev->clk)/1000;
+}
+
+static int __devinit dw_i2c_probe(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *ioarea;
+ int irq, r;
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return irq; /* -ENXIO */
+ }
+
+ ioarea = request_mem_region(mem->start, resource_size(mem),
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ init_completion(&dev->cmd_complete);
+ mutex_init(&dev->lock);
+ dev->dev = get_device(&pdev->dev);
+ dev->irq = irq;
+ platform_set_drvdata(pdev, dev);
+
+ dev->clk = clk_get(&pdev->dev, NULL);
+ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+
+ if (IS_ERR(dev->clk)) {
+ r = -ENODEV;
+ goto err_free_mem;
+ }
+ clk_enable(dev->clk);
+
+ dev->functionality =
+ I2C_FUNC_I2C |
+ I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
+
+ dev->base = ioremap(mem->start, resource_size(mem));
+ if (dev->base == NULL) {
+ dev_err(&pdev->dev, "failure mapping io resources\n");
+ r = -EBUSY;
+ goto err_unuse_clocks;
+ }
+ {
+ u32 param1 = i2c_dw_read_comp_param(dev);
+
+ dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
+ dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
+ }
+ r = i2c_dw_init(dev);
+ if (r)
+ goto err_iounmap;
+
+ i2c_dw_disable_int(dev);
+ r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
+ if (r) {
+ dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_iounmap;
+ }
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
+ sizeof(adap->name));
+ adap->algo = &i2c_dw_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ r = i2c_add_numbered_adapter(adap);
+ if (r) {
+ dev_err(&pdev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_iounmap:
+ iounmap(dev->base);
+err_unuse_clocks:
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+err_free_mem:
+ platform_set_drvdata(pdev, NULL);
+ put_device(&pdev->dev);
+ kfree(dev);
+err_release_region:
+ release_mem_region(mem->start, resource_size(mem));
+
+ return r;
+}
+
+static int __devexit dw_i2c_remove(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&dev->adapter);
+ put_device(&pdev->dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+
+ i2c_dw_disable(dev);
+ free_irq(dev->irq, dev);
+ kfree(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:i2c_designware");
+
+static struct platform_driver dw_i2c_driver = {
+ .remove = __devexit_p(dw_i2c_remove),
+ .driver = {
+ .name = "i2c_designware",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+ return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index ce1a32b71e47..8cebef49aeac 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -64,6 +64,7 @@
#define TEN_BIT_ADDR_DEFAULT 0xF000
#define TEN_BIT_ADDR_MASK 0xF0
#define PCH_START 0x0020
+#define PCH_RESTART 0x0004
#define PCH_ESR_START 0x0001
#define PCH_BUFF_START 0x1
#define PCH_REPSTART 0x0004
@@ -273,23 +274,24 @@ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
s32 timeout)
{
void __iomem *p = adap->pch_base_address;
+ ktime_t ns_val;
+
+ if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+ return 0;
/* MAX timeout value is timeout*1000*1000nsec */
- ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
+ ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
do {
- if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- break;
msleep(20);
+ if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+ return 0;
} while (ktime_lt(ktime_get(), ns_val));
pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
+ pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
+ pch_i2c_init(adap);
- if (timeout == 0) {
- pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
- return -ETIME;
- }
-
- return 0;
+ return -ETIME;
}
/**
@@ -311,21 +313,19 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap)
*/
static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
{
- s32 ret;
+ long ret;
ret = wait_event_timeout(pch_event,
(adap->pch_event_flag != 0), msecs_to_jiffies(50));
- if (ret < 0) {
- pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
- return ret;
- }
if (ret == 0) {
pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
+ adap->pch_event_flag = 0;
return -ETIMEDOUT;
}
if (adap->pch_event_flag & I2C_ERROR_MASK) {
pch_err(adap, "error bits set: %x\n", adap->pch_event_flag);
+ adap->pch_event_flag = 0;
return -EIO;
}
@@ -394,6 +394,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
u32 addr_2_msb;
u32 addr_8_lsb;
s32 wrcount;
+ s32 rtn;
void __iomem *p = adap->pch_base_address;
length = msgs->len;
@@ -412,15 +413,29 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
+ addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06;
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
if (first)
pch_i2c_start(adap);
- if (pch_i2c_wait_for_xfer_complete(adap) == 0 &&
- pch_i2c_getack(adap) == 0) {
+
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
addr_8_lsb = (addr & I2C_ADDR_MSK);
iowrite32(addr_8_lsb, p + PCH_I2CDR);
- } else {
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
pch_i2c_stop(adap);
return -ETIME;
}
@@ -431,30 +446,51 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
pch_i2c_start(adap);
}
- if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
- (pch_i2c_getack(adap) == 0)) {
- for (wrcount = 0; wrcount < length; ++wrcount) {
- /* write buffer value to I2C data register */
- iowrite32(buf[wrcount], p + PCH_I2CDR);
- pch_dbg(adap, "writing %x to Data register\n",
- buf[wrcount]);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- if (pch_i2c_wait_for_xfer_complete(adap) != 0)
- return -ETIME;
+ for (wrcount = 0; wrcount < length; ++wrcount) {
+ /* write buffer value to I2C data register */
+ iowrite32(buf[wrcount], p + PCH_I2CDR);
+ pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]);
- if (pch_i2c_getack(adap))
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
return -EIO;
+ }
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMCF_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
}
+ }
- /* check if this is the last message */
- if (last)
- pch_i2c_stop(adap);
- else
- pch_i2c_repstart(adap);
- } else {
+ /* check if this is the last message */
+ if (last)
pch_i2c_stop(adap);
- return -EIO;
- }
+ else
+ pch_i2c_repstart(adap);
pch_dbg(adap, "return=%d\n", wrcount);
@@ -484,6 +520,19 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap)
}
/**
+ * pch_i2c_restart() - Generate I2C restart condition in normal mode.
+ * @adap: Pointer to struct i2c_algo_pch_data.
+ *
+ * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA.
+ */
+static void pch_i2c_restart(struct i2c_algo_pch_data *adap)
+{
+ void __iomem *p = adap->pch_base_address;
+ pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL));
+ pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART);
+}
+
+/**
* pch_i2c_readbytes() - read data from I2C bus in normal mode.
* @i2c_adap: Pointer to the struct i2c_adapter.
* @msgs: Pointer to i2c_msg structure.
@@ -500,7 +549,9 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u32 length;
u32 addr;
u32 addr_2_msb;
+ u32 addr_8_lsb;
void __iomem *p = adap->pch_base_address;
+ s32 rtn;
length = msgs->len;
buf = msgs->buf;
@@ -515,9 +566,55 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
+ addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+ if (first)
+ pch_i2c_start(adap);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ addr_8_lsb = (addr & I2C_ADDR_MSK);
+ iowrite32(addr_8_lsb, p + PCH_I2CDR);
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
+ pch_i2c_restart(adap);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ addr_2_msb |= I2C_RD;
+ iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK,
+ p + PCH_I2CDR);
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR,
+ I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
} else {
/* 7 address bits + R/W bit */
addr = (((addr) << 1) | (I2C_RD));
@@ -528,56 +625,81 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
if (first)
pch_i2c_start(adap);
- if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
- (pch_i2c_getack(adap) == 0)) {
- pch_dbg(adap, "return %d\n", 0);
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave address"
+ "setting\n");
+ return -EIO;
+ }
+ } else if (rtn == -EIO) { /* Arbitration Lost */
+ pch_err(adap, "Lost Arbitration\n");
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
+ pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
+ pch_i2c_init(adap);
+ return -EAGAIN;
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- if (length == 0) {
- pch_i2c_stop(adap);
- ioread32(p + PCH_I2CDR); /* Dummy read needs */
+ if (length == 0) {
+ pch_i2c_stop(adap);
+ ioread32(p + PCH_I2CDR); /* Dummy read needs */
- count = length;
- } else {
- int read_index;
- int loop;
- pch_i2c_sendack(adap);
+ count = length;
+ } else {
+ int read_index;
+ int loop;
+ pch_i2c_sendack(adap);
- /* Dummy read */
- for (loop = 1, read_index = 0; loop < length; loop++) {
- buf[read_index] = ioread32(p + PCH_I2CDR);
+ /* Dummy read */
+ for (loop = 1, read_index = 0; loop < length; loop++) {
+ buf[read_index] = ioread32(p + PCH_I2CDR);
- if (loop != 1)
- read_index++;
+ if (loop != 1)
+ read_index++;
- if (pch_i2c_wait_for_xfer_complete(adap) != 0) {
- pch_i2c_stop(adap);
- return -ETIME;
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave"
+ "address setting\n");
+ return -EIO;
}
- } /* end for */
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
+ }
- pch_i2c_sendnack(adap);
+ } /* end for */
- buf[read_index] = ioread32(p + PCH_I2CDR);
+ pch_i2c_sendnack(adap);
- if (length != 1)
- read_index++;
+ buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */
- if (pch_i2c_wait_for_xfer_complete(adap) == 0) {
- if (last)
- pch_i2c_stop(adap);
- else
- pch_i2c_repstart(adap);
+ if (length != 1)
+ read_index++;
- buf[read_index++] = ioread32(p + PCH_I2CDR);
- count = read_index;
- } else {
- count = -ETIME;
+ rtn = pch_i2c_wait_for_xfer_complete(adap);
+ if (rtn == 0) {
+ if (pch_i2c_getack(adap)) {
+ pch_dbg(adap, "Receive NACK for slave"
+ "address setting\n");
+ return -EIO;
}
-
+ } else { /* wait-event timeout */
+ pch_i2c_stop(adap);
+ return -ETIME;
}
- } else {
- count = -ETIME;
- pch_i2c_stop(adap);
+
+ if (last)
+ pch_i2c_stop(adap);
+ else
+ pch_i2c_repstart(adap);
+
+ buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */
+ count = read_index;
}
return count;
diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c
index 3876a2478bd7..63bb1cc2a042 100644
--- a/drivers/i2c/busses/i2c-highlander.c
+++ b/drivers/i2c/busses/i2c-highlander.c
@@ -387,7 +387,7 @@ static int __devinit highlander_i2c_probe(struct platform_device *pdev)
dev->irq = 0;
if (dev->irq) {
- ret = request_irq(dev->irq, highlander_i2c_irq, IRQF_DISABLED,
+ ret = request_irq(dev->irq, highlander_i2c_irq, 0,
pdev->name, dev);
if (unlikely(ret))
goto err_unmap;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 4c2a62b75b5c..58832e578fff 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -48,6 +48,9 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_i2c.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
@@ -125,6 +128,11 @@ struct imx_i2c_struct {
unsigned int ifdr; /* IMX_I2C_IFDR */
};
+static const struct of_device_id i2c_imx_dt_ids[] = {
+ { .compatible = "fsl,imx1-i2c", },
+ { /* sentinel */ }
+};
+
/** Functions for IMX I2C adapter driver ***************************************
*******************************************************************************/
@@ -466,10 +474,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx;
struct resource *res;
- struct imxi2c_platform_data *pdata;
+ struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
void __iomem *base;
resource_size_t res_size;
- int irq;
+ int irq, bitrate;
int ret;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
@@ -485,19 +493,11 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
return -ENOENT;
}
- pdata = pdev->dev.platform_data;
-
- if (pdata && pdata->init) {
- ret = pdata->init(&pdev->dev);
- if (ret)
- return ret;
- }
-
res_size = resource_size(res);
if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
- ret = -EBUSY;
- goto fail0;
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ return -EBUSY;
}
base = ioremap(res->start, res_size);
@@ -520,6 +520,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.algo = &i2c_imx_algo;
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;
@@ -546,10 +547,12 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
/* Set up clock divider */
- if (pdata && pdata->bitrate)
- i2c_imx_set_clk(i2c_imx, pdata->bitrate);
- else
- i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
+ bitrate = IMX_I2C_BIT_RATE;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &bitrate);
+ if (ret < 0 && pdata && pdata->bitrate)
+ bitrate = pdata->bitrate;
+ i2c_imx_set_clk(i2c_imx, bitrate);
/* Set up chip registers to defaults */
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
@@ -562,6 +565,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
goto fail5;
}
+ of_i2c_register_devices(&i2c_imx->adapter);
+
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
@@ -586,16 +591,12 @@ fail2:
iounmap(base);
fail1:
release_mem_region(res->start, resource_size(res));
-fail0:
- if (pdata && pdata->exit)
- pdata->exit(&pdev->dev);
return ret; /* Return error number */
}
static int __exit i2c_imx_remove(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
- struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
/* remove adapter */
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
@@ -611,10 +612,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
- /* Shut down hardware */
- if (pdata && pdata->exit)
- pdata->exit(&pdev->dev);
-
clk_put(i2c_imx->clk);
iounmap(i2c_imx->base);
@@ -628,6 +625,7 @@ static struct platform_driver i2c_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = i2c_imx_dt_ids,
}
};
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index b228e09c5d05..5267ab93d550 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -63,11 +63,11 @@
/* Master controller (MCR) register */
#define I2C_MCR_OP (0x1 << 0) /* Operation */
#define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */
-#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */
+#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */
#define I2C_MCR_SB (0x1 << 11) /* Extended address */
#define I2C_MCR_AM (0x3 << 12) /* Address type */
-#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */
-#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */
+#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */
+#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */
/* Status register (SR) */
#define I2C_SR_OP (0x3 << 0) /* Operation */
@@ -77,7 +77,7 @@
#define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */
/* Interrupt mask set/clear (IMSCR) bits */
-#define I2C_IT_TXFE (0x1 << 0)
+#define I2C_IT_TXFE (0x1 << 0)
#define I2C_IT_TXFNE (0x1 << 1)
#define I2C_IT_TXFF (0x1 << 2)
#define I2C_IT_TXFOVR (0x1 << 3)
@@ -135,31 +135,31 @@ struct i2c_nmk_client {
};
/**
- * struct nmk_i2c_dev - private data structure of the controller
- * @pdev: parent platform device
- * @adap: corresponding I2C adapter
- * @irq: interrupt line for the controller
- * @virtbase: virtual io memory area
- * @clk: hardware i2c block clock
- * @cfg: machine provided controller configuration
- * @cli: holder of client specific data
- * @stop: stop condition
- * @xfer_complete: acknowledge completion for a I2C message
- * @result: controller propogated result
- * @regulator: pointer to i2c regulator
- * @busy: Busy doing transfer
+ * struct nmk_i2c_dev - private data structure of the controller.
+ * @pdev: parent platform device.
+ * @adap: corresponding I2C adapter.
+ * @irq: interrupt line for the controller.
+ * @virtbase: virtual io memory area.
+ * @clk: hardware i2c block clock.
+ * @cfg: machine provided controller configuration.
+ * @cli: holder of client specific data.
+ * @stop: stop condition.
+ * @xfer_complete: acknowledge completion for a I2C message.
+ * @result: controller propogated result.
+ * @regulator: pointer to i2c regulator.
+ * @busy: Busy doing transfer.
*/
struct nmk_i2c_dev {
struct platform_device *pdev;
- struct i2c_adapter adap;
- int irq;
+ struct i2c_adapter adap;
+ int irq;
void __iomem *virtbase;
struct clk *clk;
struct nmk_i2c_controller cfg;
struct i2c_nmk_client cli;
- int stop;
+ int stop;
struct completion xfer_complete;
- int result;
+ int result;
struct regulator *regulator;
bool busy;
};
@@ -217,8 +217,9 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
}
}
- dev_err(&dev->pdev->dev, "flushing operation timed out "
- "giving up after %d attempts", LOOP_ATTEMPTS);
+ dev_err(&dev->pdev->dev,
+ "flushing operation timed out giving up after %d attempts",
+ LOOP_ATTEMPTS);
return -ETIMEDOUT;
}
@@ -270,7 +271,7 @@ exit:
}
/* enable peripheral, master mode operation */
-#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE)
+#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE)
/**
* load_i2c_mcr_reg() - load the MCR register
@@ -363,8 +364,8 @@ 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, "do not support this mode "
- "defaulting to std. mode\n");
+ dev_err(&dev->pdev->dev,
+ "do not support this mode defaulting to std. mode\n");
brcr2 = i2c_clk/(100000 * 2) & 0xffff;
writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
writel(I2C_FREQ_MODE_STANDARD << 4,
@@ -423,7 +424,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
if (timeout < 0) {
dev_err(&dev->pdev->dev,
- "wait_for_completion_timeout"
+ "wait_for_completion_timeout "
"returned %d waiting for event\n", timeout);
status = timeout;
}
@@ -556,8 +557,8 @@ 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", cause
- >= ARRAY_SIZE(abort_causes) ?
+ dev_err(&dev->pdev->dev, "%s\n",
+ cause >= ARRAY_SIZE(abort_causes) ?
"unknown reason" :
abort_causes[cause]);
}
@@ -582,13 +583,13 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
*
* NOTE:
* READ TRANSFER : We impose a restriction of the first message to be the
- * index message for any read transaction.
- * - a no index is coded as '0',
- * - 2byte big endian index is coded as '3'
- * !!! msg[0].buf holds the actual index.
- * This is compatible with generic messages of smbus emulator
- * that send a one byte index.
- * eg. a I2C transation to read 2 bytes from index 0
+ * index message for any read transaction.
+ * - a no index is coded as '0',
+ * - 2byte big endian index is coded as '3'
+ * !!! msg[0].buf holds the actual index.
+ * This is compatible with generic messages of smbus emulator
+ * that send a one byte index.
+ * eg. a I2C transation to read 2 bytes from index 0
* idx = 0;
* msg[0].addr = client->addr;
* msg[0].flags = 0x0;
@@ -644,8 +645,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
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");
+ dev_err(&dev->pdev->dev,
+ "10 bit addressing not supported\n");
status = -EINVAL;
goto out;
@@ -789,8 +790,9 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
if (dev->cli.count) {
dev->result = -EIO;
- dev_err(&dev->pdev->dev, "%lu bytes still remain to be"
- "xfered\n", dev->cli.count);
+ dev_err(&dev->pdev->dev,
+ "%lu bytes still remain to be xfered\n",
+ dev->cli.count);
(void) init_hw(dev);
}
complete(&dev->xfer_complete);
@@ -923,7 +925,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
}
if (request_mem_region(res->start, resource_size(res),
- DRIVER_NAME "I/O region") == NULL) {
+ DRIVER_NAME "I/O region") == NULL) {
ret = -EBUSY;
goto err_no_region;
}
@@ -935,7 +937,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
}
dev->irq = platform_get_irq(pdev, 0);
- ret = request_irq(dev->irq, i2c_irq_handler, IRQF_DISABLED,
+ 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);
@@ -980,8 +982,9 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
- dev_info(&pdev->dev, "initialize %s on virtual "
- "base %p\n", adap->name, dev->virtbase);
+ dev_info(&pdev->dev,
+ "initialize %s on virtual base %p\n",
+ adap->name, dev->virtbase);
ret = i2c_add_numbered_adapter(adap);
if (ret) {
diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c
index 72434263787b..835e47b39bc2 100644
--- a/drivers/i2c/busses/i2c-nuc900.c
+++ b/drivers/i2c/busses/i2c-nuc900.c
@@ -610,7 +610,7 @@ static int __devinit nuc900_i2c_probe(struct platform_device *pdev)
goto err_iomap;
}
- ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_SHARED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 2dfb63176856..a43d0023446a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -42,12 +42,12 @@
#include <linux/pm_runtime.h>
/* I2C controller revisions */
-#define OMAP_I2C_REV_2 0x20
+#define OMAP_I2C_OMAP1_REV_2 0x20
/* 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_4430 0x40
+#define OMAP_I2C_REV_ON_3530_4430 0x40
/* timeout waiting for the controller to respond */
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
@@ -72,11 +72,12 @@ enum {
OMAP_I2C_SCLH_REG,
OMAP_I2C_SYSTEST_REG,
OMAP_I2C_BUFSTAT_REG,
- OMAP_I2C_REVNB_LO,
- OMAP_I2C_REVNB_HI,
- OMAP_I2C_IRQSTATUS_RAW,
- OMAP_I2C_IRQENABLE_SET,
- OMAP_I2C_IRQENABLE_CLR,
+ /* only on OMAP4430 */
+ OMAP_I2C_IP_V2_REVNB_LO,
+ OMAP_I2C_IP_V2_REVNB_HI,
+ OMAP_I2C_IP_V2_IRQSTATUS_RAW,
+ OMAP_I2C_IP_V2_IRQENABLE_SET,
+ OMAP_I2C_IP_V2_IRQENABLE_CLR,
};
/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
@@ -193,7 +194,6 @@ struct omap_i2c_dev {
*/
u8 rev;
unsigned b_hw:1; /* bad h/w fixes */
- unsigned idle:1;
u16 iestate; /* Saved interrupt register */
u16 pscstate;
u16 scllstate;
@@ -204,7 +204,7 @@ struct omap_i2c_dev {
u16 errata;
};
-static const u8 reg_map[] = {
+static const u8 reg_map_ip_v1[] = {
[OMAP_I2C_REV_REG] = 0x00,
[OMAP_I2C_IE_REG] = 0x01,
[OMAP_I2C_STAT_REG] = 0x02,
@@ -225,7 +225,7 @@ static const u8 reg_map[] = {
[OMAP_I2C_BUFSTAT_REG] = 0x10,
};
-static const u8 omap4_reg_map[] = {
+static const u8 reg_map_ip_v2[] = {
[OMAP_I2C_REV_REG] = 0x04,
[OMAP_I2C_IE_REG] = 0x2c,
[OMAP_I2C_STAT_REG] = 0x28,
@@ -244,11 +244,11 @@ static const u8 omap4_reg_map[] = {
[OMAP_I2C_SCLH_REG] = 0xb8,
[OMAP_I2C_SYSTEST_REG] = 0xbC,
[OMAP_I2C_BUFSTAT_REG] = 0xc0,
- [OMAP_I2C_REVNB_LO] = 0x00,
- [OMAP_I2C_REVNB_HI] = 0x04,
- [OMAP_I2C_IRQSTATUS_RAW] = 0x24,
- [OMAP_I2C_IRQENABLE_SET] = 0x2c,
- [OMAP_I2C_IRQENABLE_CLR] = 0x30,
+ [OMAP_I2C_IP_V2_REVNB_LO] = 0x00,
+ [OMAP_I2C_IP_V2_REVNB_HI] = 0x04,
+ [OMAP_I2C_IP_V2_IRQSTATUS_RAW] = 0x24,
+ [OMAP_I2C_IP_V2_IRQENABLE_SET] = 0x2c,
+ [OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
};
static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -266,17 +266,11 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
static void omap_i2c_unidle(struct omap_i2c_dev *dev)
{
- struct platform_device *pdev;
struct omap_i2c_bus_platform_data *pdata;
- WARN_ON(!dev->idle);
+ pdata = dev->dev->platform_data;
- pdev = to_platform_device(dev->dev);
- pdata = pdev->dev.platform_data;
-
- pm_runtime_get_sync(&pdev->dev);
-
- if (cpu_is_omap34xx()) {
+ if (pdata->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);
@@ -286,7 +280,6 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
}
- dev->idle = 0;
/*
* Don't write to this register if the IE state is 0 as it can
@@ -298,32 +291,25 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
static void omap_i2c_idle(struct omap_i2c_dev *dev)
{
- struct platform_device *pdev;
struct omap_i2c_bus_platform_data *pdata;
u16 iv;
- WARN_ON(dev->idle);
-
- pdev = to_platform_device(dev->dev);
- pdata = pdev->dev.platform_data;
+ pdata = dev->dev->platform_data;
dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
- if (dev->rev >= OMAP_I2C_REV_ON_4430)
- omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1);
+ if (pdata->rev == 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_REV_2) {
+ 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 before the dev->idle store occurs */
+ /* Flush posted write */
omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
}
- dev->idle = 1;
-
- pm_runtime_put_sync(&pdev->dev);
}
static int omap_i2c_init(struct omap_i2c_dev *dev)
@@ -334,8 +320,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
unsigned long timeout;
unsigned long internal_clk = 0;
struct clk *fclk;
+ struct omap_i2c_bus_platform_data *pdata;
+
+ pdata = dev->dev->platform_data;
- if (dev->rev >= OMAP_I2C_REV_2) {
+ if (dev->rev >= OMAP_I2C_OMAP1_REV_2) {
/* Disable I2C controller before soft reset */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) &
@@ -378,12 +367,13 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
* REVISIT: Some wkup sources might not be needed.
*/
dev->westate = OMAP_I2C_WE_ALL;
- omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+ omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
+ dev->westate);
}
}
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
- if (cpu_class_is_omap1()) {
+ if (pdata->flags & OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK) {
/*
* The I2C functional clock is the armxor_ck, so there's
* no need to get "armxor_ck" separately. Now, if OMAP2420
@@ -407,7 +397,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
psc = fclk_rate / 12000000;
}
- if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
+ if (!(pdata->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) {
/*
* HSI2C controller internal clk rate should be 19.2 Mhz for
@@ -415,7 +405,8 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
* to get longer filter period for better noise suppression.
* The filter is iclk (fclk for HS) period.
*/
- if (dev->speed > 400 || cpu_is_omap2430())
+ if (dev->speed > 400 ||
+ pdata->flags & OMAP_I2C_FLAG_FORCE_19200_INT_CLK)
internal_clk = 19200;
else if (dev->speed > 100)
internal_clk = 9600;
@@ -484,7 +475,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
dev->errata = 0;
- if (cpu_is_omap2430() || cpu_is_omap34xx())
+ if (pdata->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
dev->errata |= I2C_OMAP_ERRATA_I207;
/* Enable interrupts */
@@ -493,7 +484,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
OMAP_I2C_IE_AL) | ((dev->fifo_size) ?
(OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
- if (cpu_is_omap34xx()) {
+ if (pdata->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
dev->pscstate = psc;
dev->scllstate = scll;
dev->sclhstate = sclh;
@@ -642,7 +633,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
int i;
int r;
- omap_i2c_unidle(dev);
+ pm_runtime_get_sync(dev->dev);
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
@@ -665,7 +656,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap_i2c_wait_for_bb(dev);
out:
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
return r;
}
@@ -720,12 +711,12 @@ static inline void i2c_omap_errata_i207(struct omap_i2c_dev *dev, u16 stat)
#ifdef CONFIG_ARCH_OMAP15XX
static irqreturn_t
-omap_i2c_rev1_isr(int this_irq, void *dev_id)
+omap_i2c_omap1_isr(int this_irq, void *dev_id)
{
struct omap_i2c_dev *dev = dev_id;
u16 iv, w;
- if (dev->idle)
+ if (pm_runtime_suspended(dev->dev))
return IRQ_NONE;
iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
@@ -774,7 +765,7 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
return IRQ_HANDLED;
}
#else
-#define omap_i2c_rev1_isr NULL
+#define omap_i2c_omap1_isr NULL
#endif
/*
@@ -813,8 +804,11 @@ omap_i2c_isr(int this_irq, void *dev_id)
u16 bits;
u16 stat, w;
int err, count = 0;
+ struct omap_i2c_bus_platform_data *pdata;
- if (dev->idle)
+ pdata = dev->dev->platform_data;
+
+ if (pm_runtime_suspended(dev->dev))
return IRQ_NONE;
bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
@@ -881,8 +875,8 @@ complete:
* Data reg in 2430, omap3 and
* omap4 is 8 bit wide
*/
- if (cpu_class_is_omap1() ||
- cpu_is_omap2420()) {
+ if (pdata->flags &
+ OMAP_I2C_FLAG_16BIT_DATA_REG) {
if (dev->buf_len) {
*dev->buf++ = w >> 8;
dev->buf_len--;
@@ -924,8 +918,8 @@ complete:
* Data reg in 2430, omap3 and
* omap4 is 8 bit wide
*/
- if (cpu_class_is_omap1() ||
- cpu_is_omap2420()) {
+ if (pdata->flags &
+ OMAP_I2C_FLAG_16BIT_DATA_REG) {
if (dev->buf_len) {
w |= *dev->buf++ << 8;
dev->buf_len--;
@@ -1016,7 +1010,6 @@ omap_i2c_probe(struct platform_device *pdev)
}
dev->speed = speed;
- dev->idle = 1;
dev->dev = &pdev->dev;
dev->irq = irq->start;
dev->base = ioremap(mem->start, resource_size(mem));
@@ -1027,27 +1020,22 @@ omap_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
- if (cpu_is_omap7xx())
- dev->reg_shift = 1;
- else if (cpu_is_omap44xx())
- dev->reg_shift = 0;
- else
- dev->reg_shift = 2;
+ dev->reg_shift = (pdata->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;
- if (cpu_is_omap44xx())
- dev->regs = (u8 *) omap4_reg_map;
+ if (pdata->rev == OMAP_I2C_IP_VERSION_2)
+ dev->regs = (u8 *)reg_map_ip_v2;
else
- dev->regs = (u8 *) reg_map;
+ dev->regs = (u8 *)reg_map_ip_v1;
- pm_runtime_enable(&pdev->dev);
- omap_i2c_unidle(dev);
+ pm_runtime_enable(dev->dev);
+ pm_runtime_get_sync(dev->dev);
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;
- if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
+ if (!(pdata->flags & OMAP_I2C_FLAG_NO_FIFO)) {
u16 s;
/* Set up the fifo size - Get total size */
@@ -1059,7 +1047,7 @@ omap_i2c_probe(struct platform_device *pdev)
* size. This is to ensure that we can handle the status on int
* call back latencies.
*/
- if (dev->rev >= OMAP_I2C_REV_ON_4430) {
+ if (dev->rev >= OMAP_I2C_REV_ON_3530_4430) {
dev->fifo_size = 0;
dev->b_hw = 0; /* Disable hardware fixes */
} else {
@@ -1075,7 +1063,8 @@ omap_i2c_probe(struct platform_device *pdev)
/* reset ASAP, clearing any IRQs */
omap_i2c_init(dev);
- isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;
+ 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);
if (r) {
@@ -1083,10 +1072,10 @@ omap_i2c_probe(struct platform_device *pdev)
goto err_unuse_clocks;
}
- dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n",
- pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed);
+ dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
+ pdata->rev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
@@ -1110,7 +1099,7 @@ err_free_irq:
free_irq(dev->irq, dev);
err_unuse_clocks:
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
- omap_i2c_idle(dev);
+ pm_runtime_put(dev->dev);
iounmap(dev->base);
err_free_mem:
platform_set_drvdata(pdev, NULL);
@@ -1139,12 +1128,43 @@ omap_i2c_remove(struct platform_device *pdev)
return 0;
}
+#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);
+
+ omap_i2c_idle(_dev);
+
+ return 0;
+}
+
+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);
+
+ return 0;
+}
+
+static struct dev_pm_ops omap_i2c_pm_ops = {
+ .runtime_suspend = omap_i2c_runtime_suspend,
+ .runtime_resume = omap_i2c_runtime_resume,
+};
+#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
+#else
+#define OMAP_I2C_PM_OPS NULL
+#endif
+
static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
.remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
.owner = THIS_MODULE,
+ .pm = OMAP_I2C_PM_OPS,
},
};
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index dfa7ae9c1b8e..127051b06921 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -306,7 +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_DISABLED | IRQF_SAMPLE_RANDOM,
+ IRQF_SHARED | IRQF_SAMPLE_RANDOM,
pldev->name, &pmcmsptwi_data);
if (rc == 0) {
/*
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index f84a63c6dd97..2754cef86a06 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -35,6 +35,8 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
#include <asm/irq.h>
@@ -78,6 +80,8 @@ struct s3c24xx_i2c {
struct resource *ioarea;
struct i2c_adapter adap;
+ struct s3c2410_platform_i2c *pdata;
+ int gpios[2];
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
@@ -95,6 +99,12 @@ static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
struct platform_device *pdev = to_platform_device(i2c->dev);
enum s3c24xx_i2c_type type;
+#ifdef CONFIG_OF
+ if (i2c->dev->of_node)
+ return of_device_is_compatible(i2c->dev->of_node,
+ "samsung,s3c2440-i2c");
+#endif
+
type = platform_get_device_id(pdev)->driver_data;
return type == TYPE_S3C2440;
}
@@ -625,7 +635,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
{
- struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
unsigned long clkin = clk_get_rate(i2c->clk);
unsigned int divs, div1;
unsigned long target_frequency;
@@ -741,6 +751,49 @@ static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
}
#endif
+#ifdef CONFIG_OF
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ int idx, gpio, ret;
+
+ for (idx = 0; idx < 2; idx++) {
+ gpio = of_get_gpio(i2c->dev->of_node, idx);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+ goto free_gpio;
+ }
+
+ ret = gpio_request(gpio, "i2c-bus");
+ if (ret) {
+ dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+ goto free_gpio;
+ }
+ }
+ return 0;
+
+free_gpio:
+ while (--idx >= 0)
+ gpio_free(i2c->gpios[idx]);
+ return -EINVAL;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+ unsigned int idx;
+ for (idx = 0; idx < 2; idx++)
+ gpio_free(i2c->gpios[idx]);
+}
+#else
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ return -EINVAL;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+}
+#endif
+
/* s3c24xx_i2c_init
*
* initialise the controller, set the IO lines and frequency
@@ -754,12 +807,15 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* get the plafrom data */
- pdata = i2c->dev->platform_data;
+ pdata = i2c->pdata;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
+ else
+ if (s3c24xx_i2c_parse_dt_gpio(i2c))
+ return -EINVAL;
/* write slave address */
@@ -785,6 +841,34 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
return 0;
}
+#ifdef CONFIG_OF
+/* s3c24xx_i2c_parse_dt
+ *
+ * Parse the device tree node and retreive the platform data.
+*/
+
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
+
+ if (!np)
+ return;
+
+ pdata->bus_num = -1; /* i2c bus number is dynamically assigned */
+ of_property_read_u32(np, "samsung,i2c-sda-delay", &pdata->sda_delay);
+ of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
+ of_property_read_u32(np, "samsung,i2c-max-bus-freq",
+ (u32 *)&pdata->frequency);
+}
+#else
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ return;
+}
+#endif
+
/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
@@ -793,14 +877,16 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
+ struct s3c2410_platform_i2c *pdata = NULL;
struct resource *res;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
+ if (!pdev->dev.of_node) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
@@ -809,6 +895,17 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!i2c->pdata) {
+ ret = -ENOMEM;
+ goto err_noclk;
+ }
+
+ if (pdata)
+ memcpy(i2c->pdata, pdata, sizeof(*pdata));
+ else
+ s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
+
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
@@ -883,7 +980,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_iomap;
}
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
+ ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
@@ -903,7 +1000,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
* being bus 0.
*/
- i2c->adap.nr = pdata->bus_num;
+ i2c->adap.nr = i2c->pdata->bus_num;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
@@ -911,6 +1009,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_cpufreq;
}
+ of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
@@ -959,6 +1058,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
iounmap(i2c->regs);
release_resource(i2c->ioarea);
+ s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea);
kfree(i2c);
@@ -1012,6 +1112,17 @@ static struct platform_device_id s3c24xx_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
+#ifdef CONFIG_OF
+static const struct of_device_id s3c24xx_i2c_match[] = {
+ { .compatible = "samsung,s3c2410-i2c" },
+ { .compatible = "samsung,s3c2440-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
+#else
+#define s3c24xx_i2c_match NULL
+#endif
+
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
@@ -1020,6 +1131,7 @@ static struct platform_driver s3c24xx_i2c_driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
+ .of_match_table = s3c24xx_i2c_match,
},
};
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index 3cad8fecc3d3..c418c41eff3d 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -502,7 +502,7 @@ static int __devinit sh7760_i2c_probe(struct platform_device *pdev)
}
OUT32(id, I2CCCR, ret);
- if (request_irq(id->irq, sh7760_i2c_irq, IRQF_DISABLED,
+ if (request_irq(id->irq, sh7760_i2c_irq, 0,
SH7760_I2C_DEVNAME, id)) {
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
ret = -EBUSY;
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index f633a53b6dbe..675c9692d148 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -543,7 +543,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
for (n = res->start; hook && n <= res->end; n++) {
- if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED,
+ if (request_irq(n, sh_mobile_i2c_isr, 0,
dev_name(&dev->dev), dev)) {
for (n--; n >= res->start; n--)
free_irq(n, dev);
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 99879617e686..4d44af181f37 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -916,7 +916,7 @@ stu300_probe(struct platform_device *pdev)
}
dev->irq = platform_get_irq(pdev, 0);
- if (request_irq(dev->irq, stu300_irh, IRQF_DISABLED,
+ if (request_irq(dev->irq, stu300_irh, 0,
NAME, dev)) {
ret = -EIO;
goto err_no_irq;
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 3c94c4a81a55..b0505309faa7 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -566,7 +566,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
struct clk *clk;
struct clk *i2c_clk;
const unsigned int *prop;
- void *base;
+ void __iomem *base;
int irq;
int ret = 0;
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index 986e5f62debe..91e349c884c5 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -550,7 +550,7 @@ static int __devexit scx200_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver scx200_pci_drv = {
+static struct platform_driver scx200_pci_driver = {
.driver = {
.name = "cs5535-smb",
.owner = THIS_MODULE,
@@ -593,14 +593,14 @@ static int __init scx200_acb_init(void)
return 0;
/* No ISA devices; register the platform driver for PCI-based devices */
- return platform_driver_register(&scx200_pci_drv);
+ return platform_driver_register(&scx200_pci_driver);
}
static void __exit scx200_acb_cleanup(void)
{
struct scx200_acb_iface *iface;
- platform_driver_unregister(&scx200_pci_drv);
+ platform_driver_unregister(&scx200_pci_driver);
mutex_lock(&scx200_acb_list_mutex);
while ((iface = scx200_acb_list) != NULL) {
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 76b6d98bd295..5a26584934ca 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -677,19 +677,19 @@ config BLK_DEV_IDE_PMAC_ATA100FIRST
config BLK_DEV_IDE_AU1XXX
bool "IDE for AMD Alchemy Au1200"
- depends on SOC_AU1200
+ depends on MIPS_ALCHEMY
select IDE_XFER_MODE
choice
prompt "IDE Mode for AMD Alchemy Au1200"
default BLK_DEV_IDE_AU1XXX_PIO_DBDMA
- depends on SOC_AU1200 && BLK_DEV_IDE_AU1XXX
+ depends on BLK_DEV_IDE_AU1XXX
config BLK_DEV_IDE_AU1XXX_PIO_DBDMA
bool "PIO+DbDMA IDE for AMD Alchemy Au1200"
config BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200"
- depends on SOC_AU1200 && BLK_DEV_IDE_AU1XXX
+ depends on BLK_DEV_IDE_AU1XXX
endchoice
config BLK_DEV_IDE_TX4938
diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c
index b26c23416fa7..259786ca8b75 100644
--- a/drivers/ide/au1xxx-ide.c
+++ b/drivers/ide/au1xxx-ide.c
@@ -36,13 +36,17 @@
#include <linux/ide.h>
#include <linux/scatterlist.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-au1x00/au1xxx_ide.h>
#define DRV_NAME "au1200-ide"
#define DRV_AUTHOR "Enrico Walther <enrico.walther@amd.com> / Pete Popov <ppopov@embeddedalley.com>"
+#ifndef IDE_REG_SHIFT
+#define IDE_REG_SHIFT 5
+#endif
+
/* enable the burstmode in the dbdma */
#define IDE_AU1XXX_BURSTMODE 1
@@ -317,10 +321,11 @@ static void auide_ddma_rx_callback(int irq, void *param)
}
#endif /* end CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */
-static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, u32 devwidth, u32 flags)
+static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize,
+ u32 devwidth, u32 flags, u32 regbase)
{
dev->dev_id = dev_id;
- dev->dev_physaddr = (u32)IDE_PHYS_ADDR;
+ dev->dev_physaddr = CPHYSADDR(regbase);
dev->dev_intlevel = 0;
dev->dev_intpolarity = 0;
dev->dev_tsize = tsize;
@@ -344,7 +349,7 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
dbdev_tab_t source_dev_tab, target_dev_tab;
u32 dev_id, tsize, devwidth, flags;
- dev_id = IDE_DDMA_REQ;
+ dev_id = hwif->ddma_id;
tsize = 8; /* 1 */
devwidth = 32; /* 16 */
@@ -356,20 +361,17 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
#endif
/* setup dev_tab for tx channel */
- auide_init_dbdma_dev( &source_dev_tab,
- dev_id,
- tsize, devwidth, DEV_FLAGS_OUT | flags);
+ auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth,
+ DEV_FLAGS_OUT | flags, auide->regbase);
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
- auide_init_dbdma_dev( &source_dev_tab,
- dev_id,
- tsize, devwidth, DEV_FLAGS_IN | flags);
+ auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth,
+ DEV_FLAGS_IN | flags, auide->regbase);
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
/* We also need to add a target device for the DMA */
- auide_init_dbdma_dev( &target_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- tsize, devwidth, DEV_FLAGS_ANYUSE);
+ auide_init_dbdma_dev(&target_dev_tab, (u32)DSCR_CMD0_ALWAYS, tsize,
+ devwidth, DEV_FLAGS_ANYUSE, auide->regbase);
auide->target_dev_id = au1xxx_ddma_add_device(&target_dev_tab);
/* Get a channel for TX */
@@ -411,14 +413,12 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
#endif
/* setup dev_tab for tx channel */
- auide_init_dbdma_dev( &source_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- 8, 32, DEV_FLAGS_OUT | flags);
+ auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32,
+ DEV_FLAGS_OUT | flags, auide->regbase);
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
- auide_init_dbdma_dev( &source_dev_tab,
- (u32)DSCR_CMD0_ALWAYS,
- 8, 32, DEV_FLAGS_IN | flags);
+ auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32,
+ DEV_FLAGS_IN | flags, auide->regbase);
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
/* Get a channel for TX */
@@ -540,6 +540,14 @@ static int au_ide_probe(struct platform_device *dev)
goto out;
}
+ res = platform_get_resource(dev, IORESOURCE_DMA, 0);
+ if (!res) {
+ pr_debug("%s: no DDMA ID resource\n", DRV_NAME);
+ ret = -ENODEV;
+ goto out;
+ }
+ ahwif->ddma_id = res->start;
+
memset(&hw, 0, sizeof(hw));
auide_setup_ports(&hw, ahwif);
hw.irq = ahwif->irq;
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index fc0f2bd9ca82..4104ea2427c2 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -889,6 +889,8 @@ retest:
break;
case IB_CM_ESTABLISHED:
spin_unlock_irq(&cm_id_priv->lock);
+ if (cm_id_priv->qp_type == IB_QPT_XRC_TGT)
+ break;
ib_send_cm_dreq(cm_id, NULL, 0);
goto retest;
case IB_CM_DREQ_SENT:
@@ -1008,7 +1010,6 @@ static void cm_format_req(struct cm_req_msg *req_msg,
req_msg->service_id = param->service_id;
req_msg->local_ca_guid = cm_id_priv->id.device->node_guid;
cm_req_set_local_qpn(req_msg, cpu_to_be32(param->qp_num));
- cm_req_set_resp_res(req_msg, param->responder_resources);
cm_req_set_init_depth(req_msg, param->initiator_depth);
cm_req_set_remote_resp_timeout(req_msg,
param->remote_cm_response_timeout);
@@ -1017,12 +1018,16 @@ static void cm_format_req(struct cm_req_msg *req_msg,
cm_req_set_starting_psn(req_msg, cpu_to_be32(param->starting_psn));
cm_req_set_local_resp_timeout(req_msg,
param->local_cm_response_timeout);
- cm_req_set_retry_count(req_msg, param->retry_count);
req_msg->pkey = param->primary_path->pkey;
cm_req_set_path_mtu(req_msg, param->primary_path->mtu);
- cm_req_set_rnr_retry_count(req_msg, param->rnr_retry_count);
cm_req_set_max_cm_retries(req_msg, param->max_cm_retries);
- cm_req_set_srq(req_msg, param->srq);
+
+ if (param->qp_type != IB_QPT_XRC_INI) {
+ cm_req_set_resp_res(req_msg, param->responder_resources);
+ cm_req_set_retry_count(req_msg, param->retry_count);
+ cm_req_set_rnr_retry_count(req_msg, param->rnr_retry_count);
+ cm_req_set_srq(req_msg, param->srq);
+ }
if (pri_path->hop_limit <= 1) {
req_msg->primary_local_lid = pri_path->slid;
@@ -1080,7 +1085,8 @@ static int cm_validate_req_param(struct ib_cm_req_param *param)
if (!param->primary_path)
return -EINVAL;
- if (param->qp_type != IB_QPT_RC && param->qp_type != IB_QPT_UC)
+ if (param->qp_type != IB_QPT_RC && param->qp_type != IB_QPT_UC &&
+ param->qp_type != IB_QPT_XRC_INI)
return -EINVAL;
if (param->private_data &&
@@ -1601,18 +1607,24 @@ static void cm_format_rep(struct cm_rep_msg *rep_msg,
cm_format_mad_hdr(&rep_msg->hdr, CM_REP_ATTR_ID, cm_id_priv->tid);
rep_msg->local_comm_id = cm_id_priv->id.local_id;
rep_msg->remote_comm_id = cm_id_priv->id.remote_id;
- cm_rep_set_local_qpn(rep_msg, cpu_to_be32(param->qp_num));
cm_rep_set_starting_psn(rep_msg, cpu_to_be32(param->starting_psn));
rep_msg->resp_resources = param->responder_resources;
- rep_msg->initiator_depth = param->initiator_depth;
cm_rep_set_target_ack_delay(rep_msg,
cm_id_priv->av.port->cm_dev->ack_delay);
cm_rep_set_failover(rep_msg, param->failover_accepted);
- cm_rep_set_flow_ctrl(rep_msg, param->flow_control);
cm_rep_set_rnr_retry_count(rep_msg, param->rnr_retry_count);
- cm_rep_set_srq(rep_msg, param->srq);
rep_msg->local_ca_guid = cm_id_priv->id.device->node_guid;
+ if (cm_id_priv->qp_type != IB_QPT_XRC_TGT) {
+ rep_msg->initiator_depth = param->initiator_depth;
+ cm_rep_set_flow_ctrl(rep_msg, param->flow_control);
+ cm_rep_set_srq(rep_msg, param->srq);
+ cm_rep_set_local_qpn(rep_msg, cpu_to_be32(param->qp_num));
+ } else {
+ cm_rep_set_srq(rep_msg, 1);
+ cm_rep_set_local_eecn(rep_msg, cpu_to_be32(param->qp_num));
+ }
+
if (param->private_data && param->private_data_len)
memcpy(rep_msg->private_data, param->private_data,
param->private_data_len);
@@ -1660,7 +1672,7 @@ int ib_send_cm_rep(struct ib_cm_id *cm_id,
cm_id_priv->initiator_depth = param->initiator_depth;
cm_id_priv->responder_resources = param->responder_resources;
cm_id_priv->rq_psn = cm_rep_get_starting_psn(rep_msg);
- cm_id_priv->local_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->local_qpn = cpu_to_be32(param->qp_num & 0xFFFFFF);
out: spin_unlock_irqrestore(&cm_id_priv->lock, flags);
return ret;
@@ -1731,7 +1743,7 @@ error: spin_unlock_irqrestore(&cm_id_priv->lock, flags);
}
EXPORT_SYMBOL(ib_send_cm_rtu);
-static void cm_format_rep_event(struct cm_work *work)
+static void cm_format_rep_event(struct cm_work *work, enum ib_qp_type qp_type)
{
struct cm_rep_msg *rep_msg;
struct ib_cm_rep_event_param *param;
@@ -1740,7 +1752,7 @@ static void cm_format_rep_event(struct cm_work *work)
param = &work->cm_event.param.rep_rcvd;
param->remote_ca_guid = rep_msg->local_ca_guid;
param->remote_qkey = be32_to_cpu(rep_msg->local_qkey);
- param->remote_qpn = be32_to_cpu(cm_rep_get_local_qpn(rep_msg));
+ param->remote_qpn = be32_to_cpu(cm_rep_get_qpn(rep_msg, qp_type));
param->starting_psn = be32_to_cpu(cm_rep_get_starting_psn(rep_msg));
param->responder_resources = rep_msg->initiator_depth;
param->initiator_depth = rep_msg->resp_resources;
@@ -1808,7 +1820,7 @@ static int cm_rep_handler(struct cm_work *work)
return -EINVAL;
}
- cm_format_rep_event(work);
+ cm_format_rep_event(work, cm_id_priv->qp_type);
spin_lock_irq(&cm_id_priv->lock);
switch (cm_id_priv->id.state) {
@@ -1823,7 +1835,7 @@ static int cm_rep_handler(struct cm_work *work)
cm_id_priv->timewait_info->work.remote_id = rep_msg->local_comm_id;
cm_id_priv->timewait_info->remote_ca_guid = rep_msg->local_ca_guid;
- cm_id_priv->timewait_info->remote_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->timewait_info->remote_qpn = cm_rep_get_qpn(rep_msg, cm_id_priv->qp_type);
spin_lock(&cm.lock);
/* Check for duplicate REP. */
@@ -1850,7 +1862,7 @@ static int cm_rep_handler(struct cm_work *work)
cm_id_priv->id.state = IB_CM_REP_RCVD;
cm_id_priv->id.remote_id = rep_msg->local_comm_id;
- cm_id_priv->remote_qpn = cm_rep_get_local_qpn(rep_msg);
+ cm_id_priv->remote_qpn = cm_rep_get_qpn(rep_msg, cm_id_priv->qp_type);
cm_id_priv->initiator_depth = rep_msg->resp_resources;
cm_id_priv->responder_resources = rep_msg->initiator_depth;
cm_id_priv->sq_psn = cm_rep_get_starting_psn(rep_msg);
@@ -3492,7 +3504,8 @@ static int cm_init_qp_rtr_attr(struct cm_id_private *cm_id_priv,
qp_attr->path_mtu = cm_id_priv->path_mtu;
qp_attr->dest_qp_num = be32_to_cpu(cm_id_priv->remote_qpn);
qp_attr->rq_psn = be32_to_cpu(cm_id_priv->rq_psn);
- if (cm_id_priv->qp_type == IB_QPT_RC) {
+ if (cm_id_priv->qp_type == IB_QPT_RC ||
+ cm_id_priv->qp_type == IB_QPT_XRC_TGT) {
*qp_attr_mask |= IB_QP_MAX_DEST_RD_ATOMIC |
IB_QP_MIN_RNR_TIMER;
qp_attr->max_dest_rd_atomic =
@@ -3537,15 +3550,21 @@ static int cm_init_qp_rts_attr(struct cm_id_private *cm_id_priv,
if (cm_id_priv->id.lap_state == IB_CM_LAP_UNINIT) {
*qp_attr_mask = IB_QP_STATE | IB_QP_SQ_PSN;
qp_attr->sq_psn = be32_to_cpu(cm_id_priv->sq_psn);
- if (cm_id_priv->qp_type == IB_QPT_RC) {
- *qp_attr_mask |= IB_QP_TIMEOUT | IB_QP_RETRY_CNT |
- IB_QP_RNR_RETRY |
+ switch (cm_id_priv->qp_type) {
+ case IB_QPT_RC:
+ case IB_QPT_XRC_INI:
+ *qp_attr_mask |= IB_QP_RETRY_CNT | IB_QP_RNR_RETRY |
IB_QP_MAX_QP_RD_ATOMIC;
- qp_attr->timeout = cm_id_priv->av.timeout;
qp_attr->retry_cnt = cm_id_priv->retry_count;
qp_attr->rnr_retry = cm_id_priv->rnr_retry_count;
- qp_attr->max_rd_atomic =
- cm_id_priv->initiator_depth;
+ qp_attr->max_rd_atomic = cm_id_priv->initiator_depth;
+ /* fall through */
+ case IB_QPT_XRC_TGT:
+ *qp_attr_mask |= IB_QP_TIMEOUT;
+ qp_attr->timeout = cm_id_priv->av.timeout;
+ break;
+ default:
+ break;
}
if (cm_id_priv->alt_av.ah_attr.dlid) {
*qp_attr_mask |= IB_QP_PATH_MIG_STATE;
diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h
index 7e63c08f697c..505db2a59e7f 100644
--- a/drivers/infiniband/core/cm_msgs.h
+++ b/drivers/infiniband/core/cm_msgs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Intel Corporation. All rights reserved.
+ * Copyright (c) 2004, 2011 Intel Corporation. All rights reserved.
* Copyright (c) 2004 Topspin Corporation. All rights reserved.
* Copyright (c) 2004 Voltaire Corporation. All rights reserved.
*
@@ -86,7 +86,7 @@ struct cm_req_msg {
__be16 pkey;
/* path MTU:4, RDC exists:1, RNR retry count:3. */
u8 offset50;
- /* max CM Retries:4, SRQ:1, rsvd:3 */
+ /* max CM Retries:4, SRQ:1, extended transport type:3 */
u8 offset51;
__be16 primary_local_lid;
@@ -175,6 +175,11 @@ static inline enum ib_qp_type cm_req_get_qp_type(struct cm_req_msg *req_msg)
switch(transport_type) {
case 0: return IB_QPT_RC;
case 1: return IB_QPT_UC;
+ case 3:
+ switch (req_msg->offset51 & 0x7) {
+ case 1: return IB_QPT_XRC_TGT;
+ default: return 0;
+ }
default: return 0;
}
}
@@ -188,6 +193,12 @@ static inline void cm_req_set_qp_type(struct cm_req_msg *req_msg,
req_msg->offset40) &
0xFFFFFFF9) | 0x2);
break;
+ case IB_QPT_XRC_INI:
+ req_msg->offset40 = cpu_to_be32((be32_to_cpu(
+ req_msg->offset40) &
+ 0xFFFFFFF9) | 0x6);
+ req_msg->offset51 = (req_msg->offset51 & 0xF8) | 1;
+ break;
default:
req_msg->offset40 = cpu_to_be32(be32_to_cpu(
req_msg->offset40) &
@@ -527,6 +538,23 @@ static inline void cm_rep_set_local_qpn(struct cm_rep_msg *rep_msg, __be32 qpn)
(be32_to_cpu(rep_msg->offset12) & 0x000000FF));
}
+static inline __be32 cm_rep_get_local_eecn(struct cm_rep_msg *rep_msg)
+{
+ return cpu_to_be32(be32_to_cpu(rep_msg->offset16) >> 8);
+}
+
+static inline void cm_rep_set_local_eecn(struct cm_rep_msg *rep_msg, __be32 eecn)
+{
+ rep_msg->offset16 = cpu_to_be32((be32_to_cpu(eecn) << 8) |
+ (be32_to_cpu(rep_msg->offset16) & 0x000000FF));
+}
+
+static inline __be32 cm_rep_get_qpn(struct cm_rep_msg *rep_msg, enum ib_qp_type qp_type)
+{
+ return (qp_type == IB_QPT_XRC_INI) ?
+ cm_rep_get_local_eecn(rep_msg) : cm_rep_get_local_qpn(rep_msg);
+}
+
static inline __be32 cm_rep_get_starting_psn(struct cm_rep_msg *rep_msg)
{
return cpu_to_be32(be32_to_cpu(rep_msg->offset20) >> 8);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index ca4c5dcd7133..872b1842598a 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -81,6 +81,7 @@ static DEFINE_IDR(sdp_ps);
static DEFINE_IDR(tcp_ps);
static DEFINE_IDR(udp_ps);
static DEFINE_IDR(ipoib_ps);
+static DEFINE_IDR(ib_ps);
struct cma_device {
struct list_head list;
@@ -1179,6 +1180,15 @@ static void cma_set_req_event_data(struct rdma_cm_event *event,
event->param.conn.qp_num = req_data->remote_qpn;
}
+static int cma_check_req_qp_type(struct rdma_cm_id *id, struct ib_cm_event *ib_event)
+{
+ return (((ib_event->event == IB_CM_REQ_RECEIVED) ||
+ (ib_event->param.req_rcvd.qp_type == id->qp_type)) ||
+ ((ib_event->event == IB_CM_SIDR_REQ_RECEIVED) &&
+ (id->qp_type == IB_QPT_UD)) ||
+ (!id->qp_type));
+}
+
static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
{
struct rdma_id_private *listen_id, *conn_id;
@@ -1186,13 +1196,16 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
int offset, ret;
listen_id = cm_id->context;
+ if (!cma_check_req_qp_type(&listen_id->id, ib_event))
+ return -EINVAL;
+
if (cma_disable_callback(listen_id, RDMA_CM_LISTEN))
return -ECONNABORTED;
memset(&event, 0, sizeof event);
offset = cma_user_data_offset(listen_id->id.ps);
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
- if (listen_id->id.qp_type == IB_QPT_UD) {
+ if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED) {
conn_id = cma_new_udp_id(&listen_id->id, ib_event);
event.param.ud.private_data = ib_event->private_data + offset;
event.param.ud.private_data_len =
@@ -1328,6 +1341,8 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
switch (iw_event->status) {
case 0:
event.event = RDMA_CM_EVENT_ESTABLISHED;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
break;
case -ECONNRESET:
case -ECONNREFUSED:
@@ -1343,6 +1358,8 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
break;
case IW_CM_EVENT_ESTABLISHED:
event.event = RDMA_CM_EVENT_ESTABLISHED;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
break;
default:
BUG_ON(1);
@@ -1433,8 +1450,8 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
event.param.conn.private_data = iw_event->private_data;
event.param.conn.private_data_len = iw_event->private_data_len;
- event.param.conn.initiator_depth = attr.max_qp_init_rd_atom;
- event.param.conn.responder_resources = attr.max_qp_rd_atom;
+ event.param.conn.initiator_depth = iw_event->ird;
+ event.param.conn.responder_resources = iw_event->ord;
/*
* Protect against the user destroying conn_id from another thread
@@ -2234,6 +2251,9 @@ static int cma_get_port(struct rdma_id_private *id_priv)
case RDMA_PS_IPOIB:
ps = &ipoib_ps;
break;
+ case RDMA_PS_IB:
+ ps = &ib_ps;
+ break;
default:
return -EPROTONOSUPPORT;
}
@@ -2569,7 +2589,7 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
req.service_id = cma_get_service_id(id_priv->id.ps,
(struct sockaddr *) &route->addr.dst_addr);
req.qp_num = id_priv->qp_num;
- req.qp_type = IB_QPT_RC;
+ req.qp_type = id_priv->id.qp_type;
req.starting_psn = id_priv->seq_num;
req.responder_resources = conn_param->responder_resources;
req.initiator_depth = conn_param->initiator_depth;
@@ -2616,14 +2636,16 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
if (ret)
goto out;
- iw_param.ord = conn_param->initiator_depth;
- iw_param.ird = conn_param->responder_resources;
- iw_param.private_data = conn_param->private_data;
- iw_param.private_data_len = conn_param->private_data_len;
- if (id_priv->id.qp)
+ if (conn_param) {
+ iw_param.ord = conn_param->initiator_depth;
+ iw_param.ird = conn_param->responder_resources;
+ iw_param.private_data = conn_param->private_data;
+ iw_param.private_data_len = conn_param->private_data_len;
+ iw_param.qpn = id_priv->id.qp ? id_priv->qp_num : conn_param->qp_num;
+ } else {
+ memset(&iw_param, 0, sizeof iw_param);
iw_param.qpn = id_priv->qp_num;
- else
- iw_param.qpn = conn_param->qp_num;
+ }
ret = iw_cm_connect(cm_id, &iw_param);
out:
if (ret) {
@@ -2765,14 +2787,20 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (id->qp_type == IB_QPT_UD)
- ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
- conn_param->private_data,
- conn_param->private_data_len);
- else if (conn_param)
- ret = cma_accept_ib(id_priv, conn_param);
- else
- ret = cma_rep_recv(id_priv);
+ if (id->qp_type == IB_QPT_UD) {
+ if (conn_param)
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
+ conn_param->private_data,
+ conn_param->private_data_len);
+ else
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
+ NULL, 0);
+ } else {
+ if (conn_param)
+ ret = cma_accept_ib(id_priv, conn_param);
+ else
+ ret = cma_rep_recv(id_priv);
+ }
break;
case RDMA_TRANSPORT_IWARP:
ret = cma_accept_iw(id_priv, conn_param);
@@ -3460,6 +3488,7 @@ static void __exit cma_cleanup(void)
idr_destroy(&tcp_ps);
idr_destroy(&udp_ps);
idr_destroy(&ipoib_ps);
+ idr_destroy(&ib_ps);
}
module_init(cma_init);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index b4d8672a3e4e..056389229ea7 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1596,6 +1596,9 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
mad->mad_hdr.class_version].class;
if (!class)
goto out;
+ if (convert_mgmt_class(mad->mad_hdr.mgmt_class) >=
+ IB_MGMT_MAX_METHODS)
+ goto out;
method = class->method_table[convert_mgmt_class(
mad->mad_hdr.mgmt_class)];
if (method)
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 9ab5df72df7b..2b59b72b57f9 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -185,17 +185,35 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused,
if (ret)
return ret;
+ rate = (25 * attr.active_speed) / 10;
+
switch (attr.active_speed) {
- case 2: speed = " DDR"; break;
- case 4: speed = " QDR"; break;
+ case 2:
+ speed = " DDR";
+ break;
+ case 4:
+ speed = " QDR";
+ break;
+ case 8:
+ speed = " FDR10";
+ rate = 10;
+ break;
+ case 16:
+ speed = " FDR";
+ rate = 14;
+ break;
+ case 32:
+ speed = " EDR";
+ rate = 25;
+ break;
}
- rate = 25 * ib_width_enum_to_int(attr.active_width) * attr.active_speed;
+ rate *= ib_width_enum_to_int(attr.active_width);
if (rate < 0)
return -EINVAL;
return sprintf(buf, "%d%s Gb/sec (%dX%s)\n",
- rate / 10, rate % 10 ? ".5" : "",
+ rate, (attr.active_speed == 1) ? ".5" : "",
ib_width_enum_to_int(attr.active_width), speed);
}
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index 08f948df8fa9..b8a0b4a7811b 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -1122,7 +1122,7 @@ static ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
if (copy_from_user(&hdr, buf, sizeof(hdr)))
return -EFAULT;
- if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
+ if (hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
return -EINVAL;
if (hdr.in + sizeof(hdr) > len)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 71be5eebd683..b69307f4f6d0 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -276,7 +276,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
ucma_set_event_context(ctx, event, uevent);
uevent->resp.event = event->event;
uevent->resp.status = event->status;
- if (cm_id->ps == RDMA_PS_UDP || cm_id->ps == RDMA_PS_IPOIB)
+ if (cm_id->qp_type == IB_QPT_UD)
ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud);
else
ucma_copy_conn_event(&uevent->resp.param.conn,
@@ -377,6 +377,9 @@ static int ucma_get_qp_type(struct rdma_ucm_create_id *cmd, enum ib_qp_type *qp_
case RDMA_PS_IPOIB:
*qp_type = IB_QPT_UD;
return 0;
+ case RDMA_PS_IB:
+ *qp_type = cmd->qp_type;
+ return 0;
default:
return -EINVAL;
}
@@ -1270,7 +1273,7 @@ static ssize_t ucma_write(struct file *filp, const char __user *buf,
if (copy_from_user(&hdr, buf, sizeof(hdr)))
return -EFAULT;
- if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
+ if (hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
return -EINVAL;
if (hdr.in + sizeof(hdr) > len)
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index b645e558876f..9155f91d66bf 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -136,7 +136,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
down_write(&current->mm->mmap_sem);
- locked = npages + current->mm->locked_vm;
+ locked = npages + current->mm->pinned_vm;
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) {
@@ -206,7 +206,7 @@ out:
__ib_umem_release(context->device, umem, 0);
kfree(umem);
} else
- current->mm->locked_vm = locked;
+ current->mm->pinned_vm = locked;
up_write(&current->mm->mmap_sem);
if (vma_list)
@@ -222,7 +222,7 @@ static void ib_umem_account(struct work_struct *work)
struct ib_umem *umem = container_of(work, struct ib_umem, work);
down_write(&umem->mm->mmap_sem);
- umem->mm->locked_vm -= umem->diff;
+ umem->mm->pinned_vm -= umem->diff;
up_write(&umem->mm->mmap_sem);
mmput(umem->mm);
kfree(umem);
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 8d261b6ea5fe..07db22997e97 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -458,8 +458,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
goto err;
}
- if (packet->mad.hdr.id < 0 ||
- packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
+ if (packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
ret = -EINVAL;
goto err;
}
@@ -703,7 +702,7 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
mutex_lock(&file->port->file_mutex);
mutex_lock(&file->mutex);
- if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
+ if (id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index a078e5624d22..5bcb2afd3dcb 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -76,6 +76,8 @@ struct ib_uverbs_device {
struct ib_device *ib_dev;
int devnum;
struct cdev cdev;
+ struct rb_root xrcd_tree;
+ struct mutex xrcd_tree_mutex;
};
struct ib_uverbs_event_file {
@@ -120,6 +122,16 @@ struct ib_uevent_object {
u32 events_reported;
};
+struct ib_uxrcd_object {
+ struct ib_uobject uobject;
+ atomic_t refcnt;
+};
+
+struct ib_usrq_object {
+ struct ib_uevent_object uevent;
+ struct ib_uxrcd_object *uxrcd;
+};
+
struct ib_uqp_object {
struct ib_uevent_object uevent;
struct list_head mcast_list;
@@ -142,6 +154,7 @@ extern struct idr ib_uverbs_ah_idr;
extern struct idr ib_uverbs_cq_idr;
extern struct idr ib_uverbs_qp_idr;
extern struct idr ib_uverbs_srq_idr;
+extern struct idr ib_uverbs_xrcd_idr;
void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj);
@@ -161,6 +174,7 @@ void ib_uverbs_qp_event_handler(struct ib_event *event, void *context_ptr);
void ib_uverbs_srq_event_handler(struct ib_event *event, void *context_ptr);
void ib_uverbs_event_handler(struct ib_event_handler *handler,
struct ib_event *event);
+void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, struct ib_xrcd *xrcd);
#define IB_UVERBS_DECLARE_CMD(name) \
ssize_t ib_uverbs_##name(struct ib_uverbs_file *file, \
@@ -181,6 +195,7 @@ IB_UVERBS_DECLARE_CMD(poll_cq);
IB_UVERBS_DECLARE_CMD(req_notify_cq);
IB_UVERBS_DECLARE_CMD(destroy_cq);
IB_UVERBS_DECLARE_CMD(create_qp);
+IB_UVERBS_DECLARE_CMD(open_qp);
IB_UVERBS_DECLARE_CMD(query_qp);
IB_UVERBS_DECLARE_CMD(modify_qp);
IB_UVERBS_DECLARE_CMD(destroy_qp);
@@ -195,5 +210,8 @@ IB_UVERBS_DECLARE_CMD(create_srq);
IB_UVERBS_DECLARE_CMD(modify_srq);
IB_UVERBS_DECLARE_CMD(query_srq);
IB_UVERBS_DECLARE_CMD(destroy_srq);
+IB_UVERBS_DECLARE_CMD(create_xsrq);
+IB_UVERBS_DECLARE_CMD(open_xrcd);
+IB_UVERBS_DECLARE_CMD(close_xrcd);
#endif /* UVERBS_H */
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index c42699285f8e..254f1649c734 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -47,6 +47,7 @@ static struct lock_class_key cq_lock_key;
static struct lock_class_key qp_lock_key;
static struct lock_class_key ah_lock_key;
static struct lock_class_key srq_lock_key;
+static struct lock_class_key xrcd_lock_key;
#define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \
do { \
@@ -255,6 +256,18 @@ static void put_srq_read(struct ib_srq *srq)
put_uobj_read(srq->uobject);
}
+static struct ib_xrcd *idr_read_xrcd(int xrcd_handle, struct ib_ucontext *context,
+ struct ib_uobject **uobj)
+{
+ *uobj = idr_read_uobj(&ib_uverbs_xrcd_idr, xrcd_handle, context, 0);
+ return *uobj ? (*uobj)->object : NULL;
+}
+
+static void put_xrcd_read(struct ib_uobject *uobj)
+{
+ put_uobj_read(uobj);
+}
+
ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
const char __user *buf,
int in_len, int out_len)
@@ -298,6 +311,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
INIT_LIST_HEAD(&ucontext->qp_list);
INIT_LIST_HEAD(&ucontext->srq_list);
INIT_LIST_HEAD(&ucontext->ah_list);
+ INIT_LIST_HEAD(&ucontext->xrcd_list);
ucontext->closing = 0;
resp.num_comp_vectors = file->device->num_comp_vectors;
@@ -579,6 +593,310 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
return in_len;
}
+struct xrcd_table_entry {
+ struct rb_node node;
+ struct ib_xrcd *xrcd;
+ struct inode *inode;
+};
+
+static int xrcd_table_insert(struct ib_uverbs_device *dev,
+ struct inode *inode,
+ struct ib_xrcd *xrcd)
+{
+ struct xrcd_table_entry *entry, *scan;
+ struct rb_node **p = &dev->xrcd_tree.rb_node;
+ struct rb_node *parent = NULL;
+
+ entry = kmalloc(sizeof *entry, GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->xrcd = xrcd;
+ entry->inode = inode;
+
+ while (*p) {
+ parent = *p;
+ scan = rb_entry(parent, struct xrcd_table_entry, node);
+
+ if (inode < scan->inode) {
+ p = &(*p)->rb_left;
+ } else if (inode > scan->inode) {
+ p = &(*p)->rb_right;
+ } else {
+ kfree(entry);
+ return -EEXIST;
+ }
+ }
+
+ rb_link_node(&entry->node, parent, p);
+ rb_insert_color(&entry->node, &dev->xrcd_tree);
+ igrab(inode);
+ return 0;
+}
+
+static struct xrcd_table_entry *xrcd_table_search(struct ib_uverbs_device *dev,
+ struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+ struct rb_node *p = dev->xrcd_tree.rb_node;
+
+ while (p) {
+ entry = rb_entry(p, struct xrcd_table_entry, node);
+
+ if (inode < entry->inode)
+ p = p->rb_left;
+ else if (inode > entry->inode)
+ p = p->rb_right;
+ else
+ return entry;
+ }
+
+ return NULL;
+}
+
+static struct ib_xrcd *find_xrcd(struct ib_uverbs_device *dev, struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+
+ entry = xrcd_table_search(dev, inode);
+ if (!entry)
+ return NULL;
+
+ return entry->xrcd;
+}
+
+static void xrcd_table_delete(struct ib_uverbs_device *dev,
+ struct inode *inode)
+{
+ struct xrcd_table_entry *entry;
+
+ entry = xrcd_table_search(dev, inode);
+ if (entry) {
+ iput(inode);
+ rb_erase(&entry->node, &dev->xrcd_tree);
+ kfree(entry);
+ }
+}
+
+ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_open_xrcd cmd;
+ struct ib_uverbs_open_xrcd_resp resp;
+ struct ib_udata udata;
+ struct ib_uxrcd_object *obj;
+ struct ib_xrcd *xrcd = NULL;
+ struct file *f = NULL;
+ struct inode *inode = NULL;
+ int ret = 0;
+ int new_xrcd = 0;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ mutex_lock(&file->device->xrcd_tree_mutex);
+
+ if (cmd.fd != -1) {
+ /* search for file descriptor */
+ f = fget(cmd.fd);
+ if (!f) {
+ ret = -EBADF;
+ goto err_tree_mutex_unlock;
+ }
+
+ inode = f->f_dentry->d_inode;
+ if (!inode) {
+ ret = -EBADF;
+ goto err_tree_mutex_unlock;
+ }
+
+ xrcd = find_xrcd(file->device, inode);
+ if (!xrcd && !(cmd.oflags & O_CREAT)) {
+ /* no file descriptor. Need CREATE flag */
+ ret = -EAGAIN;
+ goto err_tree_mutex_unlock;
+ }
+
+ if (xrcd && cmd.oflags & O_EXCL) {
+ ret = -EINVAL;
+ goto err_tree_mutex_unlock;
+ }
+ }
+
+ obj = kmalloc(sizeof *obj, GFP_KERNEL);
+ if (!obj) {
+ ret = -ENOMEM;
+ goto err_tree_mutex_unlock;
+ }
+
+ init_uobj(&obj->uobject, 0, file->ucontext, &xrcd_lock_key);
+
+ down_write(&obj->uobject.mutex);
+
+ if (!xrcd) {
+ xrcd = file->device->ib_dev->alloc_xrcd(file->device->ib_dev,
+ file->ucontext, &udata);
+ if (IS_ERR(xrcd)) {
+ ret = PTR_ERR(xrcd);
+ goto err;
+ }
+
+ xrcd->inode = inode;
+ xrcd->device = file->device->ib_dev;
+ atomic_set(&xrcd->usecnt, 0);
+ mutex_init(&xrcd->tgt_qp_mutex);
+ INIT_LIST_HEAD(&xrcd->tgt_qp_list);
+ new_xrcd = 1;
+ }
+
+ atomic_set(&obj->refcnt, 0);
+ obj->uobject.object = xrcd;
+ ret = idr_add_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+ if (ret)
+ goto err_idr;
+
+ memset(&resp, 0, sizeof resp);
+ resp.xrcd_handle = obj->uobject.id;
+
+ if (inode) {
+ if (new_xrcd) {
+ /* create new inode/xrcd table entry */
+ ret = xrcd_table_insert(file->device, inode, xrcd);
+ if (ret)
+ goto err_insert_xrcd;
+ }
+ atomic_inc(&xrcd->usecnt);
+ }
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof resp)) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ if (f)
+ fput(f);
+
+ mutex_lock(&file->mutex);
+ list_add_tail(&obj->uobject.list, &file->ucontext->xrcd_list);
+ mutex_unlock(&file->mutex);
+
+ obj->uobject.live = 1;
+ up_write(&obj->uobject.mutex);
+
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+ return in_len;
+
+err_copy:
+ if (inode) {
+ if (new_xrcd)
+ xrcd_table_delete(file->device, inode);
+ atomic_dec(&xrcd->usecnt);
+ }
+
+err_insert_xrcd:
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+
+err_idr:
+ ib_dealloc_xrcd(xrcd);
+
+err:
+ put_uobj_write(&obj->uobject);
+
+err_tree_mutex_unlock:
+ if (f)
+ fput(f);
+
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+
+ return ret;
+}
+
+ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_close_xrcd cmd;
+ struct ib_uobject *uobj;
+ struct ib_xrcd *xrcd = NULL;
+ struct inode *inode = NULL;
+ struct ib_uxrcd_object *obj;
+ int live;
+ int ret = 0;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ mutex_lock(&file->device->xrcd_tree_mutex);
+ uobj = idr_write_uobj(&ib_uverbs_xrcd_idr, cmd.xrcd_handle, file->ucontext);
+ if (!uobj) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ xrcd = uobj->object;
+ inode = xrcd->inode;
+ obj = container_of(uobj, struct ib_uxrcd_object, uobject);
+ if (atomic_read(&obj->refcnt)) {
+ put_uobj_write(uobj);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!inode || atomic_dec_and_test(&xrcd->usecnt)) {
+ ret = ib_dealloc_xrcd(uobj->object);
+ if (!ret)
+ uobj->live = 0;
+ }
+
+ live = uobj->live;
+ if (inode && ret)
+ atomic_inc(&xrcd->usecnt);
+
+ put_uobj_write(uobj);
+
+ if (ret)
+ goto out;
+
+ if (inode && !live)
+ xrcd_table_delete(file->device, inode);
+
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ mutex_lock(&file->mutex);
+ list_del(&uobj->list);
+ mutex_unlock(&file->mutex);
+
+ put_uobj(uobj);
+ ret = in_len;
+
+out:
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+ return ret;
+}
+
+void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev,
+ struct ib_xrcd *xrcd)
+{
+ struct inode *inode;
+
+ inode = xrcd->inode;
+ if (inode && !atomic_dec_and_test(&xrcd->usecnt))
+ return;
+
+ ib_dealloc_xrcd(xrcd);
+
+ if (inode)
+ xrcd_table_delete(dev, inode);
+}
+
ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1052,9 +1370,12 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
struct ib_uverbs_create_qp_resp resp;
struct ib_udata udata;
struct ib_uqp_object *obj;
- struct ib_pd *pd;
- struct ib_cq *scq, *rcq;
- struct ib_srq *srq;
+ struct ib_device *device;
+ struct ib_pd *pd = NULL;
+ struct ib_xrcd *xrcd = NULL;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
+ struct ib_cq *scq = NULL, *rcq = NULL;
+ struct ib_srq *srq = NULL;
struct ib_qp *qp;
struct ib_qp_init_attr attr;
int ret;
@@ -1076,15 +1397,39 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key);
down_write(&obj->uevent.uobject.mutex);
- srq = cmd.is_srq ? idr_read_srq(cmd.srq_handle, file->ucontext) : NULL;
- pd = idr_read_pd(cmd.pd_handle, file->ucontext);
- scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0);
- rcq = cmd.recv_cq_handle == cmd.send_cq_handle ?
- scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1);
+ if (cmd.qp_type == IB_QPT_XRC_TGT) {
+ xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj);
+ if (!xrcd) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ device = xrcd->device;
+ } else {
+ pd = idr_read_pd(cmd.pd_handle, file->ucontext);
+ scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0);
+ if (!pd || !scq) {
+ ret = -EINVAL;
+ goto err_put;
+ }
- if (!pd || !scq || !rcq || (cmd.is_srq && !srq)) {
- ret = -EINVAL;
- goto err_put;
+ if (cmd.qp_type == IB_QPT_XRC_INI) {
+ cmd.max_recv_wr = cmd.max_recv_sge = 0;
+ } else {
+ if (cmd.is_srq) {
+ srq = idr_read_srq(cmd.srq_handle, file->ucontext);
+ if (!srq || srq->srq_type != IB_SRQT_BASIC) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ }
+ rcq = (cmd.recv_cq_handle == cmd.send_cq_handle) ?
+ scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1);
+ if (!rcq) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+ }
+ device = pd->device;
}
attr.event_handler = ib_uverbs_qp_event_handler;
@@ -1092,6 +1437,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
attr.send_cq = scq;
attr.recv_cq = rcq;
attr.srq = srq;
+ attr.xrcd = xrcd;
attr.sq_sig_type = cmd.sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;
attr.qp_type = cmd.qp_type;
attr.create_flags = 0;
@@ -1106,26 +1452,34 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
INIT_LIST_HEAD(&obj->uevent.event_list);
INIT_LIST_HEAD(&obj->mcast_list);
- qp = pd->device->create_qp(pd, &attr, &udata);
+ if (cmd.qp_type == IB_QPT_XRC_TGT)
+ qp = ib_create_qp(pd, &attr);
+ else
+ qp = device->create_qp(pd, &attr, &udata);
+
if (IS_ERR(qp)) {
ret = PTR_ERR(qp);
goto err_put;
}
- qp->device = pd->device;
- qp->pd = pd;
- qp->send_cq = attr.send_cq;
- qp->recv_cq = attr.recv_cq;
- qp->srq = attr.srq;
- qp->uobject = &obj->uevent.uobject;
- qp->event_handler = attr.event_handler;
- qp->qp_context = attr.qp_context;
- qp->qp_type = attr.qp_type;
- atomic_inc(&pd->usecnt);
- atomic_inc(&attr.send_cq->usecnt);
- atomic_inc(&attr.recv_cq->usecnt);
- if (attr.srq)
- atomic_inc(&attr.srq->usecnt);
+ if (cmd.qp_type != IB_QPT_XRC_TGT) {
+ qp->real_qp = qp;
+ qp->device = device;
+ qp->pd = pd;
+ qp->send_cq = attr.send_cq;
+ qp->recv_cq = attr.recv_cq;
+ qp->srq = attr.srq;
+ qp->event_handler = attr.event_handler;
+ qp->qp_context = attr.qp_context;
+ qp->qp_type = attr.qp_type;
+ atomic_inc(&pd->usecnt);
+ atomic_inc(&attr.send_cq->usecnt);
+ if (attr.recv_cq)
+ atomic_inc(&attr.recv_cq->usecnt);
+ if (attr.srq)
+ atomic_inc(&attr.srq->usecnt);
+ }
+ qp->uobject = &obj->uevent.uobject;
obj->uevent.uobject.object = qp;
ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
@@ -1147,9 +1501,13 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
goto err_copy;
}
- put_pd_read(pd);
- put_cq_read(scq);
- if (rcq != scq)
+ if (xrcd)
+ put_xrcd_read(xrcd_uobj);
+ if (pd)
+ put_pd_read(pd);
+ if (scq)
+ put_cq_read(scq);
+ if (rcq && rcq != scq)
put_cq_read(rcq);
if (srq)
put_srq_read(srq);
@@ -1171,6 +1529,8 @@ err_destroy:
ib_destroy_qp(qp);
err_put:
+ if (xrcd)
+ put_xrcd_read(xrcd_uobj);
if (pd)
put_pd_read(pd);
if (scq)
@@ -1184,6 +1544,98 @@ err_put:
return ret;
}
+ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len, int out_len)
+{
+ struct ib_uverbs_open_qp cmd;
+ struct ib_uverbs_create_qp_resp resp;
+ struct ib_udata udata;
+ struct ib_uqp_object *obj;
+ struct ib_xrcd *xrcd;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
+ struct ib_qp *qp;
+ struct ib_qp_open_attr attr;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ obj = kmalloc(sizeof *obj, GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key);
+ down_write(&obj->uevent.uobject.mutex);
+
+ xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj);
+ if (!xrcd) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+
+ attr.event_handler = ib_uverbs_qp_event_handler;
+ attr.qp_context = file;
+ attr.qp_num = cmd.qpn;
+ attr.qp_type = cmd.qp_type;
+
+ obj->uevent.events_reported = 0;
+ INIT_LIST_HEAD(&obj->uevent.event_list);
+ INIT_LIST_HEAD(&obj->mcast_list);
+
+ qp = ib_open_qp(xrcd, &attr);
+ if (IS_ERR(qp)) {
+ ret = PTR_ERR(qp);
+ goto err_put;
+ }
+
+ qp->uobject = &obj->uevent.uobject;
+
+ obj->uevent.uobject.object = qp;
+ ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ if (ret)
+ goto err_destroy;
+
+ memset(&resp, 0, sizeof resp);
+ resp.qpn = qp->qp_num;
+ resp.qp_handle = obj->uevent.uobject.id;
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof resp)) {
+ ret = -EFAULT;
+ goto err_remove;
+ }
+
+ put_xrcd_read(xrcd_uobj);
+
+ mutex_lock(&file->mutex);
+ list_add_tail(&obj->uevent.uobject.list, &file->ucontext->qp_list);
+ mutex_unlock(&file->mutex);
+
+ obj->uevent.uobject.live = 1;
+
+ up_write(&obj->uevent.uobject.mutex);
+
+ return in_len;
+
+err_remove:
+ idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+
+err_destroy:
+ ib_destroy_qp(qp);
+
+err_put:
+ put_xrcd_read(xrcd_uobj);
+ put_uobj_write(&obj->uevent.uobject);
+ return ret;
+}
+
ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1284,6 +1736,20 @@ out:
return ret ? ret : in_len;
}
+/* Remove ignored fields set in the attribute mask */
+static int modify_qp_mask(enum ib_qp_type qp_type, int mask)
+{
+ switch (qp_type) {
+ case IB_QPT_XRC_INI:
+ return mask & ~(IB_QP_MAX_DEST_RD_ATOMIC | IB_QP_MIN_RNR_TIMER);
+ case IB_QPT_XRC_TGT:
+ return mask & ~(IB_QP_MAX_QP_RD_ATOMIC | IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY);
+ default:
+ return mask;
+ }
+}
+
ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
@@ -1356,7 +1822,12 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
attr->alt_ah_attr.ah_flags = cmd.alt_dest.is_global ? IB_AH_GRH : 0;
attr->alt_ah_attr.port_num = cmd.alt_dest.port_num;
- ret = qp->device->modify_qp(qp, attr, cmd.attr_mask, &udata);
+ if (qp->real_qp == qp) {
+ ret = qp->device->modify_qp(qp, attr,
+ modify_qp_mask(qp->qp_type, cmd.attr_mask), &udata);
+ } else {
+ ret = ib_modify_qp(qp, attr, modify_qp_mask(qp->qp_type, cmd.attr_mask));
+ }
put_qp_read(qp);
@@ -1553,7 +2024,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
}
resp.bad_wr = 0;
- ret = qp->device->post_send(qp, wr, &bad_wr);
+ ret = qp->device->post_send(qp->real_qp, wr, &bad_wr);
if (ret)
for (next = wr; next; next = next->next) {
++resp.bad_wr;
@@ -1691,7 +2162,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
goto out;
resp.bad_wr = 0;
- ret = qp->device->post_recv(qp, wr, &bad_wr);
+ ret = qp->device->post_recv(qp->real_qp, wr, &bad_wr);
put_qp_read(qp);
@@ -1975,107 +2446,199 @@ out_put:
return ret ? ret : in_len;
}
-ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
- const char __user *buf, int in_len,
- int out_len)
+int __uverbs_create_xsrq(struct ib_uverbs_file *file,
+ struct ib_uverbs_create_xsrq *cmd,
+ struct ib_udata *udata)
{
- struct ib_uverbs_create_srq cmd;
struct ib_uverbs_create_srq_resp resp;
- struct ib_udata udata;
- struct ib_uevent_object *obj;
+ struct ib_usrq_object *obj;
struct ib_pd *pd;
struct ib_srq *srq;
+ struct ib_uobject *uninitialized_var(xrcd_uobj);
struct ib_srq_init_attr attr;
int ret;
- if (out_len < sizeof resp)
- return -ENOSPC;
-
- if (copy_from_user(&cmd, buf, sizeof cmd))
- return -EFAULT;
-
- INIT_UDATA(&udata, buf + sizeof cmd,
- (unsigned long) cmd.response + sizeof resp,
- in_len - sizeof cmd, out_len - sizeof resp);
-
obj = kmalloc(sizeof *obj, GFP_KERNEL);
if (!obj)
return -ENOMEM;
- init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &srq_lock_key);
- down_write(&obj->uobject.mutex);
+ init_uobj(&obj->uevent.uobject, cmd->user_handle, file->ucontext, &srq_lock_key);
+ down_write(&obj->uevent.uobject.mutex);
- pd = idr_read_pd(cmd.pd_handle, file->ucontext);
+ pd = idr_read_pd(cmd->pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err;
}
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ attr.ext.xrc.cq = idr_read_cq(cmd->cq_handle, file->ucontext, 0);
+ if (!attr.ext.xrc.cq) {
+ ret = -EINVAL;
+ goto err_put_pd;
+ }
+
+ attr.ext.xrc.xrcd = idr_read_xrcd(cmd->xrcd_handle, file->ucontext, &xrcd_uobj);
+ if (!attr.ext.xrc.xrcd) {
+ ret = -EINVAL;
+ goto err_put_cq;
+ }
+
+ obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject);
+ atomic_inc(&obj->uxrcd->refcnt);
+ }
+
attr.event_handler = ib_uverbs_srq_event_handler;
attr.srq_context = file;
- attr.attr.max_wr = cmd.max_wr;
- attr.attr.max_sge = cmd.max_sge;
- attr.attr.srq_limit = cmd.srq_limit;
+ attr.srq_type = cmd->srq_type;
+ attr.attr.max_wr = cmd->max_wr;
+ attr.attr.max_sge = cmd->max_sge;
+ attr.attr.srq_limit = cmd->srq_limit;
- obj->events_reported = 0;
- INIT_LIST_HEAD(&obj->event_list);
+ obj->uevent.events_reported = 0;
+ INIT_LIST_HEAD(&obj->uevent.event_list);
- srq = pd->device->create_srq(pd, &attr, &udata);
+ srq = pd->device->create_srq(pd, &attr, udata);
if (IS_ERR(srq)) {
ret = PTR_ERR(srq);
goto err_put;
}
- srq->device = pd->device;
- srq->pd = pd;
- srq->uobject = &obj->uobject;
+ srq->device = pd->device;
+ srq->pd = pd;
+ srq->srq_type = cmd->srq_type;
+ srq->uobject = &obj->uevent.uobject;
srq->event_handler = attr.event_handler;
srq->srq_context = attr.srq_context;
+
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ srq->ext.xrc.cq = attr.ext.xrc.cq;
+ srq->ext.xrc.xrcd = attr.ext.xrc.xrcd;
+ atomic_inc(&attr.ext.xrc.cq->usecnt);
+ atomic_inc(&attr.ext.xrc.xrcd->usecnt);
+ }
+
atomic_inc(&pd->usecnt);
atomic_set(&srq->usecnt, 0);
- obj->uobject.object = srq;
- ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uobject);
+ obj->uevent.uobject.object = srq;
+ ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
if (ret)
goto err_destroy;
memset(&resp, 0, sizeof resp);
- resp.srq_handle = obj->uobject.id;
+ resp.srq_handle = obj->uevent.uobject.id;
resp.max_wr = attr.attr.max_wr;
resp.max_sge = attr.attr.max_sge;
+ if (cmd->srq_type == IB_SRQT_XRC)
+ resp.srqn = srq->ext.xrc.srq_num;
- if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ if (copy_to_user((void __user *) (unsigned long) cmd->response,
&resp, sizeof resp)) {
ret = -EFAULT;
goto err_copy;
}
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ put_uobj_read(xrcd_uobj);
+ put_cq_read(attr.ext.xrc.cq);
+ }
put_pd_read(pd);
mutex_lock(&file->mutex);
- list_add_tail(&obj->uobject.list, &file->ucontext->srq_list);
+ list_add_tail(&obj->uevent.uobject.list, &file->ucontext->srq_list);
mutex_unlock(&file->mutex);
- obj->uobject.live = 1;
+ obj->uevent.uobject.live = 1;
- up_write(&obj->uobject.mutex);
+ up_write(&obj->uevent.uobject.mutex);
- return in_len;
+ return 0;
err_copy:
- idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uobject);
+ idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
err_destroy:
ib_destroy_srq(srq);
err_put:
+ if (cmd->srq_type == IB_SRQT_XRC) {
+ atomic_dec(&obj->uxrcd->refcnt);
+ put_uobj_read(xrcd_uobj);
+ }
+
+err_put_cq:
+ if (cmd->srq_type == IB_SRQT_XRC)
+ put_cq_read(attr.ext.xrc.cq);
+
+err_put_pd:
put_pd_read(pd);
err:
- put_uobj_write(&obj->uobject);
+ put_uobj_write(&obj->uevent.uobject);
return ret;
}
+ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_create_srq cmd;
+ struct ib_uverbs_create_xsrq xcmd;
+ struct ib_uverbs_create_srq_resp resp;
+ struct ib_udata udata;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ xcmd.response = cmd.response;
+ xcmd.user_handle = cmd.user_handle;
+ xcmd.srq_type = IB_SRQT_BASIC;
+ xcmd.pd_handle = cmd.pd_handle;
+ xcmd.max_wr = cmd.max_wr;
+ xcmd.max_sge = cmd.max_sge;
+ xcmd.srq_limit = cmd.srq_limit;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ ret = __uverbs_create_xsrq(file, &xcmd, &udata);
+ if (ret)
+ return ret;
+
+ return in_len;
+}
+
+ssize_t ib_uverbs_create_xsrq(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len, int out_len)
+{
+ struct ib_uverbs_create_xsrq cmd;
+ struct ib_uverbs_create_srq_resp resp;
+ struct ib_udata udata;
+ int ret;
+
+ if (out_len < sizeof resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof cmd))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof cmd,
+ (unsigned long) cmd.response + sizeof resp,
+ in_len - sizeof cmd, out_len - sizeof resp);
+
+ ret = __uverbs_create_xsrq(file, &cmd, &udata);
+ if (ret)
+ return ret;
+
+ return in_len;
+}
+
ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 56898b6578a4..879636746373 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -72,6 +72,7 @@ DEFINE_IDR(ib_uverbs_ah_idr);
DEFINE_IDR(ib_uverbs_cq_idr);
DEFINE_IDR(ib_uverbs_qp_idr);
DEFINE_IDR(ib_uverbs_srq_idr);
+DEFINE_IDR(ib_uverbs_xrcd_idr);
static DEFINE_SPINLOCK(map_lock);
static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES);
@@ -107,6 +108,10 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
[IB_USER_VERBS_CMD_MODIFY_SRQ] = ib_uverbs_modify_srq,
[IB_USER_VERBS_CMD_QUERY_SRQ] = ib_uverbs_query_srq,
[IB_USER_VERBS_CMD_DESTROY_SRQ] = ib_uverbs_destroy_srq,
+ [IB_USER_VERBS_CMD_OPEN_XRCD] = ib_uverbs_open_xrcd,
+ [IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd,
+ [IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq,
+ [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp
};
static void ib_uverbs_add_one(struct ib_device *device);
@@ -202,8 +207,12 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
- ib_uverbs_detach_umcast(qp, uqp);
- ib_destroy_qp(qp);
+ if (qp != qp->real_qp) {
+ ib_close_qp(qp);
+ } else {
+ ib_uverbs_detach_umcast(qp, uqp);
+ ib_destroy_qp(qp);
+ }
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
@@ -241,6 +250,18 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
kfree(uobj);
}
+ mutex_lock(&file->device->xrcd_tree_mutex);
+ list_for_each_entry_safe(uobj, tmp, &context->xrcd_list, list) {
+ struct ib_xrcd *xrcd = uobj->object;
+ struct ib_uxrcd_object *uxrcd =
+ container_of(uobj, struct ib_uxrcd_object, uobject);
+
+ idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ ib_uverbs_dealloc_xrcd(file->device, xrcd);
+ kfree(uxrcd);
+ }
+ mutex_unlock(&file->device->xrcd_tree_mutex);
+
list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) {
struct ib_pd *pd = uobj->object;
@@ -557,8 +578,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
if (hdr.in_words * 4 != count)
return -EINVAL;
- if (hdr.command < 0 ||
- hdr.command >= ARRAY_SIZE(uverbs_cmd_table) ||
+ if (hdr.command >= ARRAY_SIZE(uverbs_cmd_table) ||
!uverbs_cmd_table[hdr.command])
return -EINVAL;
@@ -741,6 +761,8 @@ static void ib_uverbs_add_one(struct ib_device *device)
kref_init(&uverbs_dev->ref);
init_completion(&uverbs_dev->comp);
+ uverbs_dev->xrcd_tree = RB_ROOT;
+ mutex_init(&uverbs_dev->xrcd_tree_mutex);
spin_lock(&map_lock);
devnum = find_first_zero_bit(dev_map, IB_UVERBS_MAX_DEVICES);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index af7a8b08b2e9..42517500b223 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -39,6 +39,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
@@ -77,6 +78,31 @@ enum ib_rate mult_to_ib_rate(int mult)
}
EXPORT_SYMBOL(mult_to_ib_rate);
+int ib_rate_to_mbps(enum ib_rate rate)
+{
+ switch (rate) {
+ case IB_RATE_2_5_GBPS: return 2500;
+ case IB_RATE_5_GBPS: return 5000;
+ case IB_RATE_10_GBPS: return 10000;
+ case IB_RATE_20_GBPS: return 20000;
+ case IB_RATE_30_GBPS: return 30000;
+ case IB_RATE_40_GBPS: return 40000;
+ case IB_RATE_60_GBPS: return 60000;
+ case IB_RATE_80_GBPS: return 80000;
+ case IB_RATE_120_GBPS: return 120000;
+ case IB_RATE_14_GBPS: return 14062;
+ case IB_RATE_56_GBPS: return 56250;
+ case IB_RATE_112_GBPS: return 112500;
+ case IB_RATE_168_GBPS: return 168750;
+ case IB_RATE_25_GBPS: return 25781;
+ case IB_RATE_100_GBPS: return 103125;
+ case IB_RATE_200_GBPS: return 206250;
+ case IB_RATE_300_GBPS: return 309375;
+ default: return -1;
+ }
+}
+EXPORT_SYMBOL(ib_rate_to_mbps);
+
enum rdma_transport_type
rdma_node_get_transport(enum rdma_node_type node_type)
{
@@ -250,6 +276,13 @@ struct ib_srq *ib_create_srq(struct ib_pd *pd,
srq->uobject = NULL;
srq->event_handler = srq_init_attr->event_handler;
srq->srq_context = srq_init_attr->srq_context;
+ srq->srq_type = srq_init_attr->srq_type;
+ if (srq->srq_type == IB_SRQT_XRC) {
+ srq->ext.xrc.xrcd = srq_init_attr->ext.xrc.xrcd;
+ srq->ext.xrc.cq = srq_init_attr->ext.xrc.cq;
+ atomic_inc(&srq->ext.xrc.xrcd->usecnt);
+ atomic_inc(&srq->ext.xrc.cq->usecnt);
+ }
atomic_inc(&pd->usecnt);
atomic_set(&srq->usecnt, 0);
}
@@ -279,16 +312,29 @@ EXPORT_SYMBOL(ib_query_srq);
int ib_destroy_srq(struct ib_srq *srq)
{
struct ib_pd *pd;
+ enum ib_srq_type srq_type;
+ struct ib_xrcd *uninitialized_var(xrcd);
+ struct ib_cq *uninitialized_var(cq);
int ret;
if (atomic_read(&srq->usecnt))
return -EBUSY;
pd = srq->pd;
+ srq_type = srq->srq_type;
+ if (srq_type == IB_SRQT_XRC) {
+ xrcd = srq->ext.xrc.xrcd;
+ cq = srq->ext.xrc.cq;
+ }
ret = srq->device->destroy_srq(srq);
- if (!ret)
+ if (!ret) {
atomic_dec(&pd->usecnt);
+ if (srq_type == IB_SRQT_XRC) {
+ atomic_dec(&xrcd->usecnt);
+ atomic_dec(&cq->usecnt);
+ }
+ }
return ret;
}
@@ -296,28 +342,123 @@ EXPORT_SYMBOL(ib_destroy_srq);
/* Queue pairs */
+static void __ib_shared_qp_event_handler(struct ib_event *event, void *context)
+{
+ struct ib_qp *qp = context;
+
+ list_for_each_entry(event->element.qp, &qp->open_list, open_list)
+ event->element.qp->event_handler(event, event->element.qp->qp_context);
+}
+
+static void __ib_insert_xrcd_qp(struct ib_xrcd *xrcd, struct ib_qp *qp)
+{
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ list_add(&qp->xrcd_list, &xrcd->tgt_qp_list);
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+}
+
+static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
+ void (*event_handler)(struct ib_event *, void *),
+ void *qp_context)
+{
+ struct ib_qp *qp;
+ unsigned long flags;
+
+ qp = kzalloc(sizeof *qp, GFP_KERNEL);
+ if (!qp)
+ return ERR_PTR(-ENOMEM);
+
+ qp->real_qp = real_qp;
+ atomic_inc(&real_qp->usecnt);
+ qp->device = real_qp->device;
+ qp->event_handler = event_handler;
+ qp->qp_context = qp_context;
+ qp->qp_num = real_qp->qp_num;
+ qp->qp_type = real_qp->qp_type;
+
+ spin_lock_irqsave(&real_qp->device->event_handler_lock, flags);
+ list_add(&qp->open_list, &real_qp->open_list);
+ spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
+
+ return qp;
+}
+
+struct ib_qp *ib_open_qp(struct ib_xrcd *xrcd,
+ struct ib_qp_open_attr *qp_open_attr)
+{
+ struct ib_qp *qp, *real_qp;
+
+ if (qp_open_attr->qp_type != IB_QPT_XRC_TGT)
+ return ERR_PTR(-EINVAL);
+
+ qp = ERR_PTR(-EINVAL);
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ list_for_each_entry(real_qp, &xrcd->tgt_qp_list, xrcd_list) {
+ if (real_qp->qp_num == qp_open_attr->qp_num) {
+ qp = __ib_open_qp(real_qp, qp_open_attr->event_handler,
+ qp_open_attr->qp_context);
+ break;
+ }
+ }
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+ return qp;
+}
+EXPORT_SYMBOL(ib_open_qp);
+
struct ib_qp *ib_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *qp_init_attr)
{
- struct ib_qp *qp;
+ struct ib_qp *qp, *real_qp;
+ struct ib_device *device;
- qp = pd->device->create_qp(pd, qp_init_attr, NULL);
+ device = pd ? pd->device : qp_init_attr->xrcd->device;
+ qp = device->create_qp(pd, qp_init_attr, NULL);
if (!IS_ERR(qp)) {
- qp->device = pd->device;
- qp->pd = pd;
- qp->send_cq = qp_init_attr->send_cq;
- qp->recv_cq = qp_init_attr->recv_cq;
- qp->srq = qp_init_attr->srq;
- qp->uobject = NULL;
- qp->event_handler = qp_init_attr->event_handler;
- qp->qp_context = qp_init_attr->qp_context;
- qp->qp_type = qp_init_attr->qp_type;
- atomic_inc(&pd->usecnt);
- atomic_inc(&qp_init_attr->send_cq->usecnt);
- atomic_inc(&qp_init_attr->recv_cq->usecnt);
- if (qp_init_attr->srq)
- atomic_inc(&qp_init_attr->srq->usecnt);
+ qp->device = device;
+ qp->real_qp = qp;
+ qp->uobject = NULL;
+ qp->qp_type = qp_init_attr->qp_type;
+
+ if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) {
+ qp->event_handler = __ib_shared_qp_event_handler;
+ qp->qp_context = qp;
+ qp->pd = NULL;
+ qp->send_cq = qp->recv_cq = NULL;
+ qp->srq = NULL;
+ qp->xrcd = qp_init_attr->xrcd;
+ atomic_inc(&qp_init_attr->xrcd->usecnt);
+ INIT_LIST_HEAD(&qp->open_list);
+ atomic_set(&qp->usecnt, 0);
+
+ real_qp = qp;
+ qp = __ib_open_qp(real_qp, qp_init_attr->event_handler,
+ qp_init_attr->qp_context);
+ if (!IS_ERR(qp))
+ __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp);
+ else
+ real_qp->device->destroy_qp(real_qp);
+ } else {
+ qp->event_handler = qp_init_attr->event_handler;
+ qp->qp_context = qp_init_attr->qp_context;
+ if (qp_init_attr->qp_type == IB_QPT_XRC_INI) {
+ qp->recv_cq = NULL;
+ qp->srq = NULL;
+ } else {
+ qp->recv_cq = qp_init_attr->recv_cq;
+ atomic_inc(&qp_init_attr->recv_cq->usecnt);
+ qp->srq = qp_init_attr->srq;
+ if (qp->srq)
+ atomic_inc(&qp_init_attr->srq->usecnt);
+ }
+
+ qp->pd = pd;
+ qp->send_cq = qp_init_attr->send_cq;
+ qp->xrcd = NULL;
+
+ atomic_inc(&pd->usecnt);
+ atomic_inc(&qp_init_attr->send_cq->usecnt);
+ }
}
return qp;
@@ -326,8 +467,8 @@ EXPORT_SYMBOL(ib_create_qp);
static const struct {
int valid;
- enum ib_qp_attr_mask req_param[IB_QPT_RAW_ETHERTYPE + 1];
- enum ib_qp_attr_mask opt_param[IB_QPT_RAW_ETHERTYPE + 1];
+ enum ib_qp_attr_mask req_param[IB_QPT_MAX];
+ enum ib_qp_attr_mask opt_param[IB_QPT_MAX];
} qp_state_table[IB_QPS_ERR + 1][IB_QPS_ERR + 1] = {
[IB_QPS_RESET] = {
[IB_QPS_RESET] = { .valid = 1 },
@@ -343,6 +484,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_PKEY_INDEX |
IB_QP_PORT |
IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_INI] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_TGT] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -365,6 +512,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_PKEY_INDEX |
IB_QP_PORT |
IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_INI] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
+ [IB_QPT_XRC_TGT] = (IB_QP_PKEY_INDEX |
+ IB_QP_PORT |
+ IB_QP_ACCESS_FLAGS),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -384,6 +537,16 @@ static const struct {
IB_QP_RQ_PSN |
IB_QP_MAX_DEST_RD_ATOMIC |
IB_QP_MIN_RNR_TIMER),
+ [IB_QPT_XRC_INI] = (IB_QP_AV |
+ IB_QP_PATH_MTU |
+ IB_QP_DEST_QPN |
+ IB_QP_RQ_PSN),
+ [IB_QPT_XRC_TGT] = (IB_QP_AV |
+ IB_QP_PATH_MTU |
+ IB_QP_DEST_QPN |
+ IB_QP_RQ_PSN |
+ IB_QP_MAX_DEST_RD_ATOMIC |
+ IB_QP_MIN_RNR_TIMER),
},
.opt_param = {
[IB_QPT_UD] = (IB_QP_PKEY_INDEX |
@@ -394,6 +557,12 @@ static const struct {
[IB_QPT_RC] = (IB_QP_ALT_PATH |
IB_QP_ACCESS_FLAGS |
IB_QP_PKEY_INDEX),
+ [IB_QPT_XRC_INI] = (IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX),
+ [IB_QPT_XRC_TGT] = (IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -414,6 +583,13 @@ static const struct {
IB_QP_RNR_RETRY |
IB_QP_SQ_PSN |
IB_QP_MAX_QP_RD_ATOMIC),
+ [IB_QPT_XRC_INI] = (IB_QP_TIMEOUT |
+ IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY |
+ IB_QP_SQ_PSN |
+ IB_QP_MAX_QP_RD_ATOMIC),
+ [IB_QPT_XRC_TGT] = (IB_QP_TIMEOUT |
+ IB_QP_SQ_PSN),
[IB_QPT_SMI] = IB_QP_SQ_PSN,
[IB_QPT_GSI] = IB_QP_SQ_PSN,
},
@@ -429,6 +605,15 @@ static const struct {
IB_QP_ACCESS_FLAGS |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -453,6 +638,15 @@ static const struct {
IB_QP_ALT_PATH |
IB_QP_PATH_MIG_STATE |
IB_QP_MIN_RNR_TIMER),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_ALT_PATH |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_ALT_PATH |
+ IB_QP_PATH_MIG_STATE |
+ IB_QP_MIN_RNR_TIMER),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -465,6 +659,8 @@ static const struct {
[IB_QPT_UD] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_UC] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_RC] = IB_QP_EN_SQD_ASYNC_NOTIFY,
+ [IB_QPT_XRC_INI] = IB_QP_EN_SQD_ASYNC_NOTIFY,
+ [IB_QPT_XRC_TGT] = IB_QP_EN_SQD_ASYNC_NOTIFY, /* ??? */
[IB_QPT_SMI] = IB_QP_EN_SQD_ASYNC_NOTIFY,
[IB_QPT_GSI] = IB_QP_EN_SQD_ASYNC_NOTIFY
}
@@ -487,6 +683,15 @@ static const struct {
IB_QP_ACCESS_FLAGS |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_CUR_STATE |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_CUR_STATE |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_CUR_STATE |
@@ -515,6 +720,25 @@ static const struct {
IB_QP_PKEY_INDEX |
IB_QP_MIN_RNR_TIMER |
IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_INI] = (IB_QP_PORT |
+ IB_QP_AV |
+ IB_QP_TIMEOUT |
+ IB_QP_RETRY_CNT |
+ IB_QP_RNR_RETRY |
+ IB_QP_MAX_QP_RD_ATOMIC |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX |
+ IB_QP_PATH_MIG_STATE),
+ [IB_QPT_XRC_TGT] = (IB_QP_PORT |
+ IB_QP_AV |
+ IB_QP_TIMEOUT |
+ IB_QP_MAX_DEST_RD_ATOMIC |
+ IB_QP_ALT_PATH |
+ IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX |
+ IB_QP_MIN_RNR_TIMER |
+ IB_QP_PATH_MIG_STATE),
[IB_QPT_SMI] = (IB_QP_PKEY_INDEX |
IB_QP_QKEY),
[IB_QPT_GSI] = (IB_QP_PKEY_INDEX |
@@ -579,7 +803,7 @@ int ib_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask)
{
- return qp->device->modify_qp(qp, qp_attr, qp_attr_mask, NULL);
+ return qp->device->modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
}
EXPORT_SYMBOL(ib_modify_qp);
@@ -589,11 +813,59 @@ int ib_query_qp(struct ib_qp *qp,
struct ib_qp_init_attr *qp_init_attr)
{
return qp->device->query_qp ?
- qp->device->query_qp(qp, qp_attr, qp_attr_mask, qp_init_attr) :
+ qp->device->query_qp(qp->real_qp, qp_attr, qp_attr_mask, qp_init_attr) :
-ENOSYS;
}
EXPORT_SYMBOL(ib_query_qp);
+int ib_close_qp(struct ib_qp *qp)
+{
+ struct ib_qp *real_qp;
+ unsigned long flags;
+
+ real_qp = qp->real_qp;
+ if (real_qp == qp)
+ return -EINVAL;
+
+ spin_lock_irqsave(&real_qp->device->event_handler_lock, flags);
+ list_del(&qp->open_list);
+ spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
+
+ atomic_dec(&real_qp->usecnt);
+ kfree(qp);
+
+ return 0;
+}
+EXPORT_SYMBOL(ib_close_qp);
+
+static int __ib_destroy_shared_qp(struct ib_qp *qp)
+{
+ struct ib_xrcd *xrcd;
+ struct ib_qp *real_qp;
+ int ret;
+
+ real_qp = qp->real_qp;
+ xrcd = real_qp->xrcd;
+
+ mutex_lock(&xrcd->tgt_qp_mutex);
+ ib_close_qp(qp);
+ if (atomic_read(&real_qp->usecnt) == 0)
+ list_del(&real_qp->xrcd_list);
+ else
+ real_qp = NULL;
+ mutex_unlock(&xrcd->tgt_qp_mutex);
+
+ if (real_qp) {
+ ret = ib_destroy_qp(real_qp);
+ if (!ret)
+ atomic_dec(&xrcd->usecnt);
+ else
+ __ib_insert_xrcd_qp(xrcd, real_qp);
+ }
+
+ return 0;
+}
+
int ib_destroy_qp(struct ib_qp *qp)
{
struct ib_pd *pd;
@@ -601,16 +873,25 @@ int ib_destroy_qp(struct ib_qp *qp)
struct ib_srq *srq;
int ret;
- pd = qp->pd;
- scq = qp->send_cq;
- rcq = qp->recv_cq;
- srq = qp->srq;
+ if (atomic_read(&qp->usecnt))
+ return -EBUSY;
+
+ if (qp->real_qp != qp)
+ return __ib_destroy_shared_qp(qp);
+
+ pd = qp->pd;
+ scq = qp->send_cq;
+ rcq = qp->recv_cq;
+ srq = qp->srq;
ret = qp->device->destroy_qp(qp);
if (!ret) {
- atomic_dec(&pd->usecnt);
- atomic_dec(&scq->usecnt);
- atomic_dec(&rcq->usecnt);
+ if (pd)
+ atomic_dec(&pd->usecnt);
+ if (scq)
+ atomic_dec(&scq->usecnt);
+ if (rcq)
+ atomic_dec(&rcq->usecnt);
if (srq)
atomic_dec(&srq->usecnt);
}
@@ -920,3 +1201,42 @@ int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
return qp->device->detach_mcast(qp, gid, lid);
}
EXPORT_SYMBOL(ib_detach_mcast);
+
+struct ib_xrcd *ib_alloc_xrcd(struct ib_device *device)
+{
+ struct ib_xrcd *xrcd;
+
+ if (!device->alloc_xrcd)
+ return ERR_PTR(-ENOSYS);
+
+ xrcd = device->alloc_xrcd(device, NULL, NULL);
+ if (!IS_ERR(xrcd)) {
+ xrcd->device = device;
+ xrcd->inode = NULL;
+ atomic_set(&xrcd->usecnt, 0);
+ mutex_init(&xrcd->tgt_qp_mutex);
+ INIT_LIST_HEAD(&xrcd->tgt_qp_list);
+ }
+
+ return xrcd;
+}
+EXPORT_SYMBOL(ib_alloc_xrcd);
+
+int ib_dealloc_xrcd(struct ib_xrcd *xrcd)
+{
+ struct ib_qp *qp;
+ int ret;
+
+ if (atomic_read(&xrcd->usecnt))
+ return -EBUSY;
+
+ while (!list_empty(&xrcd->tgt_qp_list)) {
+ qp = list_entry(xrcd->tgt_qp_list.next, struct ib_qp, xrcd_list);
+ ret = ib_destroy_qp(qp);
+ if (ret)
+ return ret;
+ }
+
+ return xrcd->device->dealloc_xrcd(xrcd);
+}
+EXPORT_SYMBOL(ib_dealloc_xrcd);
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
index 24f9e3a90e8e..32d34e88d5cf 100644
--- a/drivers/infiniband/hw/amso1100/c2_ae.c
+++ b/drivers/infiniband/hw/amso1100/c2_ae.c
@@ -288,6 +288,11 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
cm_event.private_data_len =
be32_to_cpu(req->private_data_length);
cm_event.private_data = req->private_data;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send
+ * max supported values
+ */
+ cm_event.ird = cm_event.ord = 128;
if (cm_id->event_handler)
cm_id->event_handler(cm_id, &cm_event);
diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c
index 0ebe4e806b86..8951db4ae29d 100644
--- a/drivers/infiniband/hw/amso1100/c2_intr.c
+++ b/drivers/infiniband/hw/amso1100/c2_intr.c
@@ -183,6 +183,11 @@ static void handle_vq(struct c2_dev *c2dev, u32 mq_index)
case IW_CM_EVENT_ESTABLISHED:
c2_set_qp_state(req->qp,
C2_QP_STATE_RTS);
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send
+ * max supported values
+ */
+ cm_event.ird = cm_event.ord = 128;
case IW_CM_EVENT_CLOSE:
/*
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index f101bb73be63..12f923d64e42 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -753,10 +753,7 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
memcpy_fromio(netdev->dev_addr, c2dev->kva + C2_REGS_RDMA_ENADDR, 6);
/* Print out the MAC address */
- pr_debug("%s: MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
- netdev->name,
- netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
- netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+ pr_debug("%s: MAC %pM\n", netdev->name, netdev->dev_addr);
#if 0
/* Disable network packets */
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 6cd642aaa4de..de6d0774e609 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -753,6 +753,11 @@ static void connect_request_upcall(struct iwch_ep *ep)
event.private_data_len = ep->plen;
event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
event.provider_data = ep;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send max
+ * supported values
+ */
+ event.ird = event.ord = 8;
if (state_read(&ep->parent_ep->com) != DEAD) {
get_ep(&ep->com);
ep->parent_ep->com.cm_id->event_handler(
@@ -770,6 +775,11 @@ static void established_upcall(struct iwch_ep *ep)
PDBG("%s ep %p\n", __func__, ep);
memset(&event, 0, sizeof(event));
event.event = IW_CM_EVENT_ESTABLISHED;
+ /*
+ * Until ird/ord negotiation via MPAv2 support is added, send max
+ * supported values
+ */
+ event.ird = event.ord = 8;
if (ep->com.cm_id) {
PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid);
ep->com.cm_id->event_handler(ep->com.cm_id, &event);
diff --git a/drivers/infiniband/hw/cxgb3/iwch_ev.c b/drivers/infiniband/hw/cxgb3/iwch_ev.c
index 71e0d845da3d..abcc9e76962b 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_ev.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_ev.c
@@ -46,6 +46,7 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
struct ib_event event;
struct iwch_qp_attributes attrs;
struct iwch_qp *qhp;
+ unsigned long flag;
spin_lock(&rnicp->lock);
qhp = get_qhp(rnicp, CQE_QPID(rsp_msg->cqe));
@@ -94,7 +95,9 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
if (qhp->ibqp.event_handler)
(*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
if (atomic_dec_and_test(&qhp->refcnt))
wake_up(&qhp->wait);
@@ -107,6 +110,7 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb)
struct iwch_cq *chp;
struct iwch_qp *qhp;
u32 cqid = RSPQ_CQID(rsp_msg);
+ unsigned long flag;
rnicp = (struct iwch_dev *) rdev_p->ulp;
spin_lock(&rnicp->lock);
@@ -170,7 +174,9 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb)
*/
if (qhp->ep && SQ_TYPE(rsp_msg->cqe))
dst_confirm(qhp->ep->dst);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
break;
case TPT_ERR_STAG:
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index c7d9411f2954..37c224fc3ad9 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -190,6 +190,7 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
chp->rhp = rhp;
chp->ibcq.cqe = 1 << chp->cq.size_log2;
spin_lock_init(&chp->lock);
+ spin_lock_init(&chp->comp_handler_lock);
atomic_set(&chp->refcnt, 1);
init_waitqueue_head(&chp->wait);
if (insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid)) {
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.h b/drivers/infiniband/hw/cxgb3/iwch_provider.h
index 9a342c9b220d..87c14b0c5ac0 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.h
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.h
@@ -103,6 +103,7 @@ struct iwch_cq {
struct iwch_dev *rhp;
struct t3_cq cq;
spinlock_t lock;
+ spinlock_t comp_handler_lock;
atomic_t refcnt;
wait_queue_head_t wait;
u32 __user *user_rptr_addr;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index ecd313f359a4..bea5839d89ee 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -822,8 +822,11 @@ static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp,
flushed = cxio_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&rchp->lock, *flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&rchp->comp_handler_lock, *flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, *flag);
+ }
/* locking hierarchy: cq lock first, then qp lock. */
spin_lock_irqsave(&schp->lock, *flag);
@@ -833,8 +836,11 @@ static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp,
flushed = cxio_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&schp->lock, *flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&schp->comp_handler_lock, *flag);
(*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, *flag);
+ }
/* deref */
if (atomic_dec_and_test(&qhp->refcnt))
@@ -853,11 +859,15 @@ static void flush_qp(struct iwch_qp *qhp, unsigned long *flag)
if (qhp->ibqp.uobject) {
cxio_set_wq_in_error(&qhp->wq);
cxio_set_cq_in_error(&rchp->cq);
+ spin_lock_irqsave(&rchp->comp_handler_lock, *flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, *flag);
if (schp != rchp) {
cxio_set_cq_in_error(&schp->cq);
+ spin_lock_irqsave(&schp->comp_handler_lock, *flag);
(*schp->ibcq.comp_handler)(&schp->ibcq,
schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, *flag);
}
return;
}
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 77f769d9227d..b36cdac9c558 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -103,7 +103,8 @@ MODULE_PARM_DESC(ep_timeout_secs, "CM Endpoint operation timeout "
static int mpa_rev = 1;
module_param(mpa_rev, int, 0644);
MODULE_PARM_DESC(mpa_rev, "MPA Revision, 0 supports amso1100, "
- "1 is spec compliant. (default=1)");
+ "1 is RFC0544 spec compliant, 2 is IETF MPA Peer Connect Draft"
+ " compliant (default=1)");
static int markers_enabled;
module_param(markers_enabled, int, 0644);
@@ -497,17 +498,21 @@ static int send_connect(struct c4iw_ep *ep)
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
}
-static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb)
+static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb,
+ u8 mpa_rev_to_use)
{
int mpalen, wrlen;
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
BUG_ON(skb_cloned(skb));
mpalen = sizeof(*mpa) + ep->plen;
+ if (mpa_rev_to_use == 2)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(skb, wrlen, GFP_KERNEL);
if (!skb) {
@@ -533,12 +538,39 @@ static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb)
mpa = (struct mpa_message *)(req + 1);
memcpy(mpa->key, MPA_KEY_REQ, sizeof(mpa->key));
mpa->flags = (crc_enabled ? MPA_CRC : 0) |
- (markers_enabled ? MPA_MARKERS : 0);
+ (markers_enabled ? MPA_MARKERS : 0) |
+ (mpa_rev_to_use == 2 ? MPA_ENHANCED_RDMA_CONN : 0);
mpa->private_data_size = htons(ep->plen);
- mpa->revision = mpa_rev;
+ mpa->revision = mpa_rev_to_use;
+ if (mpa_rev_to_use == 1)
+ ep->tried_with_mpa_v1 = 1;
+
+ if (mpa_rev_to_use == 2) {
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons((u16)ep->ird);
+ mpa_v2_params.ord = htons((u16)ep->ord);
+
+ if (peer2peer) {
+ mpa_v2_params.ird |= htons(MPA_V2_PEER2PEER_MODEL);
+ if (p2p_type == FW_RI_INIT_P2PTYPE_RDMA_WRITE)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_WRITE_RTR);
+ else if (p2p_type == FW_RI_INIT_P2PTYPE_READ_REQ)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_READ_RTR);
+ }
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
- if (ep->plen)
- memcpy(mpa->private_data, ep->mpa_pkt + sizeof(*mpa), ep->plen);
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params),
+ ep->mpa_pkt + sizeof(*mpa), ep->plen);
+ } else
+ if (ep->plen)
+ memcpy(mpa->private_data,
+ ep->mpa_pkt + sizeof(*mpa), ep->plen);
/*
* Reference the mpa skb. This ensures the data area
@@ -562,10 +594,13 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen)
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
struct sk_buff *skb;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
mpalen = sizeof(*mpa) + plen;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(NULL, wrlen, GFP_KERNEL);
@@ -595,8 +630,29 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen)
mpa->flags = MPA_REJECT;
mpa->revision = mpa_rev;
mpa->private_data_size = htons(plen);
- if (plen)
- memcpy(mpa->private_data, pdata, plen);
+
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ mpa->flags |= MPA_ENHANCED_RDMA_CONN;
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons(((u16)ep->ird) |
+ (peer2peer ? MPA_V2_PEER2PEER_MODEL :
+ 0));
+ mpa_v2_params.ord = htons(((u16)ep->ord) | (peer2peer ?
+ (p2p_type ==
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE ?
+ MPA_V2_RDMA_WRITE_RTR : p2p_type ==
+ FW_RI_INIT_P2PTYPE_READ_REQ ?
+ MPA_V2_RDMA_READ_RTR : 0) : 0));
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
+
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params), pdata, plen);
+ } else
+ if (plen)
+ memcpy(mpa->private_data, pdata, plen);
/*
* Reference the mpa skb again. This ensures the data area
@@ -617,10 +673,13 @@ static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen)
struct fw_ofld_tx_data_wr *req;
struct mpa_message *mpa;
struct sk_buff *skb;
+ struct mpa_v2_conn_params mpa_v2_params;
PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen);
mpalen = sizeof(*mpa) + plen;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn)
+ mpalen += sizeof(struct mpa_v2_conn_params);
wrlen = roundup(mpalen + sizeof *req, 16);
skb = get_skb(NULL, wrlen, GFP_KERNEL);
@@ -649,10 +708,36 @@ static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen)
memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key));
mpa->flags = (ep->mpa_attr.crc_enabled ? MPA_CRC : 0) |
(markers_enabled ? MPA_MARKERS : 0);
- mpa->revision = mpa_rev;
+ mpa->revision = ep->mpa_attr.version;
mpa->private_data_size = htons(plen);
- if (plen)
- memcpy(mpa->private_data, pdata, plen);
+
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ mpa->flags |= MPA_ENHANCED_RDMA_CONN;
+ mpa->private_data_size +=
+ htons(sizeof(struct mpa_v2_conn_params));
+ mpa_v2_params.ird = htons((u16)ep->ird);
+ mpa_v2_params.ord = htons((u16)ep->ord);
+ if (peer2peer && (ep->mpa_attr.p2p_type !=
+ FW_RI_INIT_P2PTYPE_DISABLED)) {
+ mpa_v2_params.ird |= htons(MPA_V2_PEER2PEER_MODEL);
+
+ if (p2p_type == FW_RI_INIT_P2PTYPE_RDMA_WRITE)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_WRITE_RTR);
+ else if (p2p_type == FW_RI_INIT_P2PTYPE_READ_REQ)
+ mpa_v2_params.ord |=
+ htons(MPA_V2_RDMA_READ_RTR);
+ }
+
+ memcpy(mpa->private_data, &mpa_v2_params,
+ sizeof(struct mpa_v2_conn_params));
+
+ if (ep->plen)
+ memcpy(mpa->private_data +
+ sizeof(struct mpa_v2_conn_params), pdata, plen);
+ } else
+ if (plen)
+ memcpy(mpa->private_data, pdata, plen);
/*
* Reference the mpa skb. This ensures the data area
@@ -695,7 +780,10 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb)
/* start MPA negotiation */
send_flowc(ep, NULL);
- send_mpa_req(ep, skb);
+ if (ep->retry_with_mpa_v1)
+ send_mpa_req(ep, skb, 1);
+ else
+ send_mpa_req(ep, skb, mpa_rev);
return 0;
}
@@ -769,8 +857,19 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status)
event.remote_addr = ep->com.remote_addr;
if ((status == 0) || (status == -ECONNREFUSED)) {
- event.private_data_len = ep->plen;
- event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
+ if (!ep->tried_with_mpa_v1) {
+ /* this means MPA_v2 is used */
+ event.private_data_len = ep->plen -
+ sizeof(struct mpa_v2_conn_params);
+ event.private_data = ep->mpa_pkt +
+ sizeof(struct mpa_message) +
+ sizeof(struct mpa_v2_conn_params);
+ } else {
+ /* this means MPA_v1 is used */
+ event.private_data_len = ep->plen;
+ event.private_data = ep->mpa_pkt +
+ sizeof(struct mpa_message);
+ }
}
PDBG("%s ep %p tid %u status %d\n", __func__, ep,
@@ -793,9 +892,22 @@ static void connect_request_upcall(struct c4iw_ep *ep)
event.event = IW_CM_EVENT_CONNECT_REQUEST;
event.local_addr = ep->com.local_addr;
event.remote_addr = ep->com.remote_addr;
- event.private_data_len = ep->plen;
- event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
event.provider_data = ep;
+ if (!ep->tried_with_mpa_v1) {
+ /* this means MPA_v2 is used */
+ event.ord = ep->ord;
+ event.ird = ep->ird;
+ event.private_data_len = ep->plen -
+ sizeof(struct mpa_v2_conn_params);
+ event.private_data = ep->mpa_pkt + sizeof(struct mpa_message) +
+ sizeof(struct mpa_v2_conn_params);
+ } else {
+ /* this means MPA_v1 is used. Send max supported */
+ event.ord = c4iw_max_read_depth;
+ event.ird = c4iw_max_read_depth;
+ event.private_data_len = ep->plen;
+ event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
+ }
if (state_read(&ep->parent_ep->com) != DEAD) {
c4iw_get_ep(&ep->com);
ep->parent_ep->com.cm_id->event_handler(
@@ -813,6 +925,8 @@ static void established_upcall(struct c4iw_ep *ep)
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
memset(&event, 0, sizeof(event));
event.event = IW_CM_EVENT_ESTABLISHED;
+ event.ird = ep->ird;
+ event.ord = ep->ord;
if (ep->com.cm_id) {
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
ep->com.cm_id->event_handler(ep->com.cm_id, &event);
@@ -848,7 +962,10 @@ static int update_rx_credits(struct c4iw_ep *ep, u32 credits)
static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
{
struct mpa_message *mpa;
+ struct mpa_v2_conn_params *mpa_v2_params;
u16 plen;
+ u16 resp_ird, resp_ord;
+ u8 rtr_mismatch = 0, insuff_ird = 0;
struct c4iw_qp_attributes attrs;
enum c4iw_qp_attr_mask mask;
int err;
@@ -888,7 +1005,9 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
mpa = (struct mpa_message *) ep->mpa_pkt;
/* Validate MPA header. */
- if (mpa->revision != mpa_rev) {
+ if (mpa->revision > mpa_rev) {
+ printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d,"
+ " Received = %d\n", __func__, mpa_rev, mpa->revision);
err = -EPROTO;
goto err;
}
@@ -938,13 +1057,66 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0;
ep->mpa_attr.recv_marker_enabled = markers_enabled;
ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0;
- ep->mpa_attr.version = mpa_rev;
- ep->mpa_attr.p2p_type = peer2peer ? p2p_type :
- FW_RI_INIT_P2PTYPE_DISABLED;
+ ep->mpa_attr.version = mpa->revision;
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+
+ if (mpa->revision == 2) {
+ ep->mpa_attr.enhanced_rdma_conn =
+ mpa->flags & MPA_ENHANCED_RDMA_CONN ? 1 : 0;
+ if (ep->mpa_attr.enhanced_rdma_conn) {
+ mpa_v2_params = (struct mpa_v2_conn_params *)
+ (ep->mpa_pkt + sizeof(*mpa));
+ resp_ird = ntohs(mpa_v2_params->ird) &
+ MPA_V2_IRD_ORD_MASK;
+ resp_ord = ntohs(mpa_v2_params->ord) &
+ MPA_V2_IRD_ORD_MASK;
+
+ /*
+ * This is a double-check. Ideally, below checks are
+ * not required since ird/ord stuff has been taken
+ * care of in c4iw_accept_cr
+ */
+ if ((ep->ird < resp_ord) || (ep->ord > resp_ird)) {
+ err = -ENOMEM;
+ ep->ird = resp_ord;
+ ep->ord = resp_ird;
+ insuff_ird = 1;
+ }
+
+ if (ntohs(mpa_v2_params->ird) &
+ MPA_V2_PEER2PEER_MODEL) {
+ if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_WRITE_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE;
+ else if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_READ_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_READ_REQ;
+ }
+ }
+ } else if (mpa->revision == 1)
+ if (peer2peer)
+ ep->mpa_attr.p2p_type = p2p_type;
+
PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, "
- "xmit_marker_enabled=%d, version=%d\n", __func__,
- ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled,
- ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version);
+ "xmit_marker_enabled=%d, version=%d p2p_type=%d local-p2p_type = "
+ "%d\n", __func__, ep->mpa_attr.crc_enabled,
+ ep->mpa_attr.recv_marker_enabled,
+ ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version,
+ ep->mpa_attr.p2p_type, p2p_type);
+
+ /*
+ * If responder's RTR does not match with that of initiator, assign
+ * FW_RI_INIT_P2PTYPE_DISABLED in mpa attributes so that RTR is not
+ * generated when moving QP to RTS state.
+ * A TERM message will be sent after QP has moved to RTS state
+ */
+ if ((ep->mpa_attr.version == 2) &&
+ (ep->mpa_attr.p2p_type != p2p_type)) {
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+ rtr_mismatch = 1;
+ }
attrs.mpa_attr = ep->mpa_attr;
attrs.max_ird = ep->ird;
@@ -961,6 +1133,39 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
ep->com.qp, mask, &attrs, 1);
if (err)
goto err;
+
+ /*
+ * If responder's RTR requirement did not match with what initiator
+ * supports, generate TERM message
+ */
+ if (rtr_mismatch) {
+ printk(KERN_ERR "%s: RTR mismatch, sending TERM\n", __func__);
+ attrs.layer_etype = LAYER_MPA | DDP_LLP;
+ attrs.ecode = MPA_NOMATCH_RTR;
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Generate TERM if initiator IRD is not sufficient for responder
+ * provided ORD. Currently, we do the same behaviour even when
+ * responder provided IRD is also not sufficient as regards to
+ * initiator ORD.
+ */
+ if (insuff_ird) {
+ printk(KERN_ERR "%s: Insufficient IRD, sending TERM\n",
+ __func__);
+ attrs.layer_etype = LAYER_MPA | DDP_LLP;
+ attrs.ecode = MPA_INSUFF_IRD;
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ err = -ENOMEM;
+ goto out;
+ }
goto out;
err:
state_set(&ep->com, ABORTING);
@@ -973,6 +1178,7 @@ out:
static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
{
struct mpa_message *mpa;
+ struct mpa_v2_conn_params *mpa_v2_params;
u16 plen;
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
@@ -1013,7 +1219,9 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
/*
* Validate MPA Header.
*/
- if (mpa->revision != mpa_rev) {
+ if (mpa->revision > mpa_rev) {
+ printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d,"
+ " Received = %d\n", __func__, mpa_rev, mpa->revision);
abort_connection(ep, skb, GFP_KERNEL);
return;
}
@@ -1056,9 +1264,37 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0;
ep->mpa_attr.recv_marker_enabled = markers_enabled;
ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0;
- ep->mpa_attr.version = mpa_rev;
- ep->mpa_attr.p2p_type = peer2peer ? p2p_type :
- FW_RI_INIT_P2PTYPE_DISABLED;
+ ep->mpa_attr.version = mpa->revision;
+ if (mpa->revision == 1)
+ ep->tried_with_mpa_v1 = 1;
+ ep->mpa_attr.p2p_type = FW_RI_INIT_P2PTYPE_DISABLED;
+
+ if (mpa->revision == 2) {
+ ep->mpa_attr.enhanced_rdma_conn =
+ mpa->flags & MPA_ENHANCED_RDMA_CONN ? 1 : 0;
+ if (ep->mpa_attr.enhanced_rdma_conn) {
+ mpa_v2_params = (struct mpa_v2_conn_params *)
+ (ep->mpa_pkt + sizeof(*mpa));
+ ep->ird = ntohs(mpa_v2_params->ird) &
+ MPA_V2_IRD_ORD_MASK;
+ ep->ord = ntohs(mpa_v2_params->ord) &
+ MPA_V2_IRD_ORD_MASK;
+ if (ntohs(mpa_v2_params->ird) & MPA_V2_PEER2PEER_MODEL)
+ if (peer2peer) {
+ if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_WRITE_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_RDMA_WRITE;
+ else if (ntohs(mpa_v2_params->ord) &
+ MPA_V2_RDMA_READ_RTR)
+ ep->mpa_attr.p2p_type =
+ FW_RI_INIT_P2PTYPE_READ_REQ;
+ }
+ }
+ } else if (mpa->revision == 1)
+ if (peer2peer)
+ ep->mpa_attr.p2p_type = p2p_type;
+
PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, "
"xmit_marker_enabled=%d, version=%d p2p_type=%d\n", __func__,
ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled,
@@ -1550,6 +1786,112 @@ static int is_neg_adv_abort(unsigned int status)
status == CPL_ERR_PERSIST_NEG_ADVICE;
}
+static int c4iw_reconnect(struct c4iw_ep *ep)
+{
+ int err = 0;
+ struct rtable *rt;
+ struct net_device *pdev;
+ struct neighbour *neigh;
+ int step;
+
+ PDBG("%s qp %p cm_id %p\n", __func__, ep->com.qp, ep->com.cm_id);
+ init_timer(&ep->timer);
+
+ /*
+ * Allocate an active TID to initiate a TCP connection.
+ */
+ ep->atid = cxgb4_alloc_atid(ep->com.dev->rdev.lldi.tids, ep);
+ if (ep->atid == -1) {
+ printk(KERN_ERR MOD "%s - cannot alloc atid.\n", __func__);
+ err = -ENOMEM;
+ goto fail2;
+ }
+
+ /* find a route */
+ rt = find_route(ep->com.dev,
+ ep->com.cm_id->local_addr.sin_addr.s_addr,
+ ep->com.cm_id->remote_addr.sin_addr.s_addr,
+ ep->com.cm_id->local_addr.sin_port,
+ ep->com.cm_id->remote_addr.sin_port, 0);
+ if (!rt) {
+ printk(KERN_ERR MOD "%s - cannot find route.\n", __func__);
+ err = -EHOSTUNREACH;
+ goto fail3;
+ }
+ ep->dst = &rt->dst;
+
+ neigh = dst_get_neighbour(ep->dst);
+
+ /* get a l2t entry */
+ if (neigh->dev->flags & IFF_LOOPBACK) {
+ PDBG("%s LOOPBACK\n", __func__);
+ pdev = ip_dev_find(&init_net,
+ ep->com.cm_id->remote_addr.sin_addr.s_addr);
+ ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+ neigh, pdev, 0);
+ ep->mtu = pdev->mtu;
+ ep->tx_chan = cxgb4_port_chan(pdev);
+ ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
+ step = ep->com.dev->rdev.lldi.ntxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->txq_idx = cxgb4_port_idx(pdev) * step;
+ step = ep->com.dev->rdev.lldi.nrxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->ctrlq_idx = cxgb4_port_idx(pdev);
+ ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
+ cxgb4_port_idx(pdev) * step];
+ dev_put(pdev);
+ } else {
+ ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+ neigh, neigh->dev, 0);
+ ep->mtu = dst_mtu(ep->dst);
+ ep->tx_chan = cxgb4_port_chan(neigh->dev);
+ ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
+ step = ep->com.dev->rdev.lldi.ntxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
+ ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
+ step = ep->com.dev->rdev.lldi.nrxq /
+ ep->com.dev->rdev.lldi.nchan;
+ ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
+ cxgb4_port_idx(neigh->dev) * step];
+ }
+ if (!ep->l2t) {
+ printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
+ err = -ENOMEM;
+ goto fail4;
+ }
+
+ PDBG("%s txq_idx %u tx_chan %u smac_idx %u rss_qid %u l2t_idx %u\n",
+ __func__, ep->txq_idx, ep->tx_chan, ep->smac_idx, ep->rss_qid,
+ ep->l2t->idx);
+
+ state_set(&ep->com, CONNECTING);
+ ep->tos = 0;
+
+ /* send connect request to rnic */
+ err = send_connect(ep);
+ if (!err)
+ goto out;
+
+ cxgb4_l2t_release(ep->l2t);
+fail4:
+ dst_release(ep->dst);
+fail3:
+ cxgb4_free_atid(ep->com.dev->rdev.lldi.tids, ep->atid);
+fail2:
+ /*
+ * remember to send notification to upper layer.
+ * We are in here so the upper layer is not aware that this is
+ * re-connect attempt and so, upper layer is still waiting for
+ * response of 1st connect request.
+ */
+ connect_reply_upcall(ep, -ECONNRESET);
+ c4iw_put_ep(&ep->com);
+out:
+ return err;
+}
+
static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_abort_req_rss *req = cplhdr(skb);
@@ -1573,8 +1915,11 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
/*
* Wake up any threads in rdma_init() or rdma_fini().
+ * However, this is not needed if com state is just
+ * MPA_REQ_SENT
*/
- c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
+ if (ep->com.state != MPA_REQ_SENT)
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
@@ -1585,7 +1930,21 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
break;
case MPA_REQ_SENT:
stop_ep_timer(ep);
- connect_reply_upcall(ep, -ECONNRESET);
+ if (mpa_rev == 2 && ep->tried_with_mpa_v1)
+ connect_reply_upcall(ep, -ECONNRESET);
+ else {
+ /*
+ * we just don't send notification upwards because we
+ * want to retry with mpa_v1 without upper layers even
+ * knowing it.
+ *
+ * do some housekeeping so as to re-initiate the
+ * connection
+ */
+ PDBG("%s: mpa_rev=%d. Retrying with mpav1\n", __func__,
+ mpa_rev);
+ ep->retry_with_mpa_v1 = 1;
+ }
break;
case MPA_REP_SENT:
break;
@@ -1621,7 +1980,9 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
dst_confirm(ep->dst);
if (ep->com.state != ABORTING) {
__state_set(&ep->com, DEAD);
- release = 1;
+ /* we don't release if we want to retry with mpa_v1 */
+ if (!ep->retry_with_mpa_v1)
+ release = 1;
}
mutex_unlock(&ep->com.mutex);
@@ -1641,6 +2002,15 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
out:
if (release)
release_ep_resources(ep);
+
+ /* retry with mpa-v1 */
+ if (ep && ep->retry_with_mpa_v1) {
+ cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid);
+ dst_release(ep->dst);
+ cxgb4_l2t_release(ep->l2t);
+ c4iw_reconnect(ep);
+ }
+
return 0;
}
@@ -1792,18 +2162,40 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
goto err;
}
- cm_id->add_ref(cm_id);
- ep->com.cm_id = cm_id;
- ep->com.qp = qp;
+ if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) {
+ if (conn_param->ord > ep->ird) {
+ ep->ird = conn_param->ird;
+ ep->ord = conn_param->ord;
+ send_mpa_reject(ep, conn_param->private_data,
+ conn_param->private_data_len);
+ abort_connection(ep, NULL, GFP_KERNEL);
+ err = -ENOMEM;
+ goto err;
+ }
+ if (conn_param->ird > ep->ord) {
+ if (!ep->ord)
+ conn_param->ird = 1;
+ else {
+ abort_connection(ep, NULL, GFP_KERNEL);
+ err = -ENOMEM;
+ goto err;
+ }
+ }
+ }
ep->ird = conn_param->ird;
ep->ord = conn_param->ord;
- if (peer2peer && ep->ird == 0)
- ep->ird = 1;
+ if (ep->mpa_attr.version != 2)
+ if (peer2peer && ep->ird == 0)
+ ep->ird = 1;
PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord);
+ cm_id->add_ref(cm_id);
+ ep->com.cm_id = cm_id;
+ ep->com.qp = qp;
+
/* bind QP to EP and move to RTS */
attrs.mpa_attr = ep->mpa_attr;
attrs.max_ird = ep->ird;
@@ -1944,6 +2336,8 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
ep->com.dev->rdev.lldi.nchan;
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
cxgb4_port_idx(neigh->dev) * step];
+ ep->retry_with_mpa_v1 = 0;
+ ep->tried_with_mpa_v1 = 0;
}
if (!ep->l2t) {
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
@@ -2323,8 +2717,11 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb)
/*
* Wake up any threads in rdma_init() or rdma_fini().
+ * However, this is not needed if com state is just
+ * MPA_REQ_SENT
*/
- c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
+ if (ep->com.state != MPA_REQ_SENT)
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
sched(dev, skb);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 1720dc790d13..f35a935267e7 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -185,7 +185,7 @@ static void insert_recv_cqe(struct t4_wq *wq, struct t4_cq *cq)
V_CQE_OPCODE(FW_RI_SEND) |
V_CQE_TYPE(0) |
V_CQE_SWCQE(1) |
- V_CQE_QPID(wq->rq.qid));
+ V_CQE_QPID(wq->sq.qid));
cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen));
cq->sw_queue[cq->sw_pidx] = cqe;
t4_swcq_produce(cq);
@@ -818,6 +818,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
chp->cq.size--; /* status page */
chp->ibcq.cqe = entries - 2;
spin_lock_init(&chp->lock);
+ spin_lock_init(&chp->comp_handler_lock);
atomic_set(&chp->refcnt, 1);
init_waitqueue_head(&chp->wait);
ret = insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid);
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 40a13cc633a3..6d0df6ec161b 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -376,10 +376,8 @@ struct uld_ctx {
struct c4iw_dev *dev;
};
-static void c4iw_remove(struct uld_ctx *ctx)
+static void c4iw_dealloc(struct uld_ctx *ctx)
{
- PDBG("%s c4iw_dev %p\n", __func__, ctx->dev);
- c4iw_unregister_device(ctx->dev);
c4iw_rdev_close(&ctx->dev->rdev);
idr_destroy(&ctx->dev->cqidr);
idr_destroy(&ctx->dev->qpidr);
@@ -389,11 +387,30 @@ static void c4iw_remove(struct uld_ctx *ctx)
ctx->dev = NULL;
}
+static void c4iw_remove(struct uld_ctx *ctx)
+{
+ PDBG("%s c4iw_dev %p\n", __func__, ctx->dev);
+ c4iw_unregister_device(ctx->dev);
+ c4iw_dealloc(ctx);
+}
+
+static int rdma_supported(const struct cxgb4_lld_info *infop)
+{
+ return infop->vr->stag.size > 0 && infop->vr->pbl.size > 0 &&
+ infop->vr->rq.size > 0 && infop->vr->qp.size > 0 &&
+ infop->vr->cq.size > 0 && infop->vr->ocq.size > 0;
+}
+
static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
{
struct c4iw_dev *devp;
int ret;
+ if (!rdma_supported(infop)) {
+ printk(KERN_INFO MOD "%s: RDMA not supported on this device.\n",
+ pci_name(infop->pdev));
+ return ERR_PTR(-ENOSYS);
+ }
devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
if (!devp) {
printk(KERN_ERR MOD "Cannot allocate ib device\n");
@@ -414,7 +431,6 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
ret = c4iw_rdev_open(&devp->rdev);
if (ret) {
- mutex_unlock(&dev_mutex);
printk(KERN_ERR MOD "Unable to open CXIO rdev err %d\n", ret);
ib_dealloc_device(&devp->ibdev);
return ERR_PTR(ret);
@@ -519,15 +535,24 @@ static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state)
case CXGB4_STATE_UP:
printk(KERN_INFO MOD "%s: Up\n", pci_name(ctx->lldi.pdev));
if (!ctx->dev) {
- int ret = 0;
+ int ret;
ctx->dev = c4iw_alloc(&ctx->lldi);
- if (!IS_ERR(ctx->dev))
- ret = c4iw_register_device(ctx->dev);
- if (IS_ERR(ctx->dev) || ret)
+ if (IS_ERR(ctx->dev)) {
+ printk(KERN_ERR MOD
+ "%s: initialization failed: %ld\n",
+ pci_name(ctx->lldi.pdev),
+ PTR_ERR(ctx->dev));
+ ctx->dev = NULL;
+ break;
+ }
+ ret = c4iw_register_device(ctx->dev);
+ if (ret) {
printk(KERN_ERR MOD
"%s: RDMA registration failed: %d\n",
pci_name(ctx->lldi.pdev), ret);
+ c4iw_dealloc(ctx);
+ }
}
break;
case CXGB4_STATE_DOWN:
diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c
index c13041a0aeba..397cb36cf103 100644
--- a/drivers/infiniband/hw/cxgb4/ev.c
+++ b/drivers/infiniband/hw/cxgb4/ev.c
@@ -42,6 +42,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
{
struct ib_event event;
struct c4iw_qp_attributes attrs;
+ unsigned long flag;
if ((qhp->attr.state == C4IW_QP_STATE_ERROR) ||
(qhp->attr.state == C4IW_QP_STATE_TERMINATE)) {
@@ -72,7 +73,9 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
if (qhp->ibqp.event_handler)
(*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
}
void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe)
@@ -183,11 +186,14 @@ out:
int c4iw_ev_handler(struct c4iw_dev *dev, u32 qid)
{
struct c4iw_cq *chp;
+ unsigned long flag;
chp = get_chp(dev, qid);
- if (chp)
+ if (chp) {
+ spin_lock_irqsave(&chp->comp_handler_lock, flag);
(*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
- else
+ spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
+ } else
PDBG("%s unknown cqid 0x%x\n", __func__, qid);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 4f045375c8e2..1357c5bf209b 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -309,6 +309,7 @@ struct c4iw_cq {
struct c4iw_dev *rhp;
struct t4_cq cq;
spinlock_t lock;
+ spinlock_t comp_handler_lock;
atomic_t refcnt;
wait_queue_head_t wait;
};
@@ -323,6 +324,7 @@ struct c4iw_mpa_attributes {
u8 recv_marker_enabled;
u8 xmit_marker_enabled;
u8 crc_enabled;
+ u8 enhanced_rdma_conn;
u8 version;
u8 p2p_type;
};
@@ -349,6 +351,8 @@ struct c4iw_qp_attributes {
u8 is_terminate_local;
struct c4iw_mpa_attributes mpa_attr;
struct c4iw_ep *llp_stream_handle;
+ u8 layer_etype;
+ u8 ecode;
};
struct c4iw_qp {
@@ -501,11 +505,18 @@ enum c4iw_mmid_state {
#define MPA_KEY_REP "MPA ID Rep Frame"
#define MPA_MAX_PRIVATE_DATA 256
+#define MPA_ENHANCED_RDMA_CONN 0x10
#define MPA_REJECT 0x20
#define MPA_CRC 0x40
#define MPA_MARKERS 0x80
#define MPA_FLAGS_MASK 0xE0
+#define MPA_V2_PEER2PEER_MODEL 0x8000
+#define MPA_V2_ZERO_LEN_FPDU_RTR 0x4000
+#define MPA_V2_RDMA_WRITE_RTR 0x8000
+#define MPA_V2_RDMA_READ_RTR 0x4000
+#define MPA_V2_IRD_ORD_MASK 0x3FFF
+
#define c4iw_put_ep(ep) { \
PDBG("put_ep (via %s:%u) ep %p refcnt %d\n", __func__, __LINE__, \
ep, atomic_read(&((ep)->kref.refcount))); \
@@ -528,6 +539,11 @@ struct mpa_message {
u8 private_data[0];
};
+struct mpa_v2_conn_params {
+ __be16 ird;
+ __be16 ord;
+};
+
struct terminate_message {
u8 layer_etype;
u8 ecode;
@@ -580,7 +596,10 @@ enum c4iw_ddp_ecodes {
enum c4iw_mpa_ecodes {
MPA_CRC_ERR = 0x02,
- MPA_MARKER_ERR = 0x03
+ MPA_MARKER_ERR = 0x03,
+ MPA_LOCAL_CATA = 0x05,
+ MPA_INSUFF_IRD = 0x06,
+ MPA_NOMATCH_RTR = 0x07,
};
enum c4iw_ep_state {
@@ -651,6 +670,8 @@ struct c4iw_ep {
u16 txq_idx;
u16 ctrlq_idx;
u8 tos;
+ u8 retry_with_mpa_v1;
+ u8 tried_with_mpa_v1;
};
static inline struct c4iw_ep *to_ep(struct iw_cm_id *cm_id)
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index a41578e48c7b..d6ccc7e84802 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -917,7 +917,11 @@ static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe,
wqe->u.terminate.type = FW_RI_TYPE_TERMINATE;
wqe->u.terminate.immdlen = cpu_to_be32(sizeof *term);
term = (struct terminate_message *)wqe->u.terminate.termmsg;
- build_term_codes(err_cqe, &term->layer_etype, &term->ecode);
+ if (qhp->attr.layer_etype == (LAYER_MPA|DDP_LLP)) {
+ term->layer_etype = qhp->attr.layer_etype;
+ term->ecode = qhp->attr.ecode;
+ } else
+ build_term_codes(err_cqe, &term->layer_etype, &term->ecode);
c4iw_ofld_send(&qhp->rhp->rdev, skb);
}
@@ -941,8 +945,11 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&rchp->lock, flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&rchp->comp_handler_lock, flag);
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
+ }
/* locking hierarchy: cq lock first, then qp lock. */
spin_lock_irqsave(&schp->lock, flag);
@@ -952,13 +959,17 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&schp->lock, flag);
- if (flushed)
+ if (flushed) {
+ spin_lock_irqsave(&schp->comp_handler_lock, flag);
(*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
+ }
}
static void flush_qp(struct c4iw_qp *qhp)
{
struct c4iw_cq *rchp, *schp;
+ unsigned long flag;
rchp = get_chp(qhp->rhp, qhp->attr.rcq);
schp = get_chp(qhp->rhp, qhp->attr.scq);
@@ -966,8 +977,16 @@ static void flush_qp(struct c4iw_qp *qhp)
if (qhp->ibqp.uobject) {
t4_set_wq_in_error(&qhp->wq);
t4_set_cq_in_error(&rchp->cq);
- if (schp != rchp)
+ spin_lock_irqsave(&rchp->comp_handler_lock, flag);
+ (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
+ if (schp != rchp) {
t4_set_cq_in_error(&schp->cq);
+ spin_lock_irqsave(&schp->comp_handler_lock, flag);
+ (*schp->ibcq.comp_handler)(&schp->ibcq,
+ schp->ibcq.cq_context);
+ spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
+ }
return;
}
__flush_qp(qhp, rchp, schp);
@@ -1012,6 +1031,7 @@ out:
static void build_rtr_msg(u8 p2p_type, struct fw_ri_init *init)
{
+ PDBG("%s p2p_type = %d\n", __func__, p2p_type);
memset(&init->u, 0, sizeof init->u);
switch (p2p_type) {
case FW_RI_INIT_P2PTYPE_RDMA_WRITE:
@@ -1206,12 +1226,16 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
disconnect = 1;
c4iw_get_ep(&qhp->ep->com);
}
+ if (qhp->ibqp.uobject)
+ t4_set_wq_in_error(&qhp->wq);
ret = rdma_fini(rhp, qhp, ep);
if (ret)
goto err;
break;
case C4IW_QP_STATE_TERMINATE:
set_state(qhp, C4IW_QP_STATE_TERMINATE);
+ qhp->attr.layer_etype = attrs->layer_etype;
+ qhp->attr.ecode = attrs->ecode;
if (qhp->ibqp.uobject)
t4_set_wq_in_error(&qhp->wq);
ep = qhp->ep;
@@ -1222,6 +1246,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
break;
case C4IW_QP_STATE_ERROR:
set_state(qhp, C4IW_QP_STATE_ERROR);
+ if (qhp->ibqp.uobject)
+ t4_set_wq_in_error(&qhp->wq);
if (!internal) {
abort = 1;
disconnect = 1;
@@ -1334,7 +1360,10 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
rhp = qhp->rhp;
attrs.next_state = C4IW_QP_STATE_ERROR;
- c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
+ if (qhp->attr.state == C4IW_QP_STATE_TERMINATE)
+ c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ else
+ c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
wait_event(qhp->wait, !qhp->ep);
remove_handle(rhp, &rhp->qpidr, qhp->wq.sq.qid);
diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c
index d9b1bb40f480..818d721fc448 100644
--- a/drivers/infiniband/hw/ehca/ehca_eq.c
+++ b/drivers/infiniband/hw/ehca/ehca_eq.c
@@ -125,7 +125,7 @@ int ehca_create_eq(struct ehca_shca *shca,
tasklet_init(&eq->interrupt_task, ehca_tasklet_eq, (long)shca);
ret = ibmebus_request_irq(eq->ist, ehca_interrupt_eq,
- IRQF_DISABLED, "ehca_eq",
+ 0, "ehca_eq",
(void *)shca);
if (ret < 0)
ehca_err(ib_dev, "Can't map interrupt handler.");
@@ -133,7 +133,7 @@ int ehca_create_eq(struct ehca_shca *shca,
tasklet_init(&eq->interrupt_task, ehca_tasklet_neq, (long)shca);
ret = ibmebus_request_irq(eq->ist, ehca_interrupt_neq,
- IRQF_DISABLED, "ehca_neq",
+ 0, "ehca_neq",
(void *)shca);
if (ret < 0)
ehca_err(ib_dev, "Can't map interrupt handler.");
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index 32fb34201aba..964f85520798 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -977,6 +977,9 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
struct hcp_modify_qp_control_block *mqpcb;
u64 hret, update_mask;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC)
+ return ERR_PTR(-ENOSYS);
+
/* For common attributes, internal_create_qp() takes its info
* out of qp_init_attr, so copy all common attrs there.
*/
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 7c1eebe8c7c9..824a4d508836 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -34,6 +34,7 @@
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/vmalloc.h>
#include "ipath_kernel.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c
index 386e2c717c53..26271984b717 100644
--- a/drivers/infiniband/hw/ipath/ipath_srq.c
+++ b/drivers/infiniband/hw/ipath/ipath_srq.c
@@ -107,6 +107,11 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
+ ret = ERR_PTR(-ENOSYS);
+ goto done;
+ }
+
if (srq_init_attr->attr.max_wr == 0) {
ret = ERR_PTR(-EINVAL);
goto done;
diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c
index cfed5399f074..dc66c4506916 100644
--- a/drivers/infiniband/hw/ipath/ipath_user_pages.c
+++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c
@@ -79,7 +79,7 @@ static int __ipath_get_user_pages(unsigned long start_page, size_t num_pages,
goto bail_release;
}
- current->mm->locked_vm += num_pages;
+ current->mm->pinned_vm += num_pages;
ret = 0;
goto bail;
@@ -178,7 +178,7 @@ void ipath_release_user_pages(struct page **p, size_t num_pages)
__ipath_release_user_pages(p, num_pages, 1);
- current->mm->locked_vm -= num_pages;
+ current->mm->pinned_vm -= num_pages;
up_write(&current->mm->mmap_sem);
}
@@ -195,7 +195,7 @@ static void user_pages_account(struct work_struct *_work)
container_of(_work, struct ipath_user_pages_work, work);
down_write(&work->mm->mmap_sem);
- work->mm->locked_vm -= work->num_pages;
+ work->mm->pinned_vm -= work->num_pages;
up_write(&work->mm->mmap_sem);
mmput(work->mm);
kfree(work);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index fa643f4f4e28..77f3dbc0aaa1 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -128,6 +128,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
(dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) &&
(dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR))
props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
+ if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC)
+ props->device_cap_flags |= IB_DEVICE_XRC;
props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) &
0xffffff;
@@ -181,8 +183,12 @@ mlx4_ib_port_link_layer(struct ib_device *device, u8 port_num)
static int ib_link_query_port(struct ib_device *ibdev, u8 port,
struct ib_port_attr *props,
+ struct ib_smp *in_mad,
struct ib_smp *out_mad)
{
+ int ext_active_speed;
+ int err;
+
props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16));
props->lmc = out_mad->data[34] & 0x7;
props->sm_lid = be16_to_cpup((__be16 *) (out_mad->data + 18));
@@ -203,6 +209,39 @@ static int ib_link_query_port(struct ib_device *ibdev, u8 port,
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = out_mad->data[41] >> 4;
+ /* Check if extended speeds (EDR/FDR/...) are supported */
+ if (props->port_cap_flags & IB_PORT_EXTENDED_SPEEDS_SUP) {
+ ext_active_speed = out_mad->data[62] >> 4;
+
+ switch (ext_active_speed) {
+ case 1:
+ props->active_speed = 16; /* FDR */
+ break;
+ case 2:
+ props->active_speed = 32; /* EDR */
+ break;
+ }
+ }
+
+ /* If reported active speed is QDR, check if is FDR-10 */
+ if (props->active_speed == 4) {
+ if (to_mdev(ibdev)->dev->caps.ext_port_cap[port] &
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) {
+ init_query_mad(in_mad);
+ in_mad->attr_id = MLX4_ATTR_EXTENDED_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port,
+ NULL, NULL, in_mad, out_mad);
+ if (err)
+ return err;
+
+ /* Checking LinkSpeedActive for FDR-10 */
+ if (out_mad->data[15] & 0x1)
+ props->active_speed = 8;
+ }
+ }
+
return 0;
}
@@ -227,7 +266,7 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port,
props->pkey_tbl_len = 1;
props->bad_pkey_cntr = be16_to_cpup((__be16 *) (out_mad->data + 46));
props->qkey_viol_cntr = be16_to_cpup((__be16 *) (out_mad->data + 48));
- props->max_mtu = IB_MTU_2048;
+ props->max_mtu = IB_MTU_4096;
props->subnet_timeout = 0;
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = 0;
@@ -274,7 +313,7 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port,
goto out;
err = mlx4_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND ?
- ib_link_query_port(ibdev, port, props, out_mad) :
+ ib_link_query_port(ibdev, port, props, in_mad, out_mad) :
eth_link_query_port(ibdev, port, props, out_mad);
out:
@@ -566,6 +605,57 @@ static int mlx4_ib_dealloc_pd(struct ib_pd *pd)
return 0;
}
+static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ struct mlx4_ib_xrcd *xrcd;
+ int err;
+
+ if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
+ return ERR_PTR(-ENOSYS);
+
+ xrcd = kmalloc(sizeof *xrcd, GFP_KERNEL);
+ if (!xrcd)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlx4_xrcd_alloc(to_mdev(ibdev)->dev, &xrcd->xrcdn);
+ if (err)
+ goto err1;
+
+ xrcd->pd = ib_alloc_pd(ibdev);
+ if (IS_ERR(xrcd->pd)) {
+ err = PTR_ERR(xrcd->pd);
+ goto err2;
+ }
+
+ xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, 1, 0);
+ if (IS_ERR(xrcd->cq)) {
+ err = PTR_ERR(xrcd->cq);
+ goto err3;
+ }
+
+ return &xrcd->ibxrcd;
+
+err3:
+ ib_dealloc_pd(xrcd->pd);
+err2:
+ mlx4_xrcd_free(to_mdev(ibdev)->dev, xrcd->xrcdn);
+err1:
+ kfree(xrcd);
+ return ERR_PTR(err);
+}
+
+static int mlx4_ib_dealloc_xrcd(struct ib_xrcd *xrcd)
+{
+ ib_destroy_cq(to_mxrcd(xrcd)->cq);
+ ib_dealloc_pd(to_mxrcd(xrcd)->pd);
+ mlx4_xrcd_free(to_mdev(xrcd->device)->dev, to_mxrcd(xrcd)->xrcdn);
+ kfree(xrcd);
+
+ return 0;
+}
+
static int add_gid_entry(struct ib_qp *ibqp, union ib_gid *gid)
{
struct mlx4_ib_qp *mqp = to_mqp(ibqp);
@@ -1044,7 +1134,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
(1ull << IB_USER_VERBS_CMD_CREATE_SRQ) |
(1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) |
(1ull << IB_USER_VERBS_CMD_QUERY_SRQ) |
- (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ);
+ (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_XSRQ) |
+ (1ull << IB_USER_VERBS_CMD_OPEN_QP);
ibdev->ib_dev.query_device = mlx4_ib_query_device;
ibdev->ib_dev.query_port = mlx4_ib_query_port;
@@ -1093,6 +1185,14 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.unmap_fmr = mlx4_ib_unmap_fmr;
ibdev->ib_dev.dealloc_fmr = mlx4_ib_fmr_dealloc;
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) {
+ ibdev->ib_dev.alloc_xrcd = mlx4_ib_alloc_xrcd;
+ ibdev->ib_dev.dealloc_xrcd = mlx4_ib_dealloc_xrcd;
+ ibdev->ib_dev.uverbs_cmd_mask |=
+ (1ull << IB_USER_VERBS_CMD_OPEN_XRCD) |
+ (1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
+ }
+
spin_lock_init(&iboe->lock);
if (init_node_data(ibdev))
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index e4bf2cff8662..ed80345c99ae 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -56,6 +56,13 @@ struct mlx4_ib_pd {
u32 pdn;
};
+struct mlx4_ib_xrcd {
+ struct ib_xrcd ibxrcd;
+ u32 xrcdn;
+ struct ib_pd *pd;
+ struct ib_cq *cq;
+};
+
struct mlx4_ib_cq_buf {
struct mlx4_buf buf;
struct mlx4_mtt mtt;
@@ -138,6 +145,7 @@ struct mlx4_ib_qp {
struct mlx4_mtt mtt;
int buf_size;
struct mutex mutex;
+ u16 xrcdn;
u32 flags;
u8 port;
u8 alt_port;
@@ -211,6 +219,11 @@ static inline struct mlx4_ib_pd *to_mpd(struct ib_pd *ibpd)
return container_of(ibpd, struct mlx4_ib_pd, ibpd);
}
+static inline struct mlx4_ib_xrcd *to_mxrcd(struct ib_xrcd *ibxrcd)
+{
+ return container_of(ibxrcd, struct mlx4_ib_xrcd, ibxrcd);
+}
+
static inline struct mlx4_ib_cq *to_mcq(struct ib_cq *ibcq)
{
return container_of(ibcq, struct mlx4_ib_cq, ibcq);
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 3a91d9d8dc51..a16f0c8e6f3f 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -302,15 +302,14 @@ static int send_wqe_overhead(enum ib_qp_type type, u32 flags)
}
static int set_rq_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap,
- int is_user, int has_srq, struct mlx4_ib_qp *qp)
+ int is_user, int has_rq, struct mlx4_ib_qp *qp)
{
/* Sanity check RQ size before proceeding */
if (cap->max_recv_wr > dev->dev->caps.max_wqes ||
cap->max_recv_sge > dev->dev->caps.max_rq_sg)
return -EINVAL;
- if (has_srq) {
- /* QPs attached to an SRQ should have no RQ */
+ if (!has_rq) {
if (cap->max_recv_wr)
return -EINVAL;
@@ -463,6 +462,14 @@ static int set_user_sq_size(struct mlx4_ib_dev *dev,
return 0;
}
+static int qp_has_rq(struct ib_qp_init_attr *attr)
+{
+ if (attr->qp_type == IB_QPT_XRC_INI || attr->qp_type == IB_QPT_XRC_TGT)
+ return 0;
+
+ return !attr->srq;
+}
+
static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata, int sqpn, struct mlx4_ib_qp *qp)
@@ -479,7 +486,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
qp->sq_signal_bits = cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE);
- err = set_rq_size(dev, &init_attr->cap, !!pd->uobject, !!init_attr->srq, qp);
+ err = set_rq_size(dev, &init_attr->cap, !!pd->uobject, qp_has_rq(init_attr), qp);
if (err)
goto err;
@@ -513,7 +520,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err_mtt;
- if (!init_attr->srq) {
+ if (qp_has_rq(init_attr)) {
err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context),
ucmd.db_addr, &qp->db);
if (err)
@@ -532,7 +539,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err;
- if (!init_attr->srq) {
+ if (qp_has_rq(init_attr)) {
err = mlx4_db_alloc(dev->dev, &qp->db, 0);
if (err)
goto err;
@@ -575,6 +582,9 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err_qpn;
+ if (init_attr->qp_type == IB_QPT_XRC_TGT)
+ qp->mqp.qpn |= (1 << 23);
+
/*
* Hardware wants QPN written in big-endian order (after
* shifting) for send doorbell. Precompute this value to save
@@ -592,9 +602,8 @@ err_qpn:
err_wrid:
if (pd->uobject) {
- if (!init_attr->srq)
- mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context),
- &qp->db);
+ if (qp_has_rq(init_attr))
+ mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db);
} else {
kfree(qp->sq.wrid);
kfree(qp->rq.wrid);
@@ -610,7 +619,7 @@ err_buf:
mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf);
err_db:
- if (!pd->uobject && !init_attr->srq)
+ if (!pd->uobject && qp_has_rq(init_attr))
mlx4_db_free(dev->dev, &qp->db);
err:
@@ -671,6 +680,33 @@ static void del_gid_entries(struct mlx4_ib_qp *qp)
}
}
+static struct mlx4_ib_pd *get_pd(struct mlx4_ib_qp *qp)
+{
+ if (qp->ibqp.qp_type == IB_QPT_XRC_TGT)
+ return to_mpd(to_mxrcd(qp->ibqp.xrcd)->pd);
+ else
+ return to_mpd(qp->ibqp.pd);
+}
+
+static void get_cqs(struct mlx4_ib_qp *qp,
+ struct mlx4_ib_cq **send_cq, struct mlx4_ib_cq **recv_cq)
+{
+ switch (qp->ibqp.qp_type) {
+ case IB_QPT_XRC_TGT:
+ *send_cq = to_mcq(to_mxrcd(qp->ibqp.xrcd)->cq);
+ *recv_cq = *send_cq;
+ break;
+ case IB_QPT_XRC_INI:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = *send_cq;
+ break;
+ default:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = to_mcq(qp->ibqp.recv_cq);
+ break;
+ }
+}
+
static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
int is_user)
{
@@ -682,8 +718,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
printk(KERN_WARNING "mlx4_ib: modify QP %06x to RESET failed.\n",
qp->mqp.qpn);
- send_cq = to_mcq(qp->ibqp.send_cq);
- recv_cq = to_mcq(qp->ibqp.recv_cq);
+ get_cqs(qp, &send_cq, &recv_cq);
mlx4_ib_lock_cqs(send_cq, recv_cq);
@@ -706,7 +741,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
mlx4_mtt_cleanup(dev->dev, &qp->mtt);
if (is_user) {
- if (!qp->ibqp.srq)
+ if (qp->rq.wqe_cnt)
mlx4_ib_db_unmap_user(to_mucontext(qp->ibqp.uobject->context),
&qp->db);
ib_umem_release(qp->umem);
@@ -714,7 +749,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
kfree(qp->sq.wrid);
kfree(qp->rq.wrid);
mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf);
- if (!qp->ibqp.srq)
+ if (qp->rq.wqe_cnt)
mlx4_db_free(dev->dev, &qp->db);
}
@@ -725,10 +760,10 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata)
{
- struct mlx4_ib_dev *dev = to_mdev(pd->device);
struct mlx4_ib_sqp *sqp;
struct mlx4_ib_qp *qp;
int err;
+ u16 xrcdn = 0;
/*
* We only support LSO and multicast loopback blocking, and
@@ -739,10 +774,20 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
return ERR_PTR(-EINVAL);
if (init_attr->create_flags &&
- (pd->uobject || init_attr->qp_type != IB_QPT_UD))
+ (udata || init_attr->qp_type != IB_QPT_UD))
return ERR_PTR(-EINVAL);
switch (init_attr->qp_type) {
+ case IB_QPT_XRC_TGT:
+ pd = to_mxrcd(init_attr->xrcd)->pd;
+ xrcdn = to_mxrcd(init_attr->xrcd)->xrcdn;
+ init_attr->send_cq = to_mxrcd(init_attr->xrcd)->cq;
+ /* fall through */
+ case IB_QPT_XRC_INI:
+ if (!(to_mdev(pd->device)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
+ return ERR_PTR(-ENOSYS);
+ init_attr->recv_cq = init_attr->send_cq;
+ /* fall through */
case IB_QPT_RC:
case IB_QPT_UC:
case IB_QPT_UD:
@@ -751,13 +796,14 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
if (!qp)
return ERR_PTR(-ENOMEM);
- err = create_qp_common(dev, pd, init_attr, udata, 0, qp);
+ err = create_qp_common(to_mdev(pd->device), pd, init_attr, udata, 0, qp);
if (err) {
kfree(qp);
return ERR_PTR(err);
}
qp->ibqp.qp_num = qp->mqp.qpn;
+ qp->xrcdn = xrcdn;
break;
}
@@ -765,7 +811,7 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
case IB_QPT_GSI:
{
/* Userspace is not allowed to create special QPs: */
- if (pd->uobject)
+ if (udata)
return ERR_PTR(-EINVAL);
sqp = kzalloc(sizeof *sqp, GFP_KERNEL);
@@ -774,8 +820,8 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
qp = &sqp->qp;
- err = create_qp_common(dev, pd, init_attr, udata,
- dev->dev->caps.sqp_start +
+ err = create_qp_common(to_mdev(pd->device), pd, init_attr, udata,
+ to_mdev(pd->device)->dev->caps.sqp_start +
(init_attr->qp_type == IB_QPT_SMI ? 0 : 2) +
init_attr->port_num - 1,
qp);
@@ -801,11 +847,13 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
{
struct mlx4_ib_dev *dev = to_mdev(qp->device);
struct mlx4_ib_qp *mqp = to_mqp(qp);
+ struct mlx4_ib_pd *pd;
if (is_qp0(dev, mqp))
mlx4_CLOSE_PORT(dev->dev, mqp->port);
- destroy_qp_common(dev, mqp, !!qp->pd->uobject);
+ pd = get_pd(mqp);
+ destroy_qp_common(dev, mqp, !!pd->ibpd.uobject);
if (is_sqp(dev, mqp))
kfree(to_msqp(mqp));
@@ -821,6 +869,8 @@ static int to_mlx4_st(enum ib_qp_type type)
case IB_QPT_RC: return MLX4_QP_ST_RC;
case IB_QPT_UC: return MLX4_QP_ST_UC;
case IB_QPT_UD: return MLX4_QP_ST_UD;
+ case IB_QPT_XRC_INI:
+ case IB_QPT_XRC_TGT: return MLX4_QP_ST_XRC;
case IB_QPT_SMI:
case IB_QPT_GSI: return MLX4_QP_ST_MLX;
default: return -1;
@@ -959,6 +1009,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
{
struct mlx4_ib_dev *dev = to_mdev(ibqp->device);
struct mlx4_ib_qp *qp = to_mqp(ibqp);
+ struct mlx4_ib_pd *pd;
+ struct mlx4_ib_cq *send_cq, *recv_cq;
struct mlx4_qp_context *context;
enum mlx4_qp_optpar optpar = 0;
int sqd_event;
@@ -1014,8 +1066,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
context->sq_size_stride = ilog2(qp->sq.wqe_cnt) << 3;
context->sq_size_stride |= qp->sq.wqe_shift - 4;
- if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) {
context->sq_size_stride |= !!qp->sq_no_prefetch << 7;
+ context->xrcd = cpu_to_be32((u32) qp->xrcdn);
+ }
if (qp->ibqp.uobject)
context->usr_page = cpu_to_be32(to_mucontext(ibqp->uobject->context)->uar.index);
@@ -1079,8 +1133,12 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
optpar |= MLX4_QP_OPTPAR_ALT_ADDR_PATH;
}
- context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn);
- context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
+ pd = get_pd(qp);
+ get_cqs(qp, &send_cq, &recv_cq);
+ context->pd = cpu_to_be32(pd->pdn);
+ context->cqn_send = cpu_to_be32(send_cq->mcq.cqn);
+ context->cqn_recv = cpu_to_be32(recv_cq->mcq.cqn);
+ context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
/* Set "fast registration enabled" for all kernel QPs */
if (!qp->ibqp.uobject)
@@ -1106,8 +1164,6 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (attr_mask & IB_QP_SQ_PSN)
context->next_send_psn = cpu_to_be32(attr->sq_psn);
- context->cqn_send = cpu_to_be32(to_mcq(ibqp->send_cq)->mcq.cqn);
-
if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
if (attr->max_dest_rd_atomic)
context->params2 |=
@@ -1130,8 +1186,6 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (attr_mask & IB_QP_RQ_PSN)
context->rnr_nextrecvpsn |= cpu_to_be32(attr->rq_psn);
- context->cqn_recv = cpu_to_be32(to_mcq(ibqp->recv_cq)->mcq.cqn);
-
if (attr_mask & IB_QP_QKEY) {
context->qkey = cpu_to_be32(attr->qkey);
optpar |= MLX4_QP_OPTPAR_Q_KEY;
@@ -1140,7 +1194,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (ibqp->srq)
context->srqn = cpu_to_be32(1 << 24 | to_msrq(ibqp->srq)->msrq.srqn);
- if (!ibqp->srq && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ if (qp->rq.wqe_cnt && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
context->db_rec_addr = cpu_to_be64(qp->db.dma);
if (cur_state == IB_QPS_INIT &&
@@ -1225,17 +1279,17 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
* entries and reinitialize the QP.
*/
if (new_state == IB_QPS_RESET && !ibqp->uobject) {
- mlx4_ib_cq_clean(to_mcq(ibqp->recv_cq), qp->mqp.qpn,
+ mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn,
ibqp->srq ? to_msrq(ibqp->srq): NULL);
- if (ibqp->send_cq != ibqp->recv_cq)
- mlx4_ib_cq_clean(to_mcq(ibqp->send_cq), qp->mqp.qpn, NULL);
+ if (send_cq != recv_cq)
+ mlx4_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
qp->rq.head = 0;
qp->rq.tail = 0;
qp->sq.head = 0;
qp->sq.tail = 0;
qp->sq_next_wqe = 0;
- if (!ibqp->srq)
+ if (qp->rq.wqe_cnt)
*qp->db.db = 0;
}
@@ -1547,14 +1601,13 @@ static void set_masked_atomic_seg(struct mlx4_wqe_masked_atomic_seg *aseg,
}
static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg,
- struct ib_send_wr *wr, __be16 *vlan)
+ struct ib_send_wr *wr)
{
memcpy(dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av));
dseg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn);
dseg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey);
dseg->vlan = to_mah(wr->wr.ud.ah)->av.eth.vlan;
memcpy(dseg->mac, to_mah(wr->wr.ud.ah)->av.eth.mac, 6);
- *vlan = dseg->vlan;
}
static void set_mlx_icrc_seg(void *dseg)
@@ -1657,7 +1710,6 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
__be32 uninitialized_var(lso_hdr_sz);
__be32 blh;
int i;
- __be16 vlan = cpu_to_be16(0xffff);
spin_lock_irqsave(&qp->sq.lock, flags);
@@ -1761,7 +1813,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
break;
case IB_QPT_UD:
- set_datagram_seg(wqe, wr, &vlan);
+ set_datagram_seg(wqe, wr);
wqe += sizeof (struct mlx4_wqe_datagram_seg);
size += sizeof (struct mlx4_wqe_datagram_seg) / 16;
@@ -1824,11 +1876,6 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
ctrl->fence_size = (wr->send_flags & IB_SEND_FENCE ?
MLX4_WQE_CTRL_FENCE : 0) | size;
- if (be16_to_cpu(vlan) < 0x1000) {
- ctrl->ins_vlan = 1 << 6;
- ctrl->vlan_tag = vlan;
- }
-
/*
* Make sure descriptor is fully written before
* setting ownership bit (because HW can start
diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c
index 818b7ecace5e..39542f3703b8 100644
--- a/drivers/infiniband/hw/mlx4/srq.c
+++ b/drivers/infiniband/hw/mlx4/srq.c
@@ -76,6 +76,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
struct mlx4_ib_srq *srq;
struct mlx4_wqe_srq_next_seg *next;
struct mlx4_wqe_data_seg *scatter;
+ u32 cqn;
+ u16 xrcdn;
int desc_size;
int buf_size;
int err;
@@ -174,12 +176,18 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
}
}
- err = mlx4_srq_alloc(dev->dev, to_mpd(pd)->pdn, &srq->mtt,
+ cqn = (init_attr->srq_type == IB_SRQT_XRC) ?
+ to_mcq(init_attr->ext.xrc.cq)->mcq.cqn : 0;
+ xrcdn = (init_attr->srq_type == IB_SRQT_XRC) ?
+ to_mxrcd(init_attr->ext.xrc.xrcd)->xrcdn :
+ (u16) dev->dev->caps.reserved_xrcds;
+ err = mlx4_srq_alloc(dev->dev, to_mpd(pd)->pdn, cqn, xrcdn, &srq->mtt,
srq->db.dma, &srq->msrq);
if (err)
goto err_wrid;
srq->msrq.event = mlx4_ib_srq_event;
+ srq->ibsrq.ext.xrc.srq_num = srq->msrq.srqn;
if (pd->uobject)
if (ib_copy_to_udata(udata, &srq->msrq.srqn, sizeof (__u32))) {
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index ab876f928a1b..ed9a989e501b 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -146,7 +146,7 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
if (!buddy->bits || !buddy->num_free)
goto err_out;
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 365fe0e14192..cb9a0b976804 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -438,6 +438,9 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd,
struct mthca_srq *srq;
int err;
+ if (init_attr->srq_type != IB_SRQT_BASIC)
+ return ERR_PTR(-ENOSYS);
+
srq = kmalloc(sizeof *srq, GFP_KERNEL);
if (!srq)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/infiniband/hw/nes/Makefile b/drivers/infiniband/hw/nes/Makefile
index 35148513c47e..97820c23ecef 100644
--- a/drivers/infiniband/hw/nes/Makefile
+++ b/drivers/infiniband/hw/nes/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_INFINIBAND_NES) += iw_nes.o
-iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o
+iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o nes_mgt.o
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index 2d668c69f6d9..5965b3df8f2f 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -84,7 +84,7 @@ module_param(send_first, int, 0644);
MODULE_PARM_DESC(send_first, "Send RDMA Message First on Active Connection");
-unsigned int nes_drv_opt = 0;
+unsigned int nes_drv_opt = NES_DRV_OPT_DISABLE_INT_MOD | NES_DRV_OPT_ENABLE_PAU;
module_param(nes_drv_opt, int, 0644);
MODULE_PARM_DESC(nes_drv_opt, "Driver option parameters");
@@ -130,9 +130,6 @@ static struct notifier_block nes_net_notifier = {
.notifier_call = nes_net_event
};
-
-
-
/**
* nes_inetaddr_event
*/
@@ -321,6 +318,9 @@ void nes_rem_ref(struct ib_qp *ibqp)
}
if (atomic_dec_and_test(&nesqp->refcount)) {
+ if (nesqp->pau_mode)
+ nes_destroy_pau_qp(nesdev, nesqp);
+
/* Destroy the QP */
cqp_request = nes_get_cqp_request(nesdev);
if (cqp_request == NULL) {
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
index 6fe79876009e..568b4f11380a 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -102,6 +102,7 @@
#define NES_DRV_OPT_NO_INLINE_DATA 0x00000080
#define NES_DRV_OPT_DISABLE_INT_MOD 0x00000100
#define NES_DRV_OPT_DISABLE_VIRT_WQ 0x00000200
+#define NES_DRV_OPT_ENABLE_PAU 0x00000400
#define NES_AEQ_EVENT_TIMEOUT 2500
#define NES_DISCONNECT_EVENT_TIMEOUT 2000
@@ -128,6 +129,7 @@
#define NES_DBG_IW_RX 0x00020000
#define NES_DBG_IW_TX 0x00040000
#define NES_DBG_SHUTDOWN 0x00080000
+#define NES_DBG_PAU 0x00100000
#define NES_DBG_RSVD1 0x10000000
#define NES_DBG_RSVD2 0x20000000
#define NES_DBG_RSVD3 0x40000000
@@ -162,6 +164,7 @@ do { \
#include "nes_context.h"
#include "nes_user.h"
#include "nes_cm.h"
+#include "nes_mgt.h"
extern int max_mtu;
#define max_frame_len (max_mtu+ETH_HLEN)
@@ -202,6 +205,8 @@ extern atomic_t cm_nodes_created;
extern atomic_t cm_nodes_destroyed;
extern atomic_t cm_accel_dropped_pkts;
extern atomic_t cm_resets_recvd;
+extern atomic_t pau_qps_created;
+extern atomic_t pau_qps_destroyed;
extern u32 int_mod_timer_init;
extern u32 int_mod_cq_depth_256;
@@ -273,6 +278,14 @@ struct nes_device {
u8 link_recheck;
};
+/* Receive skb private area - must fit in skb->cb area */
+struct nes_rskb_cb {
+ u64 busaddr;
+ u32 maplen;
+ u32 seqnum;
+ u8 *data_start;
+ struct nes_qp *nesqp;
+};
static inline __le32 get_crc_value(struct nes_v4_quad *nes_quad)
{
@@ -305,8 +318,8 @@ set_wqe_32bit_value(__le32 *wqe_words, u32 index, u32 value)
static inline void
nes_fill_init_cqp_wqe(struct nes_hw_cqp_wqe *cqp_wqe, struct nes_device *nesdev)
{
- set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_CTX_LOW_IDX,
- (u64)((unsigned long) &nesdev->cqp));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX] = 0;
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index c118663e4437..dfce9ea98a39 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -77,26 +77,19 @@ atomic_t cm_nodes_destroyed;
atomic_t cm_accel_dropped_pkts;
atomic_t cm_resets_recvd;
-static inline int mini_cm_accelerated(struct nes_cm_core *,
- struct nes_cm_node *);
-static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *,
- struct nes_vnic *, struct nes_cm_info *);
+static inline int mini_cm_accelerated(struct nes_cm_core *, struct nes_cm_node *);
+static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *, struct nes_vnic *, struct nes_cm_info *);
static int mini_cm_del_listen(struct nes_cm_core *, struct nes_cm_listener *);
-static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *,
- struct nes_vnic *, u16, void *, struct nes_cm_info *);
+static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *, struct nes_vnic *, u16, void *, struct nes_cm_info *);
static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *);
-static int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
-static int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
-static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
- struct sk_buff *);
+static int mini_cm_accept(struct nes_cm_core *, struct nes_cm_node *);
+static int mini_cm_reject(struct nes_cm_core *, struct nes_cm_node *);
+static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *, struct sk_buff *);
static int mini_cm_dealloc_core(struct nes_cm_core *);
static int mini_cm_get(struct nes_cm_core *);
static int mini_cm_set(struct nes_cm_core *, u32, u32);
-static void form_cm_frame(struct sk_buff *, struct nes_cm_node *,
- void *, u32, void *, u32, u8);
+static void form_cm_frame(struct sk_buff *, struct nes_cm_node *, void *, u32, void *, u32, u8);
static int add_ref_cm_node(struct nes_cm_node *);
static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
@@ -111,16 +104,14 @@ static int send_syn(struct nes_cm_node *, u32, struct sk_buff *);
static int send_reset(struct nes_cm_node *, struct sk_buff *);
static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb);
static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb);
-static void process_packet(struct nes_cm_node *, struct sk_buff *,
- struct nes_cm_core *);
+static void process_packet(struct nes_cm_node *, struct sk_buff *, struct nes_cm_core *);
static void active_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void passive_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void cleanup_retrans_entry(struct nes_cm_node *);
static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *);
static void free_retrans_entry(struct nes_cm_node *cm_node);
-static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb, int optionsize, int passive);
+static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph, struct sk_buff *skb, int optionsize, int passive);
/* CM event handler functions */
static void cm_event_connected(struct nes_cm_event *);
@@ -130,6 +121,12 @@ static void cm_event_mpa_req(struct nes_cm_event *);
static void cm_event_mpa_reject(struct nes_cm_event *);
static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node);
+/* MPA build functions */
+static int cm_build_mpa_frame(struct nes_cm_node *, u8 **, u16 *, u8 *, u8);
+static void build_mpa_v2(struct nes_cm_node *, void *, u8);
+static void build_mpa_v1(struct nes_cm_node *, void *, u8);
+static void build_rdma0_msg(struct nes_cm_node *, struct nes_qp **);
+
static void print_core(struct nes_cm_core *core);
/* External CM API Interface */
@@ -159,12 +156,21 @@ atomic_t cm_connecteds;
atomic_t cm_connect_reqs;
atomic_t cm_rejects;
+int nes_add_ref_cm_node(struct nes_cm_node *cm_node)
+{
+ return add_ref_cm_node(cm_node);
+}
+
+int nes_rem_ref_cm_node(struct nes_cm_node *cm_node)
+{
+ return rem_ref_cm_node(cm_node->cm_core, cm_node);
+}
/**
* create_event
*/
-static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
- enum nes_cm_event_type type)
+static struct nes_cm_event *create_event(struct nes_cm_node * cm_node,
+ enum nes_cm_event_type type)
{
struct nes_cm_event *event;
@@ -186,10 +192,10 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
event->cm_info.cm_id = cm_node->cm_id;
nes_debug(NES_DBG_CM, "cm_node=%p Created event=%p, type=%u, "
- "dst_addr=%08x[%x], src_addr=%08x[%x]\n",
- cm_node, event, type, event->cm_info.loc_addr,
- event->cm_info.loc_port, event->cm_info.rem_addr,
- event->cm_info.rem_port);
+ "dst_addr=%08x[%x], src_addr=%08x[%x]\n",
+ cm_node, event, type, event->cm_info.loc_addr,
+ event->cm_info.loc_port, event->cm_info.rem_addr,
+ event->cm_info.rem_port);
nes_cm_post_event(event);
return event;
@@ -201,14 +207,19 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
*/
static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
+
if (!skb) {
nes_debug(NES_DBG_CM, "skb set to NULL\n");
return -1;
}
/* send an MPA Request frame */
- form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
- cm_node->mpa_frame_size, SET_ACK);
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REQUEST);
+ form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK);
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
}
@@ -217,7 +228,11 @@ static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
static int send_mpa_reject(struct nes_cm_node *cm_node)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb = NULL;
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
@@ -226,8 +241,8 @@ static int send_mpa_reject(struct nes_cm_node *cm_node)
}
/* send an MPA reject frame */
- form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
- cm_node->mpa_frame_size, SET_ACK | SET_FIN);
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REPLY);
+ form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK | SET_FIN);
cm_node->state = NES_CM_STATE_FIN_WAIT1;
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
@@ -239,24 +254,31 @@ static int send_mpa_reject(struct nes_cm_node *cm_node)
* IETF MPA frame
*/
static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
- u32 len)
+ u32 len)
{
- struct ietf_mpa_frame *mpa_frame;
+ struct ietf_mpa_v1 *mpa_frame;
+ struct ietf_mpa_v2 *mpa_v2_frame;
+ struct ietf_rtr_msg *rtr_msg;
+ int mpa_hdr_len;
+ int priv_data_len;
*type = NES_MPA_REQUEST_ACCEPT;
/* assume req frame is in tcp data payload */
- if (len < sizeof(struct ietf_mpa_frame)) {
+ if (len < sizeof(struct ietf_mpa_v1)) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too small (%x)\n", len);
return -EINVAL;
}
- mpa_frame = (struct ietf_mpa_frame *)buffer;
- cm_node->mpa_frame_size = ntohs(mpa_frame->priv_data_len);
+ /* points to the beginning of the frame, which could be MPA V1 or V2 */
+ mpa_frame = (struct ietf_mpa_v1 *)buffer;
+ mpa_hdr_len = sizeof(struct ietf_mpa_v1);
+ priv_data_len = ntohs(mpa_frame->priv_data_len);
+
/* make sure mpa private data len is less than 512 bytes */
- if (cm_node->mpa_frame_size > IETF_MAX_PRIV_DATA_LEN) {
+ if (priv_data_len > IETF_MAX_PRIV_DATA_LEN) {
nes_debug(NES_DBG_CM, "The received Length of Private"
- " Data field exceeds 512 octets\n");
+ " Data field exceeds 512 octets\n");
return -EINVAL;
}
/*
@@ -264,11 +286,22 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
* received MPA version and MPA key information
*
*/
- if (mpa_frame->rev != mpa_version) {
+ if (mpa_frame->rev != IETF_MPA_V1 && mpa_frame->rev != IETF_MPA_V2) {
+ nes_debug(NES_DBG_CM, "The received mpa version"
+ " is not supported\n");
+ return -EINVAL;
+ }
+ /*
+ * backwards compatibility only
+ */
+ if (mpa_frame->rev > cm_node->mpa_frame_rev) {
nes_debug(NES_DBG_CM, "The received mpa version"
- " can not be interoperated\n");
+ " can not be interoperated\n");
return -EINVAL;
+ } else {
+ cm_node->mpa_frame_rev = mpa_frame->rev;
}
+
if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE)) {
nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
@@ -281,25 +314,75 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
}
}
- if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
+
+ if (priv_data_len + mpa_hdr_len != len) {
nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
- " complete (%x + %x != %x)\n",
- cm_node->mpa_frame_size,
- (u32)sizeof(struct ietf_mpa_frame), len);
+ " complete (%x + %x != %x)\n",
+ priv_data_len, mpa_hdr_len, len);
return -EINVAL;
}
/* make sure it does not exceed the max size */
if (len > MAX_CM_BUFFER) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too large"
- " (%x + %x != %x)\n",
- cm_node->mpa_frame_size,
- (u32)sizeof(struct ietf_mpa_frame), len);
+ " (%x + %x != %x)\n",
+ priv_data_len, mpa_hdr_len, len);
return -EINVAL;
}
+ cm_node->mpa_frame_size = priv_data_len;
+
+ switch (mpa_frame->rev) {
+ case IETF_MPA_V2: {
+ u16 ird_size;
+ u16 ord_size;
+ mpa_v2_frame = (struct ietf_mpa_v2 *)buffer;
+ mpa_hdr_len += IETF_RTR_MSG_SIZE;
+ cm_node->mpa_frame_size -= IETF_RTR_MSG_SIZE;
+ rtr_msg = &mpa_v2_frame->rtr_msg;
+
+ /* parse rtr message */
+ rtr_msg->ctrl_ird = ntohs(rtr_msg->ctrl_ird);
+ rtr_msg->ctrl_ord = ntohs(rtr_msg->ctrl_ord);
+ ird_size = rtr_msg->ctrl_ird & IETF_NO_IRD_ORD;
+ ord_size = rtr_msg->ctrl_ord & IETF_NO_IRD_ORD;
+
+ if (!(rtr_msg->ctrl_ird & IETF_PEER_TO_PEER)) {
+ /* send reset */
+ return -EINVAL;
+ }
+
+ if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+ /* responder */
+ if (cm_node->ord_size > ird_size)
+ cm_node->ord_size = ird_size;
+ } else {
+ /* initiator */
+ if (cm_node->ord_size > ird_size)
+ cm_node->ord_size = ird_size;
+
+ if (cm_node->ird_size < ord_size) {
+ /* no resources available */
+ /* send terminate message */
+ return -EINVAL;
+ }
+ }
+
+ if (rtr_msg->ctrl_ord & IETF_RDMA0_READ) {
+ cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
+ } else if (rtr_msg->ctrl_ord & IETF_RDMA0_WRITE) {
+ cm_node->send_rdma0_op = SEND_RDMA_WRITE_ZERO;
+ } else { /* Not supported RDMA0 operation */
+ return -EINVAL;
+ }
+ break;
+ }
+ case IETF_MPA_V1:
+ default:
+ break;
+ }
+
/* copy entire MPA frame to our cm_node's frame */
- memcpy(cm_node->mpa_frame_buf, buffer + sizeof(struct ietf_mpa_frame),
- cm_node->mpa_frame_size);
+ memcpy(cm_node->mpa_frame_buf, buffer + mpa_hdr_len, cm_node->mpa_frame_size);
if (mpa_frame->flags & IETF_MPA_FLAGS_REJECT)
*type = NES_MPA_REQUEST_REJECT;
@@ -312,8 +395,8 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
* node info to build.
*/
static void form_cm_frame(struct sk_buff *skb,
- struct nes_cm_node *cm_node, void *options, u32 optionsize,
- void *data, u32 datasize, u8 flags)
+ struct nes_cm_node *cm_node, void *options, u32 optionsize,
+ void *data, u32 datasize, u8 flags)
{
struct tcphdr *tcph;
struct iphdr *iph;
@@ -322,14 +405,14 @@ static void form_cm_frame(struct sk_buff *skb,
u16 packetsize = sizeof(*iph);
packetsize += sizeof(*tcph);
- packetsize += optionsize + datasize;
+ packetsize += optionsize + datasize;
+ skb_trim(skb, 0);
memset(skb->data, 0x00, ETH_HLEN + sizeof(*iph) + sizeof(*tcph));
- skb->len = 0;
buf = skb_put(skb, packetsize + ETH_HLEN);
- ethh = (struct ethhdr *) buf;
+ ethh = (struct ethhdr *)buf;
buf += ETH_HLEN;
iph = (struct iphdr *)buf;
@@ -337,7 +420,7 @@ static void form_cm_frame(struct sk_buff *skb,
tcph = (struct tcphdr *)buf;
skb_reset_mac_header(skb);
skb_set_network_header(skb, ETH_HLEN);
- skb_set_transport_header(skb, ETH_HLEN+sizeof(*iph));
+ skb_set_transport_header(skb, ETH_HLEN + sizeof(*iph));
buf += sizeof(*tcph);
skb->ip_summed = CHECKSUM_PARTIAL;
@@ -350,14 +433,14 @@ static void form_cm_frame(struct sk_buff *skb,
ethh->h_proto = htons(0x0800);
iph->version = IPVERSION;
- iph->ihl = 5; /* 5 * 4Byte words, IP headr len */
+ iph->ihl = 5; /* 5 * 4Byte words, IP headr len */
iph->tos = 0;
iph->tot_len = htons(packetsize);
iph->id = htons(++cm_node->tcp_cntxt.loc_id);
iph->frag_off = htons(0x4000);
iph->ttl = 0x40;
- iph->protocol = 0x06; /* IPPROTO_TCP */
+ iph->protocol = 0x06; /* IPPROTO_TCP */
iph->saddr = htonl(cm_node->loc_addr);
iph->daddr = htonl(cm_node->rem_addr);
@@ -370,14 +453,16 @@ static void form_cm_frame(struct sk_buff *skb,
cm_node->tcp_cntxt.loc_ack_num = cm_node->tcp_cntxt.rcv_nxt;
tcph->ack_seq = htonl(cm_node->tcp_cntxt.loc_ack_num);
tcph->ack = 1;
- } else
+ } else {
tcph->ack_seq = 0;
+ }
if (flags & SET_SYN) {
cm_node->tcp_cntxt.loc_seq_num++;
tcph->syn = 1;
- } else
+ } else {
cm_node->tcp_cntxt.loc_seq_num += datasize;
+ }
if (flags & SET_FIN) {
cm_node->tcp_cntxt.loc_seq_num++;
@@ -398,10 +483,8 @@ static void form_cm_frame(struct sk_buff *skb,
skb_shinfo(skb)->nr_frags = 0;
cm_packets_created++;
-
}
-
/**
* print_core - dump a cm core
*/
@@ -413,7 +496,7 @@ static void print_core(struct nes_cm_core *core)
return;
nes_debug(NES_DBG_CM, "---------------------------------------------\n");
- nes_debug(NES_DBG_CM, "State : %u \n", core->state);
+ nes_debug(NES_DBG_CM, "State : %u \n", core->state);
nes_debug(NES_DBG_CM, "Listen Nodes : %u \n", atomic_read(&core->listen_node_cnt));
nes_debug(NES_DBG_CM, "Active Nodes : %u \n", atomic_read(&core->node_cnt));
@@ -423,6 +506,147 @@ static void print_core(struct nes_cm_core *core)
nes_debug(NES_DBG_CM, "-------------- end core ---------------\n");
}
+/**
+ * cm_build_mpa_frame - build a MPA V1 frame or MPA V2 frame
+ */
+static int cm_build_mpa_frame(struct nes_cm_node *cm_node, u8 **start_buff,
+ u16 *buff_len, u8 *pci_mem, u8 mpa_key)
+{
+ int ret = 0;
+
+ *start_buff = (pci_mem) ? pci_mem : &cm_node->mpa_frame_buf[0];
+
+ switch (cm_node->mpa_frame_rev) {
+ case IETF_MPA_V1:
+ *start_buff = (u8 *)*start_buff + sizeof(struct ietf_rtr_msg);
+ *buff_len = sizeof(struct ietf_mpa_v1) + cm_node->mpa_frame_size;
+ build_mpa_v1(cm_node, *start_buff, mpa_key);
+ break;
+ case IETF_MPA_V2:
+ *buff_len = sizeof(struct ietf_mpa_v2) + cm_node->mpa_frame_size;
+ build_mpa_v2(cm_node, *start_buff, mpa_key);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/**
+ * build_mpa_v2 - build a MPA V2 frame
+ */
+static void build_mpa_v2(struct nes_cm_node *cm_node,
+ void *start_addr, u8 mpa_key)
+{
+ struct ietf_mpa_v2 *mpa_frame = (struct ietf_mpa_v2 *)start_addr;
+ struct ietf_rtr_msg *rtr_msg = &mpa_frame->rtr_msg;
+
+ /* initialize the upper 5 bytes of the frame */
+ build_mpa_v1(cm_node, start_addr, mpa_key);
+ mpa_frame->flags |= IETF_MPA_V2_FLAG; /* set a bit to indicate MPA V2 */
+ mpa_frame->priv_data_len += htons(IETF_RTR_MSG_SIZE);
+
+ /* initialize RTR msg */
+ rtr_msg->ctrl_ird = (cm_node->ird_size > IETF_NO_IRD_ORD) ?
+ IETF_NO_IRD_ORD : cm_node->ird_size;
+ rtr_msg->ctrl_ord = (cm_node->ord_size > IETF_NO_IRD_ORD) ?
+ IETF_NO_IRD_ORD : cm_node->ord_size;
+
+ rtr_msg->ctrl_ird |= IETF_PEER_TO_PEER;
+ rtr_msg->ctrl_ird |= IETF_FLPDU_ZERO_LEN;
+
+ switch (mpa_key) {
+ case MPA_KEY_REQUEST:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_WRITE;
+ rtr_msg->ctrl_ord |= IETF_RDMA0_READ;
+ break;
+ case MPA_KEY_REPLY:
+ switch (cm_node->send_rdma0_op) {
+ case SEND_RDMA_WRITE_ZERO:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_WRITE;
+ break;
+ case SEND_RDMA_READ_ZERO:
+ rtr_msg->ctrl_ord |= IETF_RDMA0_READ;
+ break;
+ }
+ }
+ rtr_msg->ctrl_ird = htons(rtr_msg->ctrl_ird);
+ rtr_msg->ctrl_ord = htons(rtr_msg->ctrl_ord);
+}
+
+/**
+ * build_mpa_v1 - build a MPA V1 frame
+ */
+static void build_mpa_v1(struct nes_cm_node *cm_node, void *start_addr, u8 mpa_key)
+{
+ struct ietf_mpa_v1 *mpa_frame = (struct ietf_mpa_v1 *)start_addr;
+
+ switch (mpa_key) {
+ case MPA_KEY_REQUEST:
+ memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
+ break;
+ case MPA_KEY_REPLY:
+ memcpy(mpa_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
+ break;
+ }
+ mpa_frame->flags = IETF_MPA_FLAGS_CRC;
+ mpa_frame->rev = cm_node->mpa_frame_rev;
+ mpa_frame->priv_data_len = htons(cm_node->mpa_frame_size);
+}
+
+static void build_rdma0_msg(struct nes_cm_node *cm_node, struct nes_qp **nesqp_addr)
+{
+ u64 u64temp;
+ struct nes_qp *nesqp = *nesqp_addr;
+ struct nes_hw_qp_wqe *wqe = &nesqp->hwqp.sq_vbase[0];
+
+ u64temp = (unsigned long)nesqp;
+ u64temp |= NES_SW_CONTEXT_ALIGN >> 1;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
+
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
+
+ switch (cm_node->send_rdma0_op) {
+ case SEND_RDMA_WRITE_ZERO:
+ nes_debug(NES_DBG_CM, "Sending first write.\n");
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+ break;
+
+ case SEND_RDMA_READ_ZERO:
+ default:
+ if (cm_node->send_rdma0_op != SEND_RDMA_READ_ZERO) {
+ printk(KERN_ERR "%s[%u]: Unsupported RDMA0 len operation=%u\n",
+ __func__, __LINE__, cm_node->send_rdma0_op);
+ WARN_ON(1);
+ }
+ nes_debug(NES_DBG_CM, "Sending first rdma operation.\n");
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_IWARP_SQ_OP_RDMAR);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_TO_LOW_IDX] = 1;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_TO_HIGH_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_STAG_IDX] = 1;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 1;
+ break;
+ }
+
+ if (nesqp->sq_kmapped) {
+ nesqp->sq_kmapped = 0;
+ kunmap(nesqp->page);
+ }
+
+ /*use the reserved spot on the WQ for the extra first WQE*/
+ nesqp->nesqp_context->ird_ord_sizes &= cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+ NES_QPCONTEXT_ORDIRD_WRPDU |
+ NES_QPCONTEXT_ORDIRD_ALSMM));
+ nesqp->skip_lsmm = 1;
+ nesqp->hwqp.sq_tail = 0;
+}
/**
* schedule_nes_timer
@@ -430,10 +654,10 @@ static void print_core(struct nes_cm_core *core)
* rem_ref_cm_node(cm_core, cm_node);add_ref_cm_node(cm_node);
*/
int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
- enum nes_timer_type type, int send_retrans,
- int close_when_complete)
+ enum nes_timer_type type, int send_retrans,
+ int close_when_complete)
{
- unsigned long flags;
+ unsigned long flags;
struct nes_cm_core *cm_core = cm_node->cm_core;
struct nes_timer_entry *new_send;
int ret = 0;
@@ -454,7 +678,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
new_send->close_when_complete = close_when_complete;
if (type == NES_TIMER_TYPE_CLOSE) {
- new_send->timetosend += (HZ/10);
+ new_send->timetosend += (HZ / 10);
if (cm_node->recv_entry) {
kfree(new_send);
WARN_ON(1);
@@ -475,7 +699,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
ret = nes_nic_cm_xmit(new_send->skb, cm_node->netdev);
if (ret != NETDEV_TX_OK) {
nes_debug(NES_DBG_CM, "Error sending packet %p "
- "(jiffies = %lu)\n", new_send, jiffies);
+ "(jiffies = %lu)\n", new_send, jiffies);
new_send->timetosend = jiffies;
ret = NETDEV_TX_OK;
} else {
@@ -504,6 +728,7 @@ static void nes_retrans_expired(struct nes_cm_node *cm_node)
struct iw_cm_id *cm_id = cm_node->cm_id;
enum nes_cm_node_state state = cm_node->state;
cm_node->state = NES_CM_STATE_CLOSED;
+
switch (state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_CLOSING:
@@ -536,10 +761,10 @@ static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node)
spin_lock_irqsave(&nesqp->lock, qplockflags);
if (nesqp->cm_id) {
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
- "refcount = %d: HIT A "
- "NES_TIMER_TYPE_CLOSE with something "
- "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
- atomic_read(&nesqp->refcount));
+ "refcount = %d: HIT A "
+ "NES_TIMER_TYPE_CLOSE with something "
+ "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
nesqp->ibqp_state = IB_QPS_ERR;
@@ -548,10 +773,10 @@ static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node)
} else {
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
- "refcount = %d: HIT A "
- "NES_TIMER_TYPE_CLOSE with nothing "
- "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
- atomic_read(&nesqp->refcount));
+ "refcount = %d: HIT A "
+ "NES_TIMER_TYPE_CLOSE with nothing "
+ "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
}
} else if (rem_node) {
/* TIME_WAIT state */
@@ -580,11 +805,12 @@ static void nes_cm_timer_tick(unsigned long pass)
int ret = NETDEV_TX_OK;
struct list_head timer_list;
+
INIT_LIST_HEAD(&timer_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp,
- &cm_core->connected_nodes) {
+ &cm_core->connected_nodes) {
cm_node = container_of(list_node, struct nes_cm_node, list);
if ((cm_node->recv_entry) || (cm_node->send_entry)) {
add_ref_cm_node(cm_node);
@@ -595,18 +821,19 @@ static void nes_cm_timer_tick(unsigned long pass)
list_for_each_safe(list_node, list_core_temp, &timer_list) {
cm_node = container_of(list_node, struct nes_cm_node,
- timer_entry);
+ timer_entry);
recv_entry = cm_node->recv_entry;
if (recv_entry) {
if (time_after(recv_entry->timetosend, jiffies)) {
if (nexttimeout > recv_entry->timetosend ||
- !settimer) {
+ !settimer) {
nexttimeout = recv_entry->timetosend;
settimer = 1;
}
- } else
+ } else {
handle_recv_entry(cm_node, 1);
+ }
}
spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
@@ -617,8 +844,8 @@ static void nes_cm_timer_tick(unsigned long pass)
if (time_after(send_entry->timetosend, jiffies)) {
if (cm_node->state != NES_CM_STATE_TSA) {
if ((nexttimeout >
- send_entry->timetosend) ||
- !settimer) {
+ send_entry->timetosend) ||
+ !settimer) {
nexttimeout =
send_entry->timetosend;
settimer = 1;
@@ -630,13 +857,13 @@ static void nes_cm_timer_tick(unsigned long pass)
}
if ((cm_node->state == NES_CM_STATE_TSA) ||
- (cm_node->state == NES_CM_STATE_CLOSED)) {
+ (cm_node->state == NES_CM_STATE_CLOSED)) {
free_retrans_entry(cm_node);
break;
}
if (!send_entry->retranscount ||
- !send_entry->retrycount) {
+ !send_entry->retrycount) {
cm_packets_dropped++;
free_retrans_entry(cm_node);
@@ -645,28 +872,28 @@ static void nes_cm_timer_tick(unsigned long pass)
nes_retrans_expired(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
spin_lock_irqsave(&cm_node->retrans_list_lock,
- flags);
+ flags);
break;
}
atomic_inc(&send_entry->skb->users);
cm_packets_retrans++;
nes_debug(NES_DBG_CM, "Retransmitting send_entry %p "
- "for node %p, jiffies = %lu, time to send = "
- "%lu, retranscount = %u, send_entry->seq_num = "
- "0x%08X, cm_node->tcp_cntxt.rem_ack_num = "
- "0x%08X\n", send_entry, cm_node, jiffies,
- send_entry->timetosend,
- send_entry->retranscount,
- send_entry->seq_num,
- cm_node->tcp_cntxt.rem_ack_num);
+ "for node %p, jiffies = %lu, time to send = "
+ "%lu, retranscount = %u, send_entry->seq_num = "
+ "0x%08X, cm_node->tcp_cntxt.rem_ack_num = "
+ "0x%08X\n", send_entry, cm_node, jiffies,
+ send_entry->timetosend,
+ send_entry->retranscount,
+ send_entry->seq_num,
+ cm_node->tcp_cntxt.rem_ack_num);
spin_unlock_irqrestore(&cm_node->retrans_list_lock,
- flags);
+ flags);
ret = nes_nic_cm_xmit(send_entry->skb, cm_node->netdev);
spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
if (ret != NETDEV_TX_OK) {
nes_debug(NES_DBG_CM, "rexmit failed for "
- "node=%p\n", cm_node);
+ "node=%p\n", cm_node);
cm_packets_bounced++;
send_entry->retrycount--;
nexttimeout = jiffies + NES_SHORT_TIME;
@@ -676,18 +903,18 @@ static void nes_cm_timer_tick(unsigned long pass)
cm_packets_sent++;
}
nes_debug(NES_DBG_CM, "Packet Sent: retrans count = "
- "%u, retry count = %u.\n",
- send_entry->retranscount,
- send_entry->retrycount);
+ "%u, retry count = %u.\n",
+ send_entry->retranscount,
+ send_entry->retrycount);
if (send_entry->send_retrans) {
send_entry->retranscount--;
timetosend = (NES_RETRY_TIMEOUT <<
- (NES_DEFAULT_RETRANS - send_entry->retranscount));
+ (NES_DEFAULT_RETRANS - send_entry->retranscount));
send_entry->timetosend = jiffies +
- min(timetosend, NES_MAX_TIMEOUT);
+ min(timetosend, NES_MAX_TIMEOUT);
if (nexttimeout > send_entry->timetosend ||
- !settimer) {
+ !settimer) {
nexttimeout = send_entry->timetosend;
settimer = 1;
}
@@ -696,11 +923,11 @@ static void nes_cm_timer_tick(unsigned long pass)
close_when_complete =
send_entry->close_when_complete;
nes_debug(NES_DBG_CM, "cm_node=%p state=%d\n",
- cm_node, cm_node->state);
+ cm_node, cm_node->state);
free_retrans_entry(cm_node);
if (close_when_complete)
rem_ref_cm_node(cm_node->cm_core,
- cm_node);
+ cm_node);
}
} while (0);
@@ -710,7 +937,7 @@ static void nes_cm_timer_tick(unsigned long pass)
if (settimer) {
if (!timer_pending(&cm_core->tcp_timer)) {
- cm_core->tcp_timer.expires = nexttimeout;
+ cm_core->tcp_timer.expires = nexttimeout;
add_timer(&cm_core->tcp_timer);
}
}
@@ -721,13 +948,13 @@ static void nes_cm_timer_tick(unsigned long pass)
* send_syn
*/
static int send_syn(struct nes_cm_node *cm_node, u32 sendack,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int ret;
int flags = SET_SYN;
char optionsbuffer[sizeof(struct option_mss) +
- sizeof(struct option_windowscale) + sizeof(struct option_base) +
- TCP_OPTIONS_PADDING];
+ sizeof(struct option_windowscale) + sizeof(struct option_base) +
+ TCP_OPTIONS_PADDING];
int optionssize = 0;
/* Sending MSS option */
@@ -854,7 +1081,7 @@ static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb)
* find_node - find a cm node that matches the reference cm node
*/
static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
- u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
+ u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
{
unsigned long flags;
struct list_head *hte;
@@ -868,12 +1095,12 @@ static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
list_for_each_entry(cm_node, hte, list) {
/* compare quad, return node handle if a match */
nes_debug(NES_DBG_CM, "finding node %x:%x =? %x:%x ^ %x:%x =? %x:%x\n",
- cm_node->loc_addr, cm_node->loc_port,
- loc_addr, loc_port,
- cm_node->rem_addr, cm_node->rem_port,
- rem_addr, rem_port);
+ cm_node->loc_addr, cm_node->loc_port,
+ loc_addr, loc_port,
+ cm_node->rem_addr, cm_node->rem_port,
+ rem_addr, rem_port);
if ((cm_node->loc_addr == loc_addr) && (cm_node->loc_port == loc_port) &&
- (cm_node->rem_addr == rem_addr) && (cm_node->rem_port == rem_port)) {
+ (cm_node->rem_addr == rem_addr) && (cm_node->rem_port == rem_port)) {
add_ref_cm_node(cm_node);
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
return cm_node;
@@ -890,7 +1117,7 @@ static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
* find_listener - find a cm node listening on this addr-port pair
*/
static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
- nes_addr_t dst_addr, u16 dst_port, enum nes_cm_listener_state listener_state)
+ nes_addr_t dst_addr, u16 dst_port, enum nes_cm_listener_state listener_state)
{
unsigned long flags;
struct nes_cm_listener *listen_node;
@@ -900,9 +1127,9 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
list_for_each_entry(listen_node, &cm_core->listen_list.list, list) {
/* compare node pair, return node handle if a match */
if (((listen_node->loc_addr == dst_addr) ||
- listen_node->loc_addr == 0x00000000) &&
- (listen_node->loc_port == dst_port) &&
- (listener_state & listen_node->listener_state)) {
+ listen_node->loc_addr == 0x00000000) &&
+ (listen_node->loc_port == dst_port) &&
+ (listener_state & listen_node->listener_state)) {
atomic_inc(&listen_node->ref_count);
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
return listen_node;
@@ -927,7 +1154,7 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
return -EINVAL;
nes_debug(NES_DBG_CM, "Adding Node %p to Active Connection HT\n",
- cm_node);
+ cm_node);
spin_lock_irqsave(&cm_core->ht_lock, flags);
@@ -946,7 +1173,7 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
* mini_cm_dec_refcnt_listen
*/
static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
- struct nes_cm_listener *listener, int free_hanging_nodes)
+ struct nes_cm_listener *listener, int free_hanging_nodes)
{
int ret = -EINVAL;
int err = 0;
@@ -957,8 +1184,8 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
struct list_head reset_list;
nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, "
- "refcnt=%d\n", listener, free_hanging_nodes,
- atomic_read(&listener->ref_count));
+ "refcnt=%d\n", listener, free_hanging_nodes,
+ atomic_read(&listener->ref_count));
/* free non-accelerated child nodes for this listener */
INIT_LIST_HEAD(&reset_list);
if (free_hanging_nodes) {
@@ -966,7 +1193,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
list_for_each_safe(list_pos, list_temp,
&g_cm_core->connected_nodes) {
cm_node = container_of(list_pos, struct nes_cm_node,
- list);
+ list);
if ((cm_node->listener == listener) &&
(!cm_node->accelerated)) {
add_ref_cm_node(cm_node);
@@ -978,7 +1205,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
list_for_each_safe(list_pos, list_temp, &reset_list) {
cm_node = container_of(list_pos, struct nes_cm_node,
- reset_entry);
+ reset_entry);
{
struct nes_cm_node *loopback = cm_node->loopbackpartner;
enum nes_cm_node_state old_state;
@@ -990,7 +1217,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
err = send_reset(cm_node, NULL);
if (err) {
cm_node->state =
- NES_CM_STATE_CLOSED;
+ NES_CM_STATE_CLOSED;
WARN_ON(1);
} else {
old_state = cm_node->state;
@@ -1035,10 +1262,9 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
- if (listener->nesvnic) {
+ if (listener->nesvnic)
nes_manage_apbvt(listener->nesvnic, listener->loc_port,
- PCI_FUNC(listener->nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
- }
+ PCI_FUNC(listener->nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
nes_debug(NES_DBG_CM, "destroying listener (%p)\n", listener);
@@ -1052,8 +1278,8 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
if (listener) {
if (atomic_read(&listener->pend_accepts_cnt) > 0)
nes_debug(NES_DBG_CM, "destroying listener (%p)"
- " with non-zero pending accepts=%u\n",
- listener, atomic_read(&listener->pend_accepts_cnt));
+ " with non-zero pending accepts=%u\n",
+ listener, atomic_read(&listener->pend_accepts_cnt));
}
return ret;
@@ -1064,7 +1290,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
* mini_cm_del_listen
*/
static int mini_cm_del_listen(struct nes_cm_core *cm_core,
- struct nes_cm_listener *listener)
+ struct nes_cm_listener *listener)
{
listener->listener_state = NES_CM_LISTENER_PASSIVE_STATE;
listener->cm_id = NULL; /* going to be destroyed pretty soon */
@@ -1076,9 +1302,10 @@ static int mini_cm_del_listen(struct nes_cm_core *cm_core,
* mini_cm_accelerated
*/
static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
- struct nes_cm_node *cm_node)
+ struct nes_cm_node *cm_node)
{
u32 was_timer_set;
+
cm_node->accelerated = 1;
if (cm_node->accept_pend) {
@@ -1112,7 +1339,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
rt = ip_route_output(&init_net, htonl(dst_ip), 0, 0, 0);
if (IS_ERR(rt)) {
printk(KERN_ERR "%s: ip_route_output_key failed for 0x%08X\n",
- __func__, dst_ip);
+ __func__, dst_ip);
return rc;
}
@@ -1130,7 +1357,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
if (arpindex >= 0) {
if (!memcmp(nesadapter->arp_table[arpindex].mac_addr,
- neigh->ha, ETH_ALEN)){
+ neigh->ha, ETH_ALEN)) {
/* Mac address same as in nes_arp_table */
neigh_release(neigh);
ip_rt_put(rt);
@@ -1138,8 +1365,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
}
nes_manage_arp_cache(nesvnic->netdev,
- nesadapter->arp_table[arpindex].mac_addr,
- dst_ip, NES_ARP_DELETE);
+ nesadapter->arp_table[arpindex].mac_addr,
+ dst_ip, NES_ARP_DELETE);
}
nes_manage_arp_cache(nesvnic->netdev, neigh->ha,
@@ -1161,8 +1388,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
* make_cm_node - create a new instance of a cm node
*/
static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct nes_cm_info *cm_info,
- struct nes_cm_listener *listener)
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info,
+ struct nes_cm_listener *listener)
{
struct nes_cm_node *cm_node;
struct timespec ts;
@@ -1181,7 +1408,12 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->rem_addr = cm_info->rem_addr;
cm_node->loc_port = cm_info->loc_port;
cm_node->rem_port = cm_info->rem_port;
- cm_node->send_write0 = send_first;
+
+ cm_node->mpa_frame_rev = mpa_version;
+ cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
+ cm_node->ird_size = IETF_NO_IRD_ORD;
+ cm_node->ord_size = IETF_NO_IRD_ORD;
+
nes_debug(NES_DBG_CM, "Make node addresses : loc = %pI4:%x, rem = %pI4:%x\n",
&cm_node->loc_addr, cm_node->loc_port,
&cm_node->rem_addr, cm_node->rem_port);
@@ -1191,7 +1423,7 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n", cm_node->listener,
- cm_node->cm_id);
+ cm_node->cm_id);
spin_lock_init(&cm_node->retrans_list_lock);
@@ -1202,11 +1434,11 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->tcp_cntxt.loc_id = NES_CM_DEF_LOCAL_ID;
cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
cm_node->tcp_cntxt.rcv_wnd = NES_CM_DEFAULT_RCV_WND_SCALED >>
- NES_CM_DEFAULT_RCV_WND_SCALE;
+ NES_CM_DEFAULT_RCV_WND_SCALE;
ts = current_kernel_time();
cm_node->tcp_cntxt.loc_seq_num = htonl(ts.tv_nsec);
cm_node->tcp_cntxt.mss = nesvnic->max_frame_size - sizeof(struct iphdr) -
- sizeof(struct tcphdr) - ETH_HLEN - VLAN_HLEN;
+ sizeof(struct tcphdr) - ETH_HLEN - VLAN_HLEN;
cm_node->tcp_cntxt.rcv_nxt = 0;
/* get a unique session ID , add thread_id to an upcounter to handle race */
atomic_inc(&cm_core->node_cnt);
@@ -1222,12 +1454,11 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->loopbackpartner = NULL;
/* get the mac addr for the remote node */
- if (ipv4_is_loopback(htonl(cm_node->rem_addr)))
+ if (ipv4_is_loopback(htonl(cm_node->rem_addr))) {
arpindex = nes_arp_table(nesdev, ntohl(nesvnic->local_ipaddr), NULL, NES_ARP_RESOLVE);
- else {
+ } else {
oldarpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
arpindex = nes_addr_resolve_neigh(nesvnic, cm_info->rem_addr, oldarpindex);
-
}
if (arpindex < 0) {
kfree(cm_node);
@@ -1260,7 +1491,7 @@ static int add_ref_cm_node(struct nes_cm_node *cm_node)
* rem_ref_cm_node - destroy an instance of a cm node
*/
static int rem_ref_cm_node(struct nes_cm_core *cm_core,
- struct nes_cm_node *cm_node)
+ struct nes_cm_node *cm_node)
{
unsigned long flags;
struct nes_qp *nesqp;
@@ -1291,9 +1522,9 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
} else {
if (cm_node->apbvt_set && cm_node->nesvnic) {
nes_manage_apbvt(cm_node->nesvnic, cm_node->loc_port,
- PCI_FUNC(
- cm_node->nesvnic->nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_DEL);
+ PCI_FUNC(
+ cm_node->nesvnic->nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_DEL);
}
}
@@ -1314,7 +1545,7 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
* process_options
*/
static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
- u32 optionsize, u32 syn_packet)
+ u32 optionsize, u32 syn_packet)
{
u32 tmp;
u32 offset = 0;
@@ -1332,15 +1563,15 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
continue;
case OPTION_NUMBER_MSS:
nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d "
- "Size: %d\n", __func__,
- all_options->as_mss.length, offset, optionsize);
+ "Size: %d\n", __func__,
+ all_options->as_mss.length, offset, optionsize);
got_mss_option = 1;
if (all_options->as_mss.length != 4) {
return 1;
} else {
tmp = ntohs(all_options->as_mss.mss);
if (tmp > 0 && tmp <
- cm_node->tcp_cntxt.mss)
+ cm_node->tcp_cntxt.mss)
cm_node->tcp_cntxt.mss = tmp;
}
break;
@@ -1348,12 +1579,9 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
cm_node->tcp_cntxt.snd_wscale =
all_options->as_windowscale.shiftcount;
break;
- case OPTION_NUMBER_WRITE0:
- cm_node->send_write0 = 1;
- break;
default:
nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n",
- all_options->as_base.optionnum);
+ all_options->as_base.optionnum);
break;
}
offset += all_options->as_base.length;
@@ -1372,8 +1600,8 @@ static void drop_packet(struct sk_buff *skb)
static void handle_fin_pkt(struct nes_cm_node *cm_node)
{
nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
- "refcnt=%d\n", cm_node, cm_node->state,
- atomic_read(&cm_node->ref_count));
+ "refcnt=%d\n", cm_node, cm_node->state,
+ atomic_read(&cm_node->ref_count));
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_SYN_SENT:
@@ -1439,7 +1667,20 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
"listener=%p state=%d\n", __func__, __LINE__, cm_node,
cm_node->listener, cm_node->state);
- active_open_err(cm_node, skb, reset);
+ switch (cm_node->mpa_frame_rev) {
+ case IETF_MPA_V2:
+ cm_node->mpa_frame_rev = IETF_MPA_V1;
+ /* send a syn and goto syn sent state */
+ cm_node->state = NES_CM_STATE_SYN_SENT;
+ if (send_syn(cm_node, 0, NULL)) {
+ active_open_err(cm_node, skb, reset);
+ }
+ break;
+ case IETF_MPA_V1:
+ default:
+ active_open_err(cm_node, skb, reset);
+ break;
+ }
break;
case NES_CM_STATE_MPAREQ_RCVD:
atomic_inc(&cm_node->passive_state);
@@ -1475,21 +1716,21 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
-
- int ret = 0;
+ int ret = 0;
int datasize = skb->len;
u8 *dataloc = skb->data;
enum nes_cm_event_type type = NES_CM_EVENT_UNKNOWN;
- u32 res_type;
+ u32 res_type;
+
ret = parse_mpa(cm_node, dataloc, &res_type, datasize);
if (ret) {
nes_debug(NES_DBG_CM, "didn't like MPA Request\n");
if (cm_node->state == NES_CM_STATE_MPAREQ_SENT) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for "
- "cm_node=%p listener=%p state=%d\n", __func__,
- __LINE__, cm_node, cm_node->listener,
- cm_node->state);
+ "cm_node=%p listener=%p state=%d\n", __func__,
+ __LINE__, cm_node, cm_node->listener,
+ cm_node->state);
active_open_err(cm_node, skb, 1);
} else {
passive_open_err(cm_node, skb, 1);
@@ -1499,16 +1740,15 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
switch (cm_node->state) {
case NES_CM_STATE_ESTABLISHED:
- if (res_type == NES_MPA_REQUEST_REJECT) {
+ if (res_type == NES_MPA_REQUEST_REJECT)
/*BIG problem as we are receiving the MPA.. So should
- * not be REJECT.. This is Passive Open.. We can
- * only receive it Reject for Active Open...*/
+ * not be REJECT.. This is Passive Open.. We can
+ * only receive it Reject for Active Open...*/
WARN_ON(1);
- }
cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
type = NES_CM_EVENT_MPA_REQ;
atomic_set(&cm_node->passive_state,
- NES_PASSIVE_STATE_INDICATED);
+ NES_PASSIVE_STATE_INDICATED);
break;
case NES_CM_STATE_MPAREQ_SENT:
cleanup_retrans_entry(cm_node);
@@ -1535,8 +1775,8 @@ static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_MPAREQ_SENT:
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
- "listener=%p state=%d\n", __func__, __LINE__, cm_node,
- cm_node->listener, cm_node->state);
+ "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+ cm_node->listener, cm_node->state);
active_open_err(cm_node, skb, 1);
break;
case NES_CM_STATE_ESTABLISHED:
@@ -1550,11 +1790,11 @@ static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
}
static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int err;
- err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num))? 0 : 1;
+ err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num)) ? 0 : 1;
if (err)
active_open_err(cm_node, skb, 1);
@@ -1562,7 +1802,7 @@ static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph,
}
static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
int err = 0;
u32 seq;
@@ -1570,21 +1810,22 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
u32 loc_seq_num = cm_node->tcp_cntxt.loc_seq_num;
u32 rcv_nxt = cm_node->tcp_cntxt.rcv_nxt;
u32 rcv_wnd;
+
seq = ntohl(tcph->seq);
ack_seq = ntohl(tcph->ack_seq);
rcv_wnd = cm_node->tcp_cntxt.rcv_wnd;
if (ack_seq != loc_seq_num)
err = 1;
- else if (!between(seq, rcv_nxt, (rcv_nxt+rcv_wnd)))
+ else if (!between(seq, rcv_nxt, (rcv_nxt + rcv_wnd)))
err = 1;
if (err) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
- "listener=%p state=%d\n", __func__, __LINE__, cm_node,
- cm_node->listener, cm_node->state);
+ "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+ cm_node->listener, cm_node->state);
indicate_pkt_err(cm_node, skb);
nes_debug(NES_DBG_CM, "seq ERROR cm_node =%p seq=0x%08X "
- "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt,
- rcv_wnd);
+ "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt,
+ rcv_wnd);
}
return err;
}
@@ -1594,9 +1835,8 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
* is created with a listener or it may comein as rexmitted packet which in
* that case will be just dropped.
*/
-
static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
int ret;
u32 inc_sequence;
@@ -1615,15 +1855,15 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_LISTENING:
/* Passive OPEN */
if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
- cm_node->listener->backlog) {
+ cm_node->listener->backlog) {
nes_debug(NES_DBG_CM, "drop syn due to backlog "
- "pressure \n");
+ "pressure \n");
cm_backlog_drops++;
passive_open_err(cm_node, skb, 0);
break;
}
ret = handle_tcp_options(cm_node, tcph, skb, optionsize,
- 1);
+ 1);
if (ret) {
passive_open_err(cm_node, skb, 0);
/* drop pkt */
@@ -1657,9 +1897,8 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
-
int ret;
u32 inc_sequence;
int optionsize;
@@ -1678,7 +1917,7 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 0);
if (ret) {
nes_debug(NES_DBG_CM, "cm_node=%p tcp_options failed\n",
- cm_node);
+ cm_node);
break;
}
cleanup_retrans_entry(cm_node);
@@ -1717,12 +1956,13 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct tcphdr *tcph)
+ struct tcphdr *tcph)
{
int datasize = 0;
u32 inc_sequence;
int ret = 0;
int optionsize;
+
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
if (check_seq(cm_node, tcph, skb))
@@ -1743,8 +1983,9 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else /* rcvd ACK only */
+ } else { /* rcvd ACK only */
dev_kfree_skb_any(skb);
+ }
break;
case NES_CM_STATE_ESTABLISHED:
/* Passive OPEN */
@@ -1752,16 +1993,18 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else
+ } else {
drop_packet(skb);
+ }
break;
case NES_CM_STATE_MPAREQ_SENT:
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else /* Could be just an ack pkt.. */
+ } else { /* Could be just an ack pkt.. */
dev_kfree_skb_any(skb);
+ }
break;
case NES_CM_STATE_LISTENING:
cleanup_retrans_entry(cm_node);
@@ -1802,14 +2045,15 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
- struct sk_buff *skb, int optionsize, int passive)
+ struct sk_buff *skb, int optionsize, int passive)
{
u8 *optionsloc = (u8 *)&tcph[1];
+
if (optionsize) {
if (process_options(cm_node, optionsloc, optionsize,
- (u32)tcph->syn)) {
+ (u32)tcph->syn)) {
nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n",
- __func__, cm_node);
+ __func__, cm_node);
if (passive)
passive_open_err(cm_node, skb, 1);
else
@@ -1819,7 +2063,7 @@ static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
}
cm_node->tcp_cntxt.snd_wnd = ntohs(tcph->window) <<
- cm_node->tcp_cntxt.snd_wscale;
+ cm_node->tcp_cntxt.snd_wscale;
if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd)
cm_node->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.snd_wnd;
@@ -1830,18 +2074,18 @@ static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
* active_open_err() will send reset() if flag set..
* It will also send ABORT event.
*/
-
static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
- int reset)
+ int reset)
{
cleanup_retrans_entry(cm_node);
if (reset) {
nes_debug(NES_DBG_CM, "ERROR active err called for cm_node=%p, "
- "state=%d\n", cm_node, cm_node->state);
+ "state=%d\n", cm_node, cm_node->state);
add_ref_cm_node(cm_node);
send_reset(cm_node, skb);
- } else
+ } else {
dev_kfree_skb_any(skb);
+ }
cm_node->state = NES_CM_STATE_CLOSED;
create_event(cm_node, NES_CM_EVENT_ABORTED);
@@ -1851,15 +2095,14 @@ static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
* passive_open_err() will either do a reset() or will free up the skb and
* remove the cm_node.
*/
-
static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
- int reset)
+ int reset)
{
cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
if (reset) {
nes_debug(NES_DBG_CM, "passive_open_err sending RST for "
- "cm_node=%p state =%d\n", cm_node, cm_node->state);
+ "cm_node=%p state =%d\n", cm_node, cm_node->state);
send_reset(cm_node, skb);
} else {
dev_kfree_skb_any(skb);
@@ -1874,6 +2117,7 @@ static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
static void free_retrans_entry(struct nes_cm_node *cm_node)
{
struct nes_timer_entry *send_entry;
+
send_entry = cm_node->send_entry;
if (send_entry) {
cm_node->send_entry = NULL;
@@ -1897,26 +2141,28 @@ static void cleanup_retrans_entry(struct nes_cm_node *cm_node)
* Returns skb if to be freed, else it will return NULL if already used..
*/
static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
- struct nes_cm_core *cm_core)
+ struct nes_cm_core *cm_core)
{
- enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
+ enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
struct tcphdr *tcph = tcp_hdr(skb);
- u32 fin_set = 0;
+ u32 fin_set = 0;
int ret = 0;
+
skb_pull(skb, ip_hdr(skb)->ihl << 2);
nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
- "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn,
- tcph->ack, tcph->rst, tcph->fin);
+ "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn,
+ tcph->ack, tcph->rst, tcph->fin);
- if (tcph->rst)
+ if (tcph->rst) {
pkt_type = NES_PKT_TYPE_RST;
- else if (tcph->syn) {
+ } else if (tcph->syn) {
pkt_type = NES_PKT_TYPE_SYN;
if (tcph->ack)
pkt_type = NES_PKT_TYPE_SYNACK;
- } else if (tcph->ack)
+ } else if (tcph->ack) {
pkt_type = NES_PKT_TYPE_ACK;
+ }
if (tcph->fin)
fin_set = 1;
@@ -1947,17 +2193,17 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
* mini_cm_listen - create a listen node with params
*/
static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
{
struct nes_cm_listener *listener;
unsigned long flags;
nes_debug(NES_DBG_CM, "Search for 0x%08x : 0x%04x\n",
- cm_info->loc_addr, cm_info->loc_port);
+ cm_info->loc_addr, cm_info->loc_port);
/* cannot have multiple matching listeners */
listener = find_listener(cm_core, htonl(cm_info->loc_addr),
- htons(cm_info->loc_port), NES_CM_LISTENER_EITHER_STATE);
+ htons(cm_info->loc_port), NES_CM_LISTENER_EITHER_STATE);
if (listener && listener->listener_state == NES_CM_LISTENER_ACTIVE_STATE) {
/* find automatically incs ref count ??? */
atomic_dec(&listener->ref_count);
@@ -2003,9 +2249,9 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
}
nes_debug(NES_DBG_CM, "Api - listen(): addr=0x%08X, port=0x%04x,"
- " listener = %p, backlog = %d, cm_id = %p.\n",
- cm_info->loc_addr, cm_info->loc_port,
- listener, listener->backlog, listener->cm_id);
+ " listener = %p, backlog = %d, cm_id = %p.\n",
+ cm_info->loc_addr, cm_info->loc_port,
+ listener, listener->backlog, listener->cm_id);
return listener;
}
@@ -2015,26 +2261,20 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
* mini_cm_connect - make a connection node with params
*/
static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, u16 private_data_len,
- void *private_data, struct nes_cm_info *cm_info)
+ struct nes_vnic *nesvnic, u16 private_data_len,
+ void *private_data, struct nes_cm_info *cm_info)
{
int ret = 0;
struct nes_cm_node *cm_node;
struct nes_cm_listener *loopbackremotelistener;
struct nes_cm_node *loopbackremotenode;
struct nes_cm_info loopback_cm_info;
- u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + private_data_len;
- struct ietf_mpa_frame *mpa_frame = NULL;
+ u8 *start_buff;
/* create a CM connection node */
cm_node = make_cm_node(cm_core, nesvnic, cm_info, NULL);
if (!cm_node)
return NULL;
- mpa_frame = &cm_node->mpa_frame;
- memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
- mpa_frame->flags = IETF_MPA_FLAGS_CRC;
- mpa_frame->rev = IETF_MPA_VERSION;
- mpa_frame->priv_data_len = htons(private_data_len);
/* set our node side to client (active) side */
cm_node->tcp_cntxt.client = 1;
@@ -2042,8 +2282,8 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
if (cm_info->loc_addr == cm_info->rem_addr) {
loopbackremotelistener = find_listener(cm_core,
- ntohl(nesvnic->local_ipaddr), cm_node->rem_port,
- NES_CM_LISTENER_ACTIVE_STATE);
+ ntohl(nesvnic->local_ipaddr), cm_node->rem_port,
+ NES_CM_LISTENER_ACTIVE_STATE);
if (loopbackremotelistener == NULL) {
create_event(cm_node, NES_CM_EVENT_ABORTED);
} else {
@@ -2052,7 +2292,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
loopback_cm_info.rem_port = cm_info->loc_port;
loopback_cm_info.cm_id = loopbackremotelistener->cm_id;
loopbackremotenode = make_cm_node(cm_core, nesvnic,
- &loopback_cm_info, loopbackremotelistener);
+ &loopback_cm_info, loopbackremotelistener);
if (!loopbackremotenode) {
rem_ref_cm_node(cm_node->cm_core, cm_node);
return NULL;
@@ -2063,7 +2303,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
NES_CM_DEFAULT_RCV_WND_SCALE;
cm_node->loopbackpartner = loopbackremotenode;
memcpy(loopbackremotenode->mpa_frame_buf, private_data,
- private_data_len);
+ private_data_len);
loopbackremotenode->mpa_frame_size = private_data_len;
/* we are done handling this state. */
@@ -2091,12 +2331,10 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
return cm_node;
}
- /* set our node side to client (active) side */
- cm_node->tcp_cntxt.client = 1;
- /* init our MPA frame ptr */
- memcpy(mpa_frame->priv_data, private_data, private_data_len);
+ start_buff = &cm_node->mpa_frame_buf[0] + sizeof(struct ietf_mpa_v2);
+ cm_node->mpa_frame_size = private_data_len;
- cm_node->mpa_frame_size = mpa_frame_size;
+ memcpy(start_buff, private_data, private_data_len);
/* send a syn and goto syn sent state */
cm_node->state = NES_CM_STATE_SYN_SENT;
@@ -2105,18 +2343,19 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
if (ret) {
/* error in sending the syn free up the cm_node struct */
nes_debug(NES_DBG_CM, "Api - connect() FAILED: dest "
- "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n",
- cm_node->rem_addr, cm_node->rem_port, cm_node,
- cm_node->cm_id);
+ "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n",
+ cm_node->rem_addr, cm_node->rem_port, cm_node,
+ cm_node->cm_id);
rem_ref_cm_node(cm_node->cm_core, cm_node);
cm_node = NULL;
}
- if (cm_node)
+ if (cm_node) {
nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X,"
- "port=0x%04x, cm_node=%p, cm_id = %p.\n",
- cm_node->rem_addr, cm_node->rem_port, cm_node,
- cm_node->cm_id);
+ "port=0x%04x, cm_node=%p, cm_id = %p.\n",
+ cm_node->rem_addr, cm_node->rem_port, cm_node,
+ cm_node->cm_id);
+ }
return cm_node;
}
@@ -2126,8 +2365,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
* mini_cm_accept - accept a connection
* This function is never called
*/
-static int mini_cm_accept(struct nes_cm_core *cm_core,
- struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
+static int mini_cm_accept(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
{
return 0;
}
@@ -2136,8 +2374,7 @@ static int mini_cm_accept(struct nes_cm_core *cm_core,
/**
* mini_cm_reject - reject and teardown a connection
*/
-static int mini_cm_reject(struct nes_cm_core *cm_core,
- struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
+static int mini_cm_reject(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
{
int ret = 0;
int err = 0;
@@ -2147,7 +2384,7 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
struct nes_cm_node *loopback = cm_node->loopbackpartner;
nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n",
- __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
+ __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
if (cm_node->tcp_cntxt.client)
return ret;
@@ -2168,8 +2405,9 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
err = send_reset(cm_node, NULL);
if (err)
WARN_ON(1);
- } else
+ } else {
cm_id->add_ref(cm_id);
+ }
}
}
} else {
@@ -2244,7 +2482,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
case NES_CM_STATE_TSA:
if (cm_node->send_entry)
printk(KERN_ERR "ERROR Close got called from STATE_TSA "
- "send_entry=%p\n", cm_node->send_entry);
+ "send_entry=%p\n", cm_node->send_entry);
ret = rem_ref_cm_node(cm_core, cm_node);
break;
}
@@ -2257,7 +2495,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
* node state machine
*/
static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
- struct nes_vnic *nesvnic, struct sk_buff *skb)
+ struct nes_vnic *nesvnic, struct sk_buff *skb)
{
struct nes_cm_node *cm_node = NULL;
struct nes_cm_listener *listener = NULL;
@@ -2269,9 +2507,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
if (!skb)
return 0;
- if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
+ if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr))
return 0;
- }
iph = (struct iphdr *)skb->data;
tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr));
@@ -2289,8 +2526,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
do {
cm_node = find_node(cm_core,
- nfo.rem_port, nfo.rem_addr,
- nfo.loc_port, nfo.loc_addr);
+ nfo.rem_port, nfo.rem_addr,
+ nfo.loc_port, nfo.loc_addr);
if (!cm_node) {
/* Only type of packet accepted are for */
@@ -2300,8 +2537,8 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
break;
}
listener = find_listener(cm_core, nfo.loc_addr,
- nfo.loc_port,
- NES_CM_LISTENER_ACTIVE_STATE);
+ nfo.loc_port,
+ NES_CM_LISTENER_ACTIVE_STATE);
if (!listener) {
nfo.cm_id = NULL;
nfo.conn_type = 0;
@@ -2312,10 +2549,10 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
nfo.cm_id = listener->cm_id;
nfo.conn_type = listener->conn_type;
cm_node = make_cm_node(cm_core, nesvnic, &nfo,
- listener);
+ listener);
if (!cm_node) {
nes_debug(NES_DBG_CM, "Unable to allocate "
- "node\n");
+ "node\n");
cm_packets_dropped++;
atomic_dec(&listener->ref_count);
dev_kfree_skb_any(skb);
@@ -2331,9 +2568,13 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
}
add_ref_cm_node(cm_node);
} else if (cm_node->state == NES_CM_STATE_TSA) {
- rem_ref_cm_node(cm_core, cm_node);
- atomic_inc(&cm_accel_dropped_pkts);
- dev_kfree_skb_any(skb);
+ if (cm_node->nesqp->pau_mode)
+ nes_queue_mgt_skbs(skb, nesvnic, cm_node->nesqp);
+ else {
+ rem_ref_cm_node(cm_core, cm_node);
+ atomic_inc(&cm_accel_dropped_pkts);
+ dev_kfree_skb_any(skb);
+ }
break;
}
skb_reset_network_header(skb);
@@ -2363,7 +2604,7 @@ static struct nes_cm_core *nes_cm_alloc_core(void)
init_timer(&cm_core->tcp_timer);
cm_core->tcp_timer.function = nes_cm_timer_tick;
- cm_core->mtu = NES_CM_DEFAULT_MTU;
+ cm_core->mtu = NES_CM_DEFAULT_MTU;
cm_core->state = NES_CM_STATE_INITED;
cm_core->free_tx_pkt_max = NES_CM_DEFAULT_FREE_PKTS;
@@ -2401,9 +2642,8 @@ static int mini_cm_dealloc_core(struct nes_cm_core *cm_core)
barrier();
- if (timer_pending(&cm_core->tcp_timer)) {
+ if (timer_pending(&cm_core->tcp_timer))
del_timer(&cm_core->tcp_timer);
- }
destroy_workqueue(cm_core->event_wq);
destroy_workqueue(cm_core->disconn_wq);
@@ -2458,8 +2698,8 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
return -EINVAL;
nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_IPV4 |
- NES_QPCONTEXT_MISC_NO_NAGLE | NES_QPCONTEXT_MISC_DO_NOT_FRAG |
- NES_QPCONTEXT_MISC_DROS);
+ NES_QPCONTEXT_MISC_NO_NAGLE | NES_QPCONTEXT_MISC_DO_NOT_FRAG |
+ NES_QPCONTEXT_MISC_DROS);
if (cm_node->tcp_cntxt.snd_wscale || cm_node->tcp_cntxt.rcv_wscale)
nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_WSCALE);
@@ -2469,15 +2709,15 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
nesqp->nesqp_context->mss |= cpu_to_le32(((u32)cm_node->tcp_cntxt.mss) << 16);
nesqp->nesqp_context->tcp_state_flow_label |= cpu_to_le32(
- (u32)NES_QPCONTEXT_TCPSTATE_EST << NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT);
+ (u32)NES_QPCONTEXT_TCPSTATE_EST << NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT);
nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
- (cm_node->tcp_cntxt.snd_wscale << NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT) &
- NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK);
+ (cm_node->tcp_cntxt.snd_wscale << NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK);
nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
- (cm_node->tcp_cntxt.rcv_wscale << NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT) &
- NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK);
+ (cm_node->tcp_cntxt.rcv_wscale << NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK);
nesqp->nesqp_context->keepalive = cpu_to_le32(0x80);
nesqp->nesqp_context->ts_recent = 0;
@@ -2486,24 +2726,24 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
nesqp->nesqp_context->snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.snd_wnd);
nesqp->nesqp_context->rcv_nxt = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
nesqp->nesqp_context->rcv_wnd = cpu_to_le32(cm_node->tcp_cntxt.rcv_wnd <<
- cm_node->tcp_cntxt.rcv_wscale);
+ cm_node->tcp_cntxt.rcv_wscale);
nesqp->nesqp_context->snd_max = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->snd_una = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->srtt = 0;
nesqp->nesqp_context->rttvar = cpu_to_le32(0x6);
nesqp->nesqp_context->ssthresh = cpu_to_le32(0x3FFFC000);
- nesqp->nesqp_context->cwnd = cpu_to_le32(2*cm_node->tcp_cntxt.mss);
+ nesqp->nesqp_context->cwnd = cpu_to_le32(2 * cm_node->tcp_cntxt.mss);
nesqp->nesqp_context->snd_wl1 = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
nesqp->nesqp_context->snd_wl2 = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
nesqp->nesqp_context->max_snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.max_snd_wnd);
nes_debug(NES_DBG_CM, "QP%u: rcv_nxt = 0x%08X, snd_nxt = 0x%08X,"
- " Setting MSS to %u, PDWscale = 0x%08X, rcv_wnd = %u, context misc = 0x%08X.\n",
- nesqp->hwqp.qp_id, le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
- le32_to_cpu(nesqp->nesqp_context->snd_nxt),
- cm_node->tcp_cntxt.mss, le32_to_cpu(nesqp->nesqp_context->pd_index_wscale),
- le32_to_cpu(nesqp->nesqp_context->rcv_wnd),
- le32_to_cpu(nesqp->nesqp_context->misc));
+ " Setting MSS to %u, PDWscale = 0x%08X, rcv_wnd = %u, context misc = 0x%08X.\n",
+ nesqp->hwqp.qp_id, le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ cm_node->tcp_cntxt.mss, le32_to_cpu(nesqp->nesqp_context->pd_index_wscale),
+ le32_to_cpu(nesqp->nesqp_context->rcv_wnd),
+ le32_to_cpu(nesqp->nesqp_context->misc));
nes_debug(NES_DBG_CM, " snd_wnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->snd_wnd));
nes_debug(NES_DBG_CM, " snd_cwnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->cwnd));
nes_debug(NES_DBG_CM, " max_swnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->max_snd_wnd));
@@ -2524,7 +2764,7 @@ int nes_cm_disconn(struct nes_qp *nesqp)
work = kzalloc(sizeof *work, GFP_ATOMIC);
if (!work)
- return -ENOMEM; /* Timer will clean up */
+ return -ENOMEM; /* Timer will clean up */
nes_add_ref(&nesqp->ibqp);
work->nesqp = nesqp;
@@ -2544,7 +2784,7 @@ static void nes_disconnect_worker(struct work_struct *work)
kfree(dwork);
nes_debug(NES_DBG_CM, "processing AEQE id 0x%04X for QP%u.\n",
- nesqp->last_aeq, nesqp->hwqp.qp_id);
+ nesqp->last_aeq, nesqp->hwqp.qp_id);
nes_cm_disconn_true(nesqp);
nes_rem_ref(&nesqp->ibqp);
}
@@ -2580,7 +2820,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
/* make sure we havent already closed this connection */
if (!cm_id) {
nes_debug(NES_DBG_CM, "QP%u disconnect_worker cmid is NULL\n",
- nesqp->hwqp.qp_id);
+ nesqp->hwqp.qp_id);
spin_unlock_irqrestore(&nesqp->lock, flags);
return -1;
}
@@ -2589,7 +2829,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
nes_debug(NES_DBG_CM, "Disconnecting QP%u\n", nesqp->hwqp.qp_id);
original_hw_tcp_state = nesqp->hw_tcp_state;
- original_ibqp_state = nesqp->ibqp_state;
+ original_ibqp_state = nesqp->ibqp_state;
last_ae = nesqp->last_aeq;
if (nesqp->term_flags) {
@@ -2647,16 +2887,16 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
cm_event.private_data_len = 0;
nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event"
- " for QP%u, SQ Head = %u, SQ Tail = %u. "
- "cm_id = %p, refcount = %u.\n",
- nesqp->hwqp.qp_id, nesqp->hwqp.sq_head,
- nesqp->hwqp.sq_tail, cm_id,
- atomic_read(&nesqp->refcount));
+ " for QP%u, SQ Head = %u, SQ Tail = %u. "
+ "cm_id = %p, refcount = %u.\n",
+ nesqp->hwqp.qp_id, nesqp->hwqp.sq_head,
+ nesqp->hwqp.sq_tail, cm_id,
+ atomic_read(&nesqp->refcount));
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
nes_debug(NES_DBG_CM, "OFA CM event_handler "
- "returned, ret=%d\n", ret);
+ "returned, ret=%d\n", ret);
}
if (issue_close) {
@@ -2674,9 +2914,8 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
cm_event.private_data_len = 0;
ret = cm_id->event_handler(cm_id, &cm_event);
- if (ret) {
+ if (ret)
nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
- }
cm_id->rem_ref(cm_id);
}
@@ -2716,8 +2955,8 @@ static int nes_disconnect(struct nes_qp *nesqp, int abrupt)
if (nesqp->lsmm_mr)
nesibdev->ibdev.dereg_mr(nesqp->lsmm_mr);
pci_free_consistent(nesdev->pcidev,
- nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
- nesqp->ietf_frame, nesqp->ietf_frame_pbase);
+ nesqp->private_data_len + nesqp->ietf_frame_size,
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
}
}
@@ -2756,6 +2995,12 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct ib_phys_buf ibphysbuf;
struct nes_pd *nespd;
u64 tagged_offset;
+ u8 mpa_frame_offset = 0;
+ struct ietf_mpa_v2 *mpa_v2_frame;
+ u8 start_addr = 0;
+ u8 *start_ptr = &start_addr;
+ u8 **start_buff = &start_ptr;
+ u16 buff_len = 0;
ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
if (!ibqp)
@@ -2796,53 +3041,49 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
netdev_refcnt_read(nesvnic->netdev));
+ nesqp->ietf_frame_size = sizeof(struct ietf_mpa_v2);
/* allocate the ietf frame and space for private data */
nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev,
- sizeof(struct ietf_mpa_frame) + conn_param->private_data_len,
- &nesqp->ietf_frame_pbase);
+ nesqp->ietf_frame_size + conn_param->private_data_len,
+ &nesqp->ietf_frame_pbase);
if (!nesqp->ietf_frame) {
- nes_debug(NES_DBG_CM, "Unable to allocate memory for private "
- "data\n");
+ nes_debug(NES_DBG_CM, "Unable to allocate memory for private data\n");
return -ENOMEM;
}
+ mpa_v2_frame = (struct ietf_mpa_v2 *)nesqp->ietf_frame;
+ if (cm_node->mpa_frame_rev == IETF_MPA_V1)
+ mpa_frame_offset = 4;
- /* setup the MPA frame */
- nesqp->private_data_len = conn_param->private_data_len;
- memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
-
- memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
- conn_param->private_data_len);
+ memcpy(mpa_v2_frame->priv_data, conn_param->private_data,
+ conn_param->private_data_len);
- nesqp->ietf_frame->priv_data_len =
- cpu_to_be16(conn_param->private_data_len);
- nesqp->ietf_frame->rev = mpa_version;
- nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
+ cm_build_mpa_frame(cm_node, start_buff, &buff_len, nesqp->ietf_frame, MPA_KEY_REPLY);
+ nesqp->private_data_len = conn_param->private_data_len;
/* setup our first outgoing iWarp send WQE (the IETF frame response) */
wqe = &nesqp->hwqp.sq_vbase[0];
if (cm_id->remote_addr.sin_addr.s_addr !=
- cm_id->local_addr.sin_addr.s_addr) {
+ cm_id->local_addr.sin_addr.s_addr) {
u64temp = (unsigned long)nesqp;
nesibdev = nesvnic->nesibdev;
nespd = nesqp->nespd;
- ibphysbuf.addr = nesqp->ietf_frame_pbase;
- ibphysbuf.size = conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame);
- tagged_offset = (u64)(unsigned long)nesqp->ietf_frame;
+ ibphysbuf.addr = nesqp->ietf_frame_pbase + mpa_frame_offset;
+ ibphysbuf.size = buff_len;
+ tagged_offset = (u64)(unsigned long)*start_buff;
ibmr = nesibdev->ibdev.reg_phys_mr((struct ib_pd *)nespd,
- &ibphysbuf, 1,
- IB_ACCESS_LOCAL_WRITE,
- &tagged_offset);
+ &ibphysbuf, 1,
+ IB_ACCESS_LOCAL_WRITE,
+ &tagged_offset);
if (!ibmr) {
nes_debug(NES_DBG_CM, "Unable to register memory region"
- "for lSMM for cm_node = %p \n",
- cm_node);
+ "for lSMM for cm_node = %p \n",
+ cm_node);
pci_free_consistent(nesdev->pcidev,
- nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
- nesqp->ietf_frame, nesqp->ietf_frame_pbase);
+ nesqp->private_data_len + nesqp->ietf_frame_size,
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
return -ENOMEM;
}
@@ -2850,22 +3091,20 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
ibmr->device = nespd->ibpd.device;
nesqp->lsmm_mr = ibmr;
- u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+ u64temp |= NES_SW_CONTEXT_ALIGN >> 1;
set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
- u64temp);
+ NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
+ u64temp);
wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING |
- NES_IWARP_SQ_WQE_WRPDU);
+ NES_IWARP_SQ_WQE_WRPDU);
wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
- cpu_to_le32(conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
+ cpu_to_le32(buff_len);
set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
- (u64)(unsigned long)nesqp->ietf_frame);
+ NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
+ (u64)(unsigned long)(*start_buff));
wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
- cpu_to_le32(conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
+ cpu_to_le32(buff_len);
wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = ibmr->lkey;
if (nesqp->sq_kmapped) {
nesqp->sq_kmapped = 0;
@@ -2874,7 +3113,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
- NES_QPCONTEXT_ORDIRD_WRPDU);
+ NES_QPCONTEXT_ORDIRD_WRPDU);
} else {
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32(NES_QPCONTEXT_ORDIRD_WRPDU);
@@ -2888,11 +3127,11 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* nesqp->cm_node = (void *)cm_id->provider_data; */
cm_id->provider_data = nesqp;
- nesqp->active_conn = 0;
+ nesqp->active_conn = 0;
if (cm_node->state == NES_CM_STATE_TSA)
nes_debug(NES_DBG_CM, "Already state = TSA for cm_node=%p\n",
- cm_node);
+ cm_node);
nes_cm_init_tsa_conn(nesqp, cm_node);
@@ -2909,13 +3148,13 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
nesqp->nesqp_context->misc2 |= cpu_to_le32(
- (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
- NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+ (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
+ NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
nesqp->nesqp_context->arp_index_vlan |=
cpu_to_le32(nes_arp_table(nesdev,
- le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
- NES_ARP_RESOLVE) << 16);
+ le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
+ NES_ARP_RESOLVE) << 16);
nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
@@ -2941,7 +3180,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
crc_value = get_crc_value(&nes_quad);
nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, CRC = 0x%08X\n",
- nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
+ nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
nesqp->hte_index &= adapter->hte_index_mask;
nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
@@ -2949,17 +3188,15 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = "
- "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + "
- "private data length=%zu.\n", nesqp->hwqp.qp_id,
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohl(cm_id->local_addr.sin_addr.s_addr),
- ntohs(cm_id->local_addr.sin_port),
- le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
- le32_to_cpu(nesqp->nesqp_context->snd_nxt),
- conn_param->private_data_len +
- sizeof(struct ietf_mpa_frame));
-
+ "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + "
+ "private data length=%u.\n", nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port),
+ le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ buff_len);
/* notify OF layer that accept event was successful */
cm_id->add_ref(cm_id);
@@ -2980,12 +3217,12 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->private_data_len;
/* copy entire MPA frame to our cm_node's frame */
memcpy(cm_node->loopbackpartner->mpa_frame_buf,
- nesqp->ietf_frame->priv_data, nesqp->private_data_len);
+ conn_param->private_data, conn_param->private_data_len);
create_event(cm_node->loopbackpartner, NES_CM_EVENT_CONNECTED);
}
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
return 0;
}
@@ -2998,34 +3235,28 @@ int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
{
struct nes_cm_node *cm_node;
struct nes_cm_node *loopback;
-
struct nes_cm_core *cm_core;
+ u8 *start_buff;
atomic_inc(&cm_rejects);
- cm_node = (struct nes_cm_node *) cm_id->provider_data;
+ cm_node = (struct nes_cm_node *)cm_id->provider_data;
loopback = cm_node->loopbackpartner;
cm_core = cm_node->cm_core;
cm_node->cm_id = cm_id;
- cm_node->mpa_frame_size = sizeof(struct ietf_mpa_frame) + pdata_len;
- if (cm_node->mpa_frame_size > MAX_CM_BUFFER)
+ if (pdata_len + sizeof(struct ietf_mpa_v2) > MAX_CM_BUFFER)
return -EINVAL;
- memcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
if (loopback) {
memcpy(&loopback->mpa_frame.priv_data, pdata, pdata_len);
loopback->mpa_frame.priv_data_len = pdata_len;
- loopback->mpa_frame_size = sizeof(struct ietf_mpa_frame) +
- pdata_len;
+ loopback->mpa_frame_size = pdata_len;
} else {
- memcpy(&cm_node->mpa_frame.priv_data, pdata, pdata_len);
- cm_node->mpa_frame.priv_data_len = cpu_to_be16(pdata_len);
+ start_buff = &cm_node->mpa_frame_buf[0] + sizeof(struct ietf_mpa_v2);
+ cm_node->mpa_frame_size = pdata_len;
+ memcpy(start_buff, pdata, pdata_len);
}
-
- cm_node->mpa_frame.rev = mpa_version;
- cm_node->mpa_frame.flags = IETF_MPA_FLAGS_CRC | IETF_MPA_FLAGS_REJECT;
-
- return cm_core->api->reject(cm_core, &cm_node->mpa_frame, cm_node);
+ return cm_core->api->reject(cm_core, cm_node);
}
@@ -3052,7 +3283,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesvnic = to_nesvnic(nesqp->ibqp.device);
if (!nesvnic)
return -EINVAL;
- nesdev = nesvnic->nesdev;
+ nesdev = nesvnic->nesdev;
if (!nesdev)
return -EINVAL;
@@ -3060,12 +3291,12 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
return -EINVAL;
nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = "
- "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
- ntohl(nesvnic->local_ipaddr),
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohl(cm_id->local_addr.sin_addr.s_addr),
- ntohs(cm_id->local_addr.sin_port));
+ "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
+ ntohl(nesvnic->local_ipaddr),
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port));
atomic_inc(&cm_connects);
nesqp->active_conn = 1;
@@ -3079,12 +3310,12 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord);
nes_debug(NES_DBG_CM, "mpa private data len =%u\n",
- conn_param->private_data_len);
+ conn_param->private_data_len);
if (cm_id->local_addr.sin_addr.s_addr !=
- cm_id->remote_addr.sin_addr.s_addr) {
+ cm_id->remote_addr.sin_addr.s_addr) {
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+ PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
apbvt_set = 1;
}
@@ -3100,13 +3331,13 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* create a connect CM node connection */
cm_node = g_cm_core->api->connect(g_cm_core, nesvnic,
- conn_param->private_data_len, (void *)conn_param->private_data,
- &cm_info);
+ conn_param->private_data_len, (void *)conn_param->private_data,
+ &cm_info);
if (!cm_node) {
if (apbvt_set)
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_DEL);
+ PCI_FUNC(nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_DEL);
cm_id->rem_ref(cm_id);
return -ENOMEM;
@@ -3156,7 +3387,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
cm_node = g_cm_core->api->listen(g_cm_core, nesvnic, &cm_info);
if (!cm_node) {
printk(KERN_ERR "%s[%u] Error returned from listen API call\n",
- __func__, __LINE__);
+ __func__, __LINE__);
return -ENOMEM;
}
@@ -3164,12 +3395,12 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
if (!cm_node->reused_node) {
err = nes_manage_apbvt(nesvnic,
- ntohs(cm_id->local_addr.sin_port),
- PCI_FUNC(nesvnic->nesdev->pcidev->devfn),
- NES_MANAGE_APBVT_ADD);
+ ntohs(cm_id->local_addr.sin_port),
+ PCI_FUNC(nesvnic->nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_ADD);
if (err) {
printk(KERN_ERR "nes_manage_apbvt call returned %d.\n",
- err);
+ err);
g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node);
return err;
}
@@ -3206,13 +3437,13 @@ int nes_destroy_listen(struct iw_cm_id *cm_id)
int nes_cm_recv(struct sk_buff *skb, struct net_device *netdevice)
{
int rc = 0;
+
cm_packets_received++;
- if ((g_cm_core) && (g_cm_core->api)) {
+ if ((g_cm_core) && (g_cm_core->api))
rc = g_cm_core->api->recv_pkt(g_cm_core, netdev_priv(netdevice), skb);
- } else {
+ else
nes_debug(NES_DBG_CM, "Unable to process packet for CM,"
- " cm is not setup properly.\n");
- }
+ " cm is not setup properly.\n");
return rc;
}
@@ -3227,11 +3458,10 @@ int nes_cm_start(void)
nes_debug(NES_DBG_CM, "\n");
/* create the primary CM core, pass this handle to subsequent core inits */
g_cm_core = nes_cm_alloc_core();
- if (g_cm_core) {
+ if (g_cm_core)
return 0;
- } else {
+ else
return -ENOMEM;
- }
}
@@ -3252,7 +3482,6 @@ int nes_cm_stop(void)
*/
static void cm_event_connected(struct nes_cm_event *event)
{
- u64 u64temp;
struct nes_qp *nesqp;
struct nes_vnic *nesvnic;
struct nes_device *nesdev;
@@ -3261,7 +3490,6 @@ static void cm_event_connected(struct nes_cm_event *event)
struct ib_qp_attr attr;
struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
- struct nes_hw_qp_wqe *wqe;
struct nes_v4_quad nes_quad;
u32 crc_value;
int ret;
@@ -3275,17 +3503,16 @@ static void cm_event_connected(struct nes_cm_event *event)
nesdev = nesvnic->nesdev;
nesadapter = nesdev->nesadapter;
- if (nesqp->destroyed) {
+ if (nesqp->destroyed)
return;
- }
atomic_inc(&cm_connecteds);
nes_debug(NES_DBG_CM, "QP%u attempting to connect to 0x%08X:0x%04X on"
- " local port 0x%04X. jiffies = %lu.\n",
- nesqp->hwqp.qp_id,
- ntohl(cm_id->remote_addr.sin_addr.s_addr),
- ntohs(cm_id->remote_addr.sin_port),
- ntohs(cm_id->local_addr.sin_port),
- jiffies);
+ " local port 0x%04X. jiffies = %lu.\n",
+ nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohs(cm_id->local_addr.sin_port),
+ jiffies);
nes_cm_init_tsa_conn(nesqp, cm_node);
@@ -3316,40 +3543,12 @@ static void cm_event_connected(struct nes_cm_event *event)
NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
/* Adjust tail for not having a LSMM */
- nesqp->hwqp.sq_tail = 1;
+ /*nesqp->hwqp.sq_tail = 1;*/
-#if defined(NES_SEND_FIRST_WRITE)
- if (cm_node->send_write0) {
- nes_debug(NES_DBG_CM, "Sending first write.\n");
- wqe = &nesqp->hwqp.sq_vbase[0];
- u64temp = (unsigned long)nesqp;
- u64temp |= NES_SW_CONTEXT_ALIGN>>1;
- set_wqe_64bit_value(wqe->wqe_words,
- NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
- wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
- cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
- wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
- wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+ build_rdma0_msg(cm_node, &nesqp);
- if (nesqp->sq_kmapped) {
- nesqp->sq_kmapped = 0;
- kunmap(nesqp->page);
- }
-
- /* use the reserved spot on the WQ for the extra first WQE */
- nesqp->nesqp_context->ird_ord_sizes &=
- cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
- NES_QPCONTEXT_ORDIRD_WRPDU |
- NES_QPCONTEXT_ORDIRD_ALSMM));
- nesqp->skip_lsmm = 1;
- nesqp->hwqp.sq_tail = 0;
- nes_write32(nesdev->regs + NES_WQE_ALLOC,
- (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
- }
-#endif
+ nes_write32(nesdev->regs + NES_WQE_ALLOC,
+ (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
memset(&nes_quad, 0, sizeof(nes_quad));
@@ -3366,13 +3565,13 @@ static void cm_event_connected(struct nes_cm_event *event)
crc_value = get_crc_value(&nes_quad);
nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, After CRC = 0x%08X\n",
- nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
+ nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
nesqp->hte_index &= nesadapter->hte_index_mask;
nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
nesqp->ietf_frame = &cm_node->mpa_frame;
- nesqp->private_data_len = (u8) cm_node->mpa_frame_size;
+ nesqp->private_data_len = (u8)cm_node->mpa_frame_size;
cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
/* notify OF layer we successfully created the requested connection */
@@ -3384,7 +3583,9 @@ static void cm_event_connected(struct nes_cm_event *event)
cm_event.remote_addr = cm_id->remote_addr;
cm_event.private_data = (void *)event->cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)event->cm_node->mpa_frame_size;
+ cm_event.ird = cm_node->ird_size;
+ cm_event.ord = cm_node->ord_size;
cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr;
ret = cm_id->event_handler(cm_id, &cm_event);
@@ -3392,12 +3593,12 @@ static void cm_event_connected(struct nes_cm_event *event)
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
attr.qp_state = IB_QPS_RTS;
nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = "
- "%lu\n", nesqp->hwqp.qp_id, jiffies);
+ "%lu\n", nesqp->hwqp.qp_id, jiffies);
return;
}
@@ -3418,16 +3619,14 @@ static void cm_event_connect_error(struct nes_cm_event *event)
return;
cm_id = event->cm_node->cm_id;
- if (!cm_id) {
+ if (!cm_id)
return;
- }
nes_debug(NES_DBG_CM, "cm_node=%p, cm_id=%p\n", event->cm_node, cm_id);
nesqp = cm_id->provider_data;
- if (!nesqp) {
+ if (!nesqp)
return;
- }
/* notify OF layer about this connection error event */
/* cm_id->rem_ref(cm_id); */
@@ -3442,14 +3641,14 @@ static void cm_event_connect_error(struct nes_cm_event *event)
cm_event.private_data_len = 0;
nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, "
- "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr,
- cm_event.remote_addr.sin_addr.s_addr);
+ "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr,
+ cm_event.remote_addr.sin_addr.s_addr);
ret = cm_id->event_handler(cm_id, &cm_event);
nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
- "ret=%d\n", __func__, __LINE__, ret);
+ "ret=%d\n", __func__, __LINE__, ret);
cm_id->rem_ref(cm_id);
rem_ref_cm_node(event->cm_node->cm_core, event->cm_node);
@@ -3519,7 +3718,7 @@ static void cm_event_reset(struct nes_cm_event *event)
*/
static void cm_event_mpa_req(struct nes_cm_event *event)
{
- struct iw_cm_id *cm_id;
+ struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
int ret;
struct nes_cm_node *cm_node;
@@ -3531,7 +3730,7 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
atomic_inc(&cm_connect_reqs);
nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
- cm_node, cm_id, jiffies);
+ cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REQUEST;
cm_event.status = 0;
@@ -3545,19 +3744,21 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port);
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)cm_node->mpa_frame_size;
+ cm_event.ird = cm_node->ird_size;
+ cm_event.ord = cm_node->ord_size;
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
- __func__, __LINE__, ret);
+ __func__, __LINE__, ret);
return;
}
static void cm_event_mpa_reject(struct nes_cm_event *event)
{
- struct iw_cm_id *cm_id;
+ struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
struct nes_cm_node *cm_node;
int ret;
@@ -3569,7 +3770,7 @@ static void cm_event_mpa_reject(struct nes_cm_event *event)
atomic_inc(&cm_connect_reqs);
nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
- cm_node, cm_id, jiffies);
+ cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
cm_event.status = -ECONNREFUSED;
@@ -3584,17 +3785,17 @@ static void cm_event_mpa_reject(struct nes_cm_event *event)
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
- cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
+ cm_event.private_data_len = (u8)cm_node->mpa_frame_size;
nes_debug(NES_DBG_CM, "call CM_EVENT_MPA_REJECTED, local_addr=%08x, "
- "remove_addr=%08x\n",
- cm_event.local_addr.sin_addr.s_addr,
- cm_event.remote_addr.sin_addr.s_addr);
+ "remove_addr=%08x\n",
+ cm_event.local_addr.sin_addr.s_addr,
+ cm_event.remote_addr.sin_addr.s_addr);
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
- __func__, __LINE__, ret);
+ __func__, __LINE__, ret);
return;
}
@@ -3613,7 +3814,7 @@ static int nes_cm_post_event(struct nes_cm_event *event)
event->cm_info.cm_id->add_ref(event->cm_info.cm_id);
INIT_WORK(&event->event_work, nes_cm_event_handler);
nes_debug(NES_DBG_CM, "cm_node=%p queue_work, event=%p\n",
- event->cm_node, event);
+ event->cm_node, event);
queue_work(event->cm_node->cm_core->event_wq, &event->event_work);
@@ -3630,7 +3831,7 @@ static int nes_cm_post_event(struct nes_cm_event *event)
static void nes_cm_event_handler(struct work_struct *work)
{
struct nes_cm_event *event = container_of(work, struct nes_cm_event,
- event_work);
+ event_work);
struct nes_cm_core *cm_core;
if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core))
@@ -3638,29 +3839,29 @@ static void nes_cm_event_handler(struct work_struct *work)
cm_core = event->cm_node->cm_core;
nes_debug(NES_DBG_CM, "event=%p, event->type=%u, events posted=%u\n",
- event, event->type, atomic_read(&cm_core->events_posted));
+ event, event->type, atomic_read(&cm_core->events_posted));
switch (event->type) {
case NES_CM_EVENT_MPA_REQ:
cm_event_mpa_req(event);
nes_debug(NES_DBG_CM, "cm_node=%p CM Event: MPA REQUEST\n",
- event->cm_node);
+ event->cm_node);
break;
case NES_CM_EVENT_RESET:
nes_debug(NES_DBG_CM, "cm_node = %p CM Event: RESET\n",
- event->cm_node);
+ event->cm_node);
cm_event_reset(event);
break;
case NES_CM_EVENT_CONNECTED:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state != NES_CM_STATE_TSA))
+ (event->cm_node->state != NES_CM_STATE_TSA))
break;
cm_event_connected(event);
nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
break;
case NES_CM_EVENT_MPA_REJECT:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state == NES_CM_STATE_TSA))
+ (event->cm_node->state == NES_CM_STATE_TSA))
break;
cm_event_mpa_reject(event);
nes_debug(NES_DBG_CM, "CM Event: REJECT\n");
@@ -3668,7 +3869,7 @@ static void nes_cm_event_handler(struct work_struct *work)
case NES_CM_EVENT_ABORTED:
if ((!event->cm_node->cm_id) ||
- (event->cm_node->state == NES_CM_STATE_TSA))
+ (event->cm_node->state == NES_CM_STATE_TSA))
break;
cm_event_connect_error(event);
nes_debug(NES_DBG_CM, "CM Event: ABORTED\n");
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index d9825fda70a1..bdfa1fbb35fc 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -48,7 +48,16 @@
#define IETF_MPA_KEY_SIZE 16
#define IETF_MPA_VERSION 1
#define IETF_MAX_PRIV_DATA_LEN 512
-#define IETF_MPA_FRAME_SIZE 20
+#define IETF_MPA_FRAME_SIZE 20
+#define IETF_RTR_MSG_SIZE 4
+#define IETF_MPA_V2_FLAG 0x10
+
+/* IETF RTR MSG Fields */
+#define IETF_PEER_TO_PEER 0x8000
+#define IETF_FLPDU_ZERO_LEN 0x4000
+#define IETF_RDMA0_WRITE 0x8000
+#define IETF_RDMA0_READ 0x4000
+#define IETF_NO_IRD_ORD 0x3FFF
enum ietf_mpa_flags {
IETF_MPA_FLAGS_MARKERS = 0x80, /* receive Markers */
@@ -56,7 +65,7 @@ enum ietf_mpa_flags {
IETF_MPA_FLAGS_REJECT = 0x20, /* Reject */
};
-struct ietf_mpa_frame {
+struct ietf_mpa_v1 {
u8 key[IETF_MPA_KEY_SIZE];
u8 flags;
u8 rev;
@@ -66,6 +75,20 @@ struct ietf_mpa_frame {
#define ietf_mpa_req_resp_frame ietf_mpa_frame
+struct ietf_rtr_msg {
+ __be16 ctrl_ird;
+ __be16 ctrl_ord;
+};
+
+struct ietf_mpa_v2 {
+ u8 key[IETF_MPA_KEY_SIZE];
+ u8 flags;
+ u8 rev;
+ __be16 priv_data_len;
+ struct ietf_rtr_msg rtr_msg;
+ u8 priv_data[0];
+};
+
struct nes_v4_quad {
u32 rsvd0;
__le32 DstIpAdrIndex; /* Only most significant 5 bits are valid */
@@ -171,8 +194,7 @@ struct nes_timer_entry {
#define NES_CM_DEF_SEQ2 0x18ed5740
#define NES_CM_DEF_LOCAL_ID2 0xb807
-#define MAX_CM_BUFFER (IETF_MPA_FRAME_SIZE + IETF_MAX_PRIV_DATA_LEN)
-
+#define MAX_CM_BUFFER (IETF_MPA_FRAME_SIZE + IETF_RTR_MSG_SIZE + IETF_MAX_PRIV_DATA_LEN)
typedef u32 nes_addr_t;
@@ -204,6 +226,21 @@ enum nes_cm_node_state {
NES_CM_STATE_CLOSED
};
+enum mpa_frame_version {
+ IETF_MPA_V1 = 1,
+ IETF_MPA_V2 = 2
+};
+
+enum mpa_frame_key {
+ MPA_KEY_REQUEST,
+ MPA_KEY_REPLY
+};
+
+enum send_rdma0 {
+ SEND_RDMA_READ_ZERO = 1,
+ SEND_RDMA_WRITE_ZERO = 2
+};
+
enum nes_tcpip_pkt_type {
NES_PKT_TYPE_UNKNOWN,
NES_PKT_TYPE_SYN,
@@ -245,9 +282,9 @@ struct nes_cm_tcp_context {
enum nes_cm_listener_state {
- NES_CM_LISTENER_PASSIVE_STATE=1,
- NES_CM_LISTENER_ACTIVE_STATE=2,
- NES_CM_LISTENER_EITHER_STATE=3
+ NES_CM_LISTENER_PASSIVE_STATE = 1,
+ NES_CM_LISTENER_ACTIVE_STATE = 2,
+ NES_CM_LISTENER_EITHER_STATE = 3
};
struct nes_cm_listener {
@@ -283,16 +320,20 @@ struct nes_cm_node {
struct nes_cm_node *loopbackpartner;
- struct nes_timer_entry *send_entry;
-
+ struct nes_timer_entry *send_entry;
+ struct nes_timer_entry *recv_entry;
spinlock_t retrans_list_lock;
- struct nes_timer_entry *recv_entry;
+ enum send_rdma0 send_rdma0_op;
- int send_write0;
union {
- struct ietf_mpa_frame mpa_frame;
- u8 mpa_frame_buf[MAX_CM_BUFFER];
+ struct ietf_mpa_v1 mpa_frame;
+ struct ietf_mpa_v2 mpa_v2_frame;
+ u8 mpa_frame_buf[MAX_CM_BUFFER];
};
+ enum mpa_frame_version mpa_frame_rev;
+ u16 ird_size;
+ u16 ord_size;
+
u16 mpa_frame_size;
struct iw_cm_id *cm_id;
struct list_head list;
@@ -399,10 +440,8 @@ struct nes_cm_ops {
struct nes_vnic *, u16, void *,
struct nes_cm_info *);
int (*close)(struct nes_cm_core *, struct nes_cm_node *);
- int (*accept)(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
- int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *,
- struct nes_cm_node *);
+ int (*accept)(struct nes_cm_core *, struct nes_cm_node *);
+ int (*reject)(struct nes_cm_core *, struct nes_cm_node *);
int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
struct sk_buff *);
int (*destroy_cm_core)(struct nes_cm_core *);
@@ -422,5 +461,7 @@ int nes_destroy_listen(struct iw_cm_id *);
int nes_cm_recv(struct sk_buff *, struct net_device *);
int nes_cm_start(void);
int nes_cm_stop(void);
+int nes_add_ref_cm_node(struct nes_cm_node *cm_node);
+int nes_rem_ref_cm_node(struct nes_cm_node *cm_node);
#endif /* NES_CM_H */
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index be36cbeae630..7c0ff19ce382 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -110,6 +110,14 @@ static unsigned char *nes_tcp_state_str[] = {
};
#endif
+static inline void print_ip(struct nes_cm_node *cm_node)
+{
+ unsigned char *rem_addr;
+ if (cm_node) {
+ rem_addr = (unsigned char *)&cm_node->rem_addr;
+ printk(KERN_ERR PFX "Remote IP addr: %pI4\n", rem_addr);
+ }
+}
/**
* nes_nic_init_timer_defaults
@@ -1555,6 +1563,7 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)
struct nes_hw_nic_rq_wqe *nic_rqe;
struct nes_hw_nic *nesnic;
struct nes_device *nesdev;
+ struct nes_rskb_cb *cb;
u32 rx_wqes_posted = 0;
nesnic = &nesvnic->nic;
@@ -1580,6 +1589,9 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)
bus_address = pci_map_single(nesdev->pcidev,
skb->data, nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = bus_address;
+ cb->maplen = nesvnic->max_frame_size;
nic_rqe = &nesnic->rq_vbase[nesvnic->nic.rq_head];
nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] =
@@ -1669,6 +1681,7 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
u32 cqp_head;
u32 counter;
u32 wqe_count;
+ struct nes_rskb_cb *cb;
u8 jumbomode=0;
/* Allocate fragment, SQ, RQ, and CQ; Reuse CEQ based on the PCI function */
@@ -1845,6 +1858,9 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
pmem = pci_map_single(nesdev->pcidev, skb->data,
nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = pmem;
+ cb->maplen = nesvnic->max_frame_size;
nic_rqe = &nesvnic->nic.rq_vbase[counter];
nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32(nesvnic->max_frame_size);
@@ -1873,6 +1889,13 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
jumbomode = 1;
nes_nic_init_timer_defaults(nesdev, jumbomode);
}
+ if ((nesdev->nesadapter->allow_unaligned_fpdus) &&
+ (nes_init_mgt_qp(nesdev, netdev, nesvnic))) {
+ nes_debug(NES_DBG_INIT, "%s: Out of memory for pau nic\n", netdev->name);
+ nes_destroy_nic_qp(nesvnic);
+ return -ENOMEM;
+ }
+
nesvnic->lro_mgr.max_aggr = nes_lro_max_aggr;
nesvnic->lro_mgr.max_desc = NES_MAX_LRO_DESCRIPTORS;
nesvnic->lro_mgr.lro_arr = nesvnic->lro_desc;
@@ -1895,28 +1918,29 @@ void nes_destroy_nic_qp(struct nes_vnic *nesvnic)
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_hw_cqp_wqe *cqp_wqe;
struct nes_hw_nic_sq_wqe *nic_sqe;
- struct nes_hw_nic_rq_wqe *nic_rqe;
__le16 *wqe_fragment_length;
u16 wqe_fragment_index;
- u64 wqe_frag;
u32 cqp_head;
u32 wqm_cfg0;
unsigned long flags;
+ struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
int ret;
+ if (nesdev->nesadapter->allow_unaligned_fpdus)
+ nes_destroy_mgt(nesvnic);
+
/* clear wqe stall before destroying NIC QP */
wqm_cfg0 = nes_read_indexed(nesdev, NES_IDX_WQM_CONFIG0);
nes_write_indexed(nesdev, NES_IDX_WQM_CONFIG0, wqm_cfg0 & 0xFFFF7FFF);
/* Free remaining NIC receive buffers */
while (nesvnic->nic.rq_head != nesvnic->nic.rq_tail) {
- nic_rqe = &nesvnic->nic.rq_vbase[nesvnic->nic.rq_tail];
- wqe_frag = (u64)le32_to_cpu(
- nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX]);
- wqe_frag |= ((u64)le32_to_cpu(
- nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX]))<<32;
- pci_unmap_single(nesdev->pcidev, (dma_addr_t)wqe_frag,
- nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ rx_skb = nesvnic->nic.rx_skb[nesvnic->nic.rq_tail];
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen,
+ PCI_DMA_FROMDEVICE);
+
dev_kfree_skb(nesvnic->nic.rx_skb[nesvnic->nic.rq_tail++]);
nesvnic->nic.rq_tail &= (nesvnic->nic.rq_size - 1);
}
@@ -2775,6 +2799,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
struct nes_hw_nic_sq_wqe *nic_sqe;
struct sk_buff *skb;
struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
__le16 *wqe_fragment_length;
u32 head;
u32 cq_size;
@@ -2859,6 +2884,8 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
bus_address += ((u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])) << 32;
pci_unmap_single(nesdev->pcidev, bus_address,
nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ cb->busaddr = 0;
/* rx_skb->tail = rx_skb->data + rx_pkt_size; */
/* rx_skb->len = rx_pkt_size; */
rx_skb->len = 0; /* TODO: see if this is necessary */
@@ -2983,6 +3010,7 @@ skip_rx_indicate0:
}
+
/**
* nes_cqp_ce_handler
*/
@@ -2997,6 +3025,8 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
u32 cq_size;
u32 cqe_count=0;
u32 error_code;
+ u32 opcode;
+ u32 ctx_index;
/* u32 counter; */
head = cq->cq_head;
@@ -3007,12 +3037,9 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
/* nes_debug(NES_DBG_CQP, "head=%u cqe_words=%08X\n", head,
le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])); */
- if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_VALID) {
- u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head].
- cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) |
- ((u64)(le32_to_cpu(cq->cq_vbase[head].
- cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX])));
- cqp = *((struct nes_hw_cqp **)&u64temp);
+ opcode = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]);
+ if (opcode & NES_CQE_VALID) {
+ cqp = &nesdev->cqp;
error_code = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_ERROR_CODE_IDX]);
if (error_code) {
@@ -3021,15 +3048,14 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])&0x3f,
(u16)(error_code >> 16),
(u16)error_code);
- nes_debug(NES_DBG_CQP, "cqp: qp_id=%u, sq_head=%u, sq_tail=%u\n",
- cqp->qp_id, cqp->sq_head, cqp->sq_tail);
}
- u64temp = (((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
- wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX]))) << 32) |
- ((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
- wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX])));
- cqp_request = *((struct nes_cqp_request **)&u64temp);
+ u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) |
+ ((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX])));
+
+ cqp_request = (struct nes_cqp_request *)(unsigned long)u64temp;
if (cqp_request) {
if (cqp_request->waiting) {
/* nes_debug(NES_DBG_CQP, "%s: Waking up requestor\n"); */
@@ -3075,9 +3101,15 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
cqp_wqe = &nesdev->cqp.sq_vbase[head];
memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
barrier();
- cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] =
+
+ opcode = cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX];
+ if ((opcode & NES_CQP_OPCODE_MASK) == NES_CQP_DOWNLOAD_SEGMENT)
+ ctx_index = NES_CQP_WQE_DL_COMP_CTX_LOW_IDX;
+ else
+ ctx_index = NES_CQP_WQE_COMP_CTX_LOW_IDX;
+ cqp_wqe->wqe_words[ctx_index] =
cpu_to_le32((u32)((unsigned long)cqp_request));
- cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] =
+ cqp_wqe->wqe_words[ctx_index + 1] =
cpu_to_le32((u32)(upper_32_bits((unsigned long)cqp_request)));
nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) put on CQPs SQ wqe%u.\n",
cqp_request, le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f, head);
@@ -3093,7 +3125,6 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
nes_read32(nesdev->regs+NES_CQE_ALLOC);
}
-
static u8 *locate_mpa(u8 *pkt, u32 aeq_info)
{
if (aeq_info & NES_AEQE_Q2_DATA_ETHERNET) {
@@ -3553,9 +3584,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
aeqe_cq_id = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]);
if (aeq_info & NES_AEQE_QP) {
- if ((!nes_is_resource_allocated(nesadapter, nesadapter->allocated_qps,
- aeqe_cq_id)) ||
- (atomic_read(&nesqp->close_timer_started)))
+ if (!nes_is_resource_allocated(nesadapter,
+ nesadapter->allocated_qps,
+ aeqe_cq_id))
return;
}
@@ -3566,8 +3597,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) &&
- (nesqp->ibqp_state == IB_QPS_RTS) &&
- ((nesadapter->eeprom_version >> 16) != NES_A0)) {
+ (nesqp->ibqp_state == IB_QPS_RTS)) {
spin_lock_irqsave(&nesqp->lock, flags);
nesqp->hw_iwarp_state = iwarp_state;
nesqp->hw_tcp_state = tcp_state;
@@ -3594,9 +3624,10 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
return;
}
spin_lock_irqsave(&nesqp->lock, flags);
- nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING;
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
spin_unlock_irqrestore(&nesqp->lock, flags);
- nes_hw_modify_qp(nesdev, nesqp, NES_CQP_QP_IWARP_STATE_CLOSING, 0, 0);
nes_cm_disconn(nesqp);
break;
@@ -3694,7 +3725,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
case NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP:
printk(KERN_ERR PFX "QP[%u] async_event_id=0x%04X IB_EVENT_QP_FATAL\n",
nesqp->hwqp.qp_id, async_event_id);
- nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
+ print_ip(nesqp->cm_node);
+ if (!atomic_read(&nesqp->close_timer_started))
+ nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
break;
case NES_AEQE_AEID_CQ_OPERATION_ERROR:
diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h
index c3241479ec0e..0b590e152c6a 100644
--- a/drivers/infiniband/hw/nes/nes_hw.h
+++ b/drivers/infiniband/hw/nes/nes_hw.h
@@ -47,6 +47,11 @@
#define NES_MULTICAST_PF_MAX 8
#define NES_A0 3
+#define NES_ENABLE_PAU 0x07000001
+#define NES_DISABLE_PAU 0x07000000
+#define NES_PAU_COUNTER 10
+#define NES_CQP_OPCODE_MASK 0x3f
+
enum pci_regs {
NES_INT_STAT = 0x0000,
NES_INT_MASK = 0x0004,
@@ -73,8 +78,10 @@ enum indexed_regs {
NES_IDX_QP_CONTROL = 0x0040,
NES_IDX_FLM_CONTROL = 0x0080,
NES_IDX_INT_CPU_STATUS = 0x00a0,
+ NES_IDX_GPR_TRIGGER = 0x00bc,
NES_IDX_GPIO_CONTROL = 0x00f0,
NES_IDX_GPIO_DATA = 0x00f4,
+ NES_IDX_GPR2 = 0x010c,
NES_IDX_TCP_CONFIG0 = 0x01e4,
NES_IDX_TCP_TIMER_CONFIG = 0x01ec,
NES_IDX_TCP_NOW = 0x01f0,
@@ -202,6 +209,7 @@ enum nes_cqp_opcodes {
NES_CQP_REGISTER_SHARED_STAG = 0x0c,
NES_CQP_DEALLOCATE_STAG = 0x0d,
NES_CQP_MANAGE_ARP_CACHE = 0x0f,
+ NES_CQP_DOWNLOAD_SEGMENT = 0x10,
NES_CQP_SUSPEND_QPS = 0x11,
NES_CQP_UPLOAD_CONTEXT = 0x13,
NES_CQP_CREATE_CEQ = 0x16,
@@ -210,7 +218,8 @@ enum nes_cqp_opcodes {
NES_CQP_DESTROY_AEQ = 0x1b,
NES_CQP_LMI_ACCESS = 0x20,
NES_CQP_FLUSH_WQES = 0x22,
- NES_CQP_MANAGE_APBVT = 0x23
+ NES_CQP_MANAGE_APBVT = 0x23,
+ NES_CQP_MANAGE_QUAD_HASH = 0x25
};
enum nes_cqp_wqe_word_idx {
@@ -222,6 +231,14 @@ enum nes_cqp_wqe_word_idx {
NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX = 5,
};
+enum nes_cqp_wqe_word_download_idx { /* format differs from other cqp ops */
+ NES_CQP_WQE_DL_OPCODE_IDX = 0,
+ NES_CQP_WQE_DL_COMP_CTX_LOW_IDX = 1,
+ NES_CQP_WQE_DL_COMP_CTX_HIGH_IDX = 2,
+ NES_CQP_WQE_DL_LENGTH_0_TOTAL_IDX = 3
+ /* For index values 4-15 use NES_NIC_SQ_WQE_ values */
+};
+
enum nes_cqp_cq_wqeword_idx {
NES_CQP_CQ_WQE_PBL_LOW_IDX = 6,
NES_CQP_CQ_WQE_PBL_HIGH_IDX = 7,
@@ -242,6 +259,7 @@ enum nes_cqp_stag_wqeword_idx {
NES_CQP_STAG_WQE_PBL_LEN_IDX = 14
};
+#define NES_CQP_OP_LOGICAL_PORT_SHIFT 26
#define NES_CQP_OP_IWARP_STATE_SHIFT 28
#define NES_CQP_OP_TERMLEN_SHIFT 28
@@ -599,6 +617,7 @@ enum nes_nic_sq_wqe_bits {
enum nes_nic_cqe_word_idx {
NES_NIC_CQE_ACCQP_ID_IDX = 0,
+ NES_NIC_CQE_HASH_RCVNXT = 1,
NES_NIC_CQE_TAG_PKT_TYPE_IDX = 2,
NES_NIC_CQE_MISC_IDX = 3,
};
@@ -1005,6 +1024,11 @@ struct nes_arp_entry {
#define NES_NIC_CQ_DOWNWARD_TREND 16
#define NES_PFT_SIZE 48
+#define NES_MGT_WQ_COUNT 32
+#define NES_MGT_CTX_SIZE ((NES_NIC_CTX_RQ_SIZE_32) | (NES_NIC_CTX_SQ_SIZE_32))
+#define NES_MGT_QP_OFFSET 36
+#define NES_MGT_QP_COUNT 4
+
struct nes_hw_tune_timer {
/* u16 cq_count; */
u16 threshold_low;
@@ -1118,6 +1142,7 @@ struct nes_adapter {
u32 et_rate_sample_interval;
u32 timer_int_limit;
u32 wqm_quanta;
+ u8 allow_unaligned_fpdus;
/* Adapter base MAC address */
u32 mac_addr_low;
@@ -1251,6 +1276,14 @@ struct nes_vnic {
enum ib_event_type delayed_event;
enum ib_event_type last_dispatched_event;
spinlock_t port_ibevent_lock;
+ u32 mgt_mem_size;
+ void *mgt_vbase;
+ dma_addr_t mgt_pbase;
+ struct nes_vnic_mgt *mgtvnic[NES_MGT_QP_COUNT];
+ struct task_struct *mgt_thread;
+ wait_queue_head_t mgt_wait_queue;
+ struct sk_buff_head mgt_skb_list;
+
};
struct nes_ib_device {
diff --git a/drivers/infiniband/hw/nes/nes_mgt.c b/drivers/infiniband/hw/nes/nes_mgt.c
new file mode 100644
index 000000000000..b3b2a240c6e9
--- /dev/null
+++ b/drivers/infiniband/hw/nes/nes_mgt.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2006 - 2009 Intel-NE, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/kthread.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include "nes.h"
+#include "nes_mgt.h"
+
+atomic_t pau_qps_created;
+atomic_t pau_qps_destroyed;
+
+static void nes_replenish_mgt_rq(struct nes_vnic_mgt *mgtvnic)
+{
+ unsigned long flags;
+ dma_addr_t bus_address;
+ struct sk_buff *skb;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ struct nes_hw_mgt *nesmgt;
+ struct nes_device *nesdev;
+ struct nes_rskb_cb *cb;
+ u32 rx_wqes_posted = 0;
+
+ nesmgt = &mgtvnic->mgt;
+ nesdev = mgtvnic->nesvnic->nesdev;
+ spin_lock_irqsave(&nesmgt->rq_lock, flags);
+ if (nesmgt->replenishing_rq != 0) {
+ if (((nesmgt->rq_size - 1) == atomic_read(&mgtvnic->rx_skbs_needed)) &&
+ (atomic_read(&mgtvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&mgtvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ mgtvnic->rq_wqes_timer.expires = jiffies + (HZ / 2); /* 1/2 second */
+ add_timer(&mgtvnic->rq_wqes_timer);
+ } else {
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ }
+ return;
+ }
+ nesmgt->replenishing_rq = 1;
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ do {
+ skb = dev_alloc_skb(mgtvnic->nesvnic->max_frame_size);
+ if (skb) {
+ skb->dev = mgtvnic->nesvnic->netdev;
+
+ bus_address = pci_map_single(nesdev->pcidev,
+ skb->data, mgtvnic->nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = bus_address;
+ cb->maplen = mgtvnic->nesvnic->max_frame_size;
+
+ nic_rqe = &nesmgt->rq_vbase[mgtvnic->mgt.rq_head];
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] =
+ cpu_to_le32(mgtvnic->nesvnic->max_frame_size);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] =
+ cpu_to_le32((u32)bus_address);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] =
+ cpu_to_le32((u32)((u64)bus_address >> 32));
+ nesmgt->rx_skb[nesmgt->rq_head] = skb;
+ nesmgt->rq_head++;
+ nesmgt->rq_head &= nesmgt->rq_size - 1;
+ atomic_dec(&mgtvnic->rx_skbs_needed);
+ barrier();
+ if (++rx_wqes_posted == 255) {
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesmgt->qp_id);
+ rx_wqes_posted = 0;
+ }
+ } else {
+ spin_lock_irqsave(&nesmgt->rq_lock, flags);
+ if (((nesmgt->rq_size - 1) == atomic_read(&mgtvnic->rx_skbs_needed)) &&
+ (atomic_read(&mgtvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&mgtvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ mgtvnic->rq_wqes_timer.expires = jiffies + (HZ / 2); /* 1/2 second */
+ add_timer(&mgtvnic->rq_wqes_timer);
+ } else {
+ spin_unlock_irqrestore(&nesmgt->rq_lock, flags);
+ }
+ break;
+ }
+ } while (atomic_read(&mgtvnic->rx_skbs_needed));
+ barrier();
+ if (rx_wqes_posted)
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesmgt->qp_id);
+ nesmgt->replenishing_rq = 0;
+}
+
+/**
+ * nes_mgt_rq_wqes_timeout
+ */
+static void nes_mgt_rq_wqes_timeout(unsigned long parm)
+{
+ struct nes_vnic_mgt *mgtvnic = (struct nes_vnic_mgt *)parm;
+
+ atomic_set(&mgtvnic->rx_skb_timer_running, 0);
+ if (atomic_read(&mgtvnic->rx_skbs_needed))
+ nes_replenish_mgt_rq(mgtvnic);
+}
+
+/**
+ * nes_mgt_free_skb - unmap and free skb
+ */
+static void nes_mgt_free_skb(struct nes_device *nesdev, struct sk_buff *skb, u32 dir)
+{
+ struct nes_rskb_cb *cb;
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen, dir);
+ cb->busaddr = 0;
+ dev_kfree_skb_any(skb);
+}
+
+/**
+ * nes_download_callback - handle download completions
+ */
+static void nes_download_callback(struct nes_device *nesdev, struct nes_cqp_request *cqp_request)
+{
+ struct pau_fpdu_info *fpdu_info = cqp_request->cqp_callback_pointer;
+ struct nes_qp *nesqp = fpdu_info->nesqp;
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < fpdu_info->frag_cnt; i++) {
+ skb = fpdu_info->frags[i].skb;
+ if (fpdu_info->frags[i].cmplt) {
+ nes_mgt_free_skb(nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ }
+
+ if (fpdu_info->hdr_vbase)
+ pci_free_consistent(nesdev->pcidev, fpdu_info->hdr_len,
+ fpdu_info->hdr_vbase, fpdu_info->hdr_pbase);
+ kfree(fpdu_info);
+}
+
+/**
+ * nes_get_seq - Get the seq, ack_seq and window from the packet
+ */
+static u32 nes_get_seq(struct sk_buff *skb, u32 *ack, u16 *wnd, u32 *fin_rcvd, u32 *rst_rcvd)
+{
+ struct nes_rskb_cb *cb = (struct nes_rskb_cb *)&skb->cb[0];
+ struct iphdr *iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ struct tcphdr *tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+
+ *ack = be32_to_cpu(tcph->ack_seq);
+ *wnd = be16_to_cpu(tcph->window);
+ *fin_rcvd = tcph->fin;
+ *rst_rcvd = tcph->rst;
+ return be32_to_cpu(tcph->seq);
+}
+
+/**
+ * nes_get_next_skb - Get the next skb based on where current skb is in the queue
+ */
+static struct sk_buff *nes_get_next_skb(struct nes_device *nesdev, struct nes_qp *nesqp,
+ struct sk_buff *skb, u32 nextseq, u32 *ack,
+ u16 *wnd, u32 *fin_rcvd, u32 *rst_rcvd)
+{
+ u32 seq;
+ bool processacks;
+ struct sk_buff *old_skb;
+
+ if (skb) {
+ /* Continue processing fpdu */
+ if (skb->next == (struct sk_buff *)&nesqp->pau_list)
+ goto out;
+ skb = skb->next;
+ processacks = false;
+ } else {
+ /* Starting a new one */
+ if (skb_queue_empty(&nesqp->pau_list))
+ goto out;
+ skb = skb_peek(&nesqp->pau_list);
+ processacks = true;
+ }
+
+ while (1) {
+ seq = nes_get_seq(skb, ack, wnd, fin_rcvd, rst_rcvd);
+ if (seq == nextseq) {
+ if (skb->len || processacks)
+ break;
+ } else if (after(seq, nextseq)) {
+ goto out;
+ }
+
+ if (skb->next == (struct sk_buff *)&nesqp->pau_list)
+ goto out;
+
+ old_skb = skb;
+ skb = skb->next;
+ skb_unlink(old_skb, &nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, old_skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ return skb;
+
+out:
+ return NULL;
+}
+
+/**
+ * get_fpdu_info - Find the next complete fpdu and return its fragments.
+ */
+static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
+ struct pau_fpdu_info **pau_fpdu_info)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ struct nes_rskb_cb *cb;
+ struct pau_fpdu_info *fpdu_info = NULL;
+ struct pau_fpdu_frag frags[MAX_FPDU_FRAGS];
+ unsigned long flags;
+ u32 fpdu_len = 0;
+ u32 tmp_len;
+ int frag_cnt = 0;
+ u32 tot_len;
+ u32 frag_tot;
+ u32 ack;
+ u32 fin_rcvd;
+ u32 rst_rcvd;
+ u16 wnd;
+ int i;
+ int rc = 0;
+
+ *pau_fpdu_info = NULL;
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ skb = nes_get_next_skb(nesdev, nesqp, NULL, nesqp->pau_rcv_nxt, &ack, &wnd, &fin_rcvd, &rst_rcvd);
+ if (!skb) {
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ goto out;
+ }
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ if (skb->len) {
+ fpdu_len = be16_to_cpu(*(__be16 *) skb->data) + MPA_FRAMING;
+ fpdu_len = (fpdu_len + 3) & 0xfffffffc;
+ tmp_len = fpdu_len;
+
+ /* See if we have all of the fpdu */
+ frag_tot = 0;
+ memset(&frags, 0, sizeof frags);
+ for (i = 0; i < MAX_FPDU_FRAGS; i++) {
+ frags[i].physaddr = cb->busaddr;
+ frags[i].physaddr += skb->data - cb->data_start;
+ frags[i].frag_len = min(tmp_len, skb->len);
+ frags[i].skb = skb;
+ frags[i].cmplt = (skb->len == frags[i].frag_len);
+ frag_tot += frags[i].frag_len;
+ frag_cnt++;
+
+ tmp_len -= frags[i].frag_len;
+ if (tmp_len == 0)
+ break;
+
+ skb = nes_get_next_skb(nesdev, nesqp, skb,
+ nesqp->pau_rcv_nxt + frag_tot, &ack, &wnd, &fin_rcvd, &rst_rcvd);
+ if (!skb) {
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ goto out;
+ } else if (rst_rcvd) {
+ /* rst received in the middle of fpdu */
+ for (; i >= 0; i--) {
+ skb_unlink(frags[i].skb, &nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, frags[i].skb, PCI_DMA_TODEVICE);
+ }
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ frags[0].physaddr = cb->busaddr;
+ frags[0].physaddr += skb->data - cb->data_start;
+ frags[0].frag_len = skb->len;
+ frags[0].skb = skb;
+ frags[0].cmplt = true;
+ frag_cnt = 1;
+ break;
+ }
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ }
+ } else {
+ /* no data */
+ frags[0].physaddr = cb->busaddr;
+ frags[0].frag_len = 0;
+ frags[0].skb = skb;
+ frags[0].cmplt = true;
+ frag_cnt = 1;
+ }
+
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+
+ /* Found one */
+ fpdu_info = kzalloc(sizeof(*fpdu_info), GFP_ATOMIC);
+ if (fpdu_info == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to alloc a fpdu_info.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ fpdu_info->cqp_request = nes_get_cqp_request(nesdev);
+ if (fpdu_info->cqp_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cb = (struct nes_rskb_cb *)&frags[0].skb->cb[0];
+ iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ fpdu_info->hdr_len = (((unsigned char *)tcph) + 4 * (tcph->doff)) - cb->data_start;
+ fpdu_info->data_len = fpdu_len;
+ tot_len = fpdu_info->hdr_len + fpdu_len - ETH_HLEN;
+
+ if (frags[0].cmplt) {
+ fpdu_info->hdr_pbase = cb->busaddr;
+ fpdu_info->hdr_vbase = NULL;
+ } else {
+ fpdu_info->hdr_vbase = pci_alloc_consistent(nesdev->pcidev,
+ fpdu_info->hdr_len, &fpdu_info->hdr_pbase);
+ if (!fpdu_info->hdr_vbase) {
+ nes_debug(NES_DBG_PAU, "Unable to allocate memory for pau first frag\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Copy hdrs, adjusting len and seqnum */
+ memcpy(fpdu_info->hdr_vbase, cb->data_start, fpdu_info->hdr_len);
+ iph = (struct iphdr *)(fpdu_info->hdr_vbase + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ }
+
+ iph->tot_len = cpu_to_be16(tot_len);
+ iph->saddr = cpu_to_be32(0x7f000001);
+
+ tcph->seq = cpu_to_be32(nesqp->pau_rcv_nxt);
+ tcph->ack_seq = cpu_to_be32(ack);
+ tcph->window = cpu_to_be16(wnd);
+
+ nesqp->pau_rcv_nxt += fpdu_len + fin_rcvd;
+
+ memcpy(fpdu_info->frags, frags, sizeof(fpdu_info->frags));
+ fpdu_info->frag_cnt = frag_cnt;
+ fpdu_info->nesqp = nesqp;
+ *pau_fpdu_info = fpdu_info;
+
+ /* Update skb's for next pass */
+ for (i = 0; i < frag_cnt; i++) {
+ cb = (struct nes_rskb_cb *)&frags[i].skb->cb[0];
+ skb_pull(frags[i].skb, frags[i].frag_len);
+
+ if (frags[i].skb->len == 0) {
+ /* Pull skb off the list - it will be freed in the callback */
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ skb_unlink(frags[i].skb, &nesqp->pau_list);
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ } else {
+ /* Last skb still has data so update the seq */
+ iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ tcph->seq = cpu_to_be32(nesqp->pau_rcv_nxt);
+ }
+ }
+
+out:
+ if (rc) {
+ if (fpdu_info) {
+ if (fpdu_info->cqp_request)
+ nes_put_cqp_request(nesdev, fpdu_info->cqp_request);
+ kfree(fpdu_info);
+ }
+ }
+ return rc;
+}
+
+/**
+ * forward_fpdu - send complete fpdus, one at a time
+ */
+static int forward_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct pau_fpdu_info *fpdu_info;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ u64 u64tmp;
+ u32 u32tmp;
+ int rc;
+
+ while (1) {
+ rc = get_fpdu_info(nesdev, nesqp, &fpdu_info);
+ if (fpdu_info == NULL)
+ return rc;
+
+ cqp_request = fpdu_info->cqp_request;
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_DL_OPCODE_IDX,
+ NES_CQP_DOWNLOAD_SEGMENT |
+ (((u32)nesvnic->logical_port) << NES_CQP_OP_LOGICAL_PORT_SHIFT));
+
+ u32tmp = fpdu_info->hdr_len << 16;
+ u32tmp |= fpdu_info->hdr_len + (u32)fpdu_info->data_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_DL_LENGTH_0_TOTAL_IDX,
+ u32tmp);
+
+ u32tmp = (fpdu_info->frags[1].frag_len << 16) | fpdu_info->frags[0].frag_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_LENGTH_2_1_IDX,
+ u32tmp);
+
+ u32tmp = (fpdu_info->frags[3].frag_len << 16) | fpdu_info->frags[2].frag_len;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_LENGTH_4_3_IDX,
+ u32tmp);
+
+ u64tmp = (u64)fpdu_info->hdr_pbase;
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX,
+ lower_32_bits(u64tmp));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_HIGH_IDX,
+ upper_32_bits(u64tmp >> 32));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[0].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[0].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG2_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[1].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG2_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[1].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG3_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[2].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG3_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[2].physaddr));
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG4_LOW_IDX,
+ lower_32_bits(fpdu_info->frags[3].physaddr));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG4_HIGH_IDX,
+ upper_32_bits(fpdu_info->frags[3].physaddr));
+
+ cqp_request->cqp_callback_pointer = fpdu_info;
+ cqp_request->callback = 1;
+ cqp_request->cqp_callback = nes_download_callback;
+
+ atomic_set(&cqp_request->refcount, 1);
+ nes_post_cqp_request(nesdev, cqp_request);
+ }
+
+ return 0;
+}
+
+static void process_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ int again = 1;
+ unsigned long flags;
+
+ do {
+ /* Ignore rc - if it failed, tcp retries will cause it to try again */
+ forward_fpdus(nesvnic, nesqp);
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ if (nesqp->pau_pending) {
+ nesqp->pau_pending = 0;
+ } else {
+ nesqp->pau_busy = 0;
+ again = 0;
+ }
+
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ } while (again);
+}
+
+/**
+ * queue_fpdus - Handle fpdu's that hw passed up to sw
+ */
+static void queue_fpdus(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct sk_buff *tmpskb;
+ struct nes_rskb_cb *cb;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ unsigned char *tcph_end;
+ u32 rcv_nxt;
+ u32 rcv_wnd;
+ u32 seqnum;
+ u32 len;
+ bool process_it = false;
+ unsigned long flags;
+
+ /* Move data ptr to after tcp header */
+ iph = (struct iphdr *)skb->data;
+ tcph = (struct tcphdr *)(((char *)iph) + (4 * iph->ihl));
+ seqnum = be32_to_cpu(tcph->seq);
+ tcph_end = (((char *)tcph) + (4 * tcph->doff));
+
+ len = be16_to_cpu(iph->tot_len);
+ if (skb->len > len)
+ skb_trim(skb, len);
+ skb_pull(skb, tcph_end - skb->data);
+
+ /* Initialize tracking values */
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->seqnum = seqnum;
+
+ /* Make sure data is in the receive window */
+ rcv_nxt = nesqp->pau_rcv_nxt;
+ rcv_wnd = le32_to_cpu(nesqp->nesqp_context->rcv_wnd);
+ if (!between(seqnum, rcv_nxt, (rcv_nxt + rcv_wnd))) {
+ nes_mgt_free_skb(nesvnic->nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ return;
+ }
+
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+
+ if (nesqp->pau_busy)
+ nesqp->pau_pending = 1;
+ else
+ nesqp->pau_busy = 1;
+
+ /* Queue skb by sequence number */
+ if (skb_queue_len(&nesqp->pau_list) == 0) {
+ skb_queue_head(&nesqp->pau_list, skb);
+ } else {
+ tmpskb = nesqp->pau_list.next;
+ while (tmpskb != (struct sk_buff *)&nesqp->pau_list) {
+ cb = (struct nes_rskb_cb *)&tmpskb->cb[0];
+ if (before(seqnum, cb->seqnum))
+ break;
+ tmpskb = tmpskb->next;
+ }
+ skb_insert(tmpskb, skb, &nesqp->pau_list);
+ }
+ if (nesqp->pau_state == PAU_READY)
+ process_it = true;
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+
+ if (process_it)
+ process_fpdus(nesvnic, nesqp);
+
+ return;
+}
+
+/**
+ * mgt_thread - Handle mgt skbs in a safe context
+ */
+static int mgt_thread(void *context)
+{
+ struct nes_vnic *nesvnic = context;
+ struct sk_buff *skb;
+ struct nes_rskb_cb *cb;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(nesvnic->mgt_wait_queue,
+ skb_queue_len(&nesvnic->mgt_skb_list) || kthread_should_stop());
+ while ((skb_queue_len(&nesvnic->mgt_skb_list)) && !kthread_should_stop()) {
+ skb = skb_dequeue(&nesvnic->mgt_skb_list);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->data_start = skb->data - ETH_HLEN;
+ cb->busaddr = pci_map_single(nesvnic->nesdev->pcidev, cb->data_start,
+ nesvnic->max_frame_size, PCI_DMA_TODEVICE);
+ queue_fpdus(skb, nesvnic, cb->nesqp);
+ }
+ }
+
+ /* Closing down so delete any entries on the queue */
+ while (skb_queue_len(&nesvnic->mgt_skb_list)) {
+ skb = skb_dequeue(&nesvnic->mgt_skb_list);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ nes_rem_ref_cm_node(cb->nesqp->cm_node);
+ dev_kfree_skb_any(skb);
+ }
+ return 0;
+}
+
+/**
+ * nes_queue_skbs - Queue skb so it can be handled in a thread context
+ */
+void nes_queue_mgt_skbs(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_rskb_cb *cb;
+
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->nesqp = nesqp;
+ skb_queue_tail(&nesvnic->mgt_skb_list, skb);
+ wake_up_interruptible(&nesvnic->mgt_wait_queue);
+}
+
+void nes_destroy_pau_qp(struct nes_device *nesdev, struct nes_qp *nesqp)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ atomic_inc(&pau_qps_destroyed);
+
+ /* Free packets that have not yet been forwarded */
+ /* Lock is acquired by skb_dequeue when removing the skb */
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
+ while (skb_queue_len(&nesqp->pau_list)) {
+ skb = skb_dequeue(&nesqp->pau_list);
+ nes_mgt_free_skb(nesdev, skb, PCI_DMA_TODEVICE);
+ nes_rem_ref_cm_node(nesqp->cm_node);
+ }
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+}
+
+static void nes_chg_qh_handler(struct nes_device *nesdev, struct nes_cqp_request *cqp_request)
+{
+ struct pau_qh_chg *qh_chg = cqp_request->cqp_callback_pointer;
+ struct nes_cqp_request *new_request;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_adapter *nesadapter;
+ struct nes_qp *nesqp;
+ struct nes_v4_quad nes_quad;
+ u32 crc_value;
+ u64 u64temp;
+
+ nesadapter = nesdev->nesadapter;
+ nesqp = qh_chg->nesqp;
+
+ /* Should we handle the bad completion */
+ if (cqp_request->major_code) {
+ printk(KERN_ERR PFX "Invalid cqp_request major_code=0x%x\n",
+ cqp_request->major_code);
+ WARN_ON(1);
+ }
+
+ switch (nesqp->pau_state) {
+ case PAU_DEL_QH:
+ /* Old hash code deleted, now set the new one */
+ nesqp->pau_state = PAU_ADD_LB_QH;
+ new_request = nes_get_cqp_request(nesdev);
+ if (new_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a new_request.\n");
+ WARN_ON(1);
+ return;
+ }
+
+ memset(&nes_quad, 0, sizeof(nes_quad));
+ nes_quad.DstIpAdrIndex =
+ cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+ nes_quad.SrcIpadr = cpu_to_be32(0x7f000001);
+ nes_quad.TcpPorts[0] = swab16(nesqp->nesqp_context->tcpPorts[1]);
+ nes_quad.TcpPorts[1] = swab16(nesqp->nesqp_context->tcpPorts[0]);
+
+ /* Produce hash key */
+ crc_value = get_crc_value(&nes_quad);
+ nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
+ nes_debug(NES_DBG_PAU, "new HTE Index = 0x%08X, CRC = 0x%08X\n",
+ nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
+
+ nesqp->hte_index &= nesadapter->hte_index_mask;
+ nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
+ nesqp->nesqp_context->ip0 = cpu_to_le32(0x7f000001);
+ nesqp->nesqp_context->rcv_nxt = cpu_to_le32(nesqp->pau_rcv_nxt);
+
+ cqp_wqe = &new_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words,
+ NES_CQP_WQE_OPCODE_IDX, NES_CQP_MANAGE_QUAD_HASH |
+ NES_CQP_QP_TYPE_IWARP | NES_CQP_QP_CONTEXT_VALID | NES_CQP_QP_IWARP_STATE_RTS);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ nes_debug(NES_DBG_PAU, "Waiting for CQP completion for adding the quad hash.\n");
+
+ new_request->cqp_callback_pointer = qh_chg;
+ new_request->callback = 1;
+ new_request->cqp_callback = nes_chg_qh_handler;
+ atomic_set(&new_request->refcount, 1);
+ nes_post_cqp_request(nesdev, new_request);
+ break;
+
+ case PAU_ADD_LB_QH:
+ /* Start processing the queued fpdu's */
+ nesqp->pau_state = PAU_READY;
+ process_fpdus(qh_chg->nesvnic, qh_chg->nesqp);
+ kfree(qh_chg);
+ break;
+ }
+}
+
+/**
+ * nes_change_quad_hash
+ */
+static int nes_change_quad_hash(struct nes_device *nesdev,
+ struct nes_vnic *nesvnic, struct nes_qp *nesqp)
+{
+ struct nes_cqp_request *cqp_request = NULL;
+ struct pau_qh_chg *qh_chg = NULL;
+ u64 u64temp;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ int ret = 0;
+
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ ret = -ENOMEM;
+ goto chg_qh_err;
+ }
+
+ qh_chg = kmalloc(sizeof *qh_chg, GFP_ATOMIC);
+ if (qh_chg == NULL) {
+ nes_debug(NES_DBG_PAU, "Failed to get a cqp_request.\n");
+ ret = -ENOMEM;
+ goto chg_qh_err;
+ }
+ qh_chg->nesdev = nesdev;
+ qh_chg->nesvnic = nesvnic;
+ qh_chg->nesqp = nesqp;
+ nesqp->pau_state = PAU_DEL_QH;
+
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words,
+ NES_CQP_WQE_OPCODE_IDX, NES_CQP_MANAGE_QUAD_HASH | NES_CQP_QP_DEL_HTE |
+ NES_CQP_QP_TYPE_IWARP | NES_CQP_QP_CONTEXT_VALID | NES_CQP_QP_IWARP_STATE_RTS);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ nes_debug(NES_DBG_PAU, "Waiting for CQP completion for deleting the quad hash.\n");
+
+ cqp_request->cqp_callback_pointer = qh_chg;
+ cqp_request->callback = 1;
+ cqp_request->cqp_callback = nes_chg_qh_handler;
+ atomic_set(&cqp_request->refcount, 1);
+ nes_post_cqp_request(nesdev, cqp_request);
+
+ return ret;
+
+chg_qh_err:
+ kfree(qh_chg);
+ if (cqp_request)
+ nes_put_cqp_request(nesdev, cqp_request);
+ return ret;
+}
+
+/**
+ * nes_mgt_ce_handler
+ * This management code deals with any packed and unaligned (pau) fpdu's
+ * that the hardware cannot handle.
+ */
+static void nes_mgt_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
+{
+ struct nes_vnic_mgt *mgtvnic = container_of(cq, struct nes_vnic_mgt, mgt_cq);
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 head;
+ u32 cq_size;
+ u32 cqe_count = 0;
+ u32 cqe_misc;
+ u32 qp_id = 0;
+ u32 skbs_needed;
+ unsigned long context;
+ struct nes_qp *nesqp;
+ struct sk_buff *rx_skb;
+ struct nes_rskb_cb *cb;
+
+ head = cq->cq_head;
+ cq_size = cq->cq_size;
+
+ while (1) {
+ cqe_misc = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]);
+ if (!(cqe_misc & NES_NIC_CQE_VALID))
+ break;
+
+ nesqp = NULL;
+ if (cqe_misc & NES_NIC_CQE_ACCQP_VALID) {
+ qp_id = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_ACCQP_ID_IDX]);
+ qp_id &= 0x001fffff;
+ if (qp_id < nesadapter->max_qp) {
+ context = (unsigned long)nesadapter->qp_table[qp_id - NES_FIRST_QPN];
+ nesqp = (struct nes_qp *)context;
+ }
+ }
+
+ if (nesqp) {
+ if (nesqp->pau_mode == false) {
+ nesqp->pau_mode = true; /* First time for this qp */
+ nesqp->pau_rcv_nxt = le32_to_cpu(
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_HASH_RCVNXT]);
+ skb_queue_head_init(&nesqp->pau_list);
+ spin_lock_init(&nesqp->pau_lock);
+ atomic_inc(&pau_qps_created);
+ nes_change_quad_hash(nesdev, mgtvnic->nesvnic, nesqp);
+ }
+
+ rx_skb = mgtvnic->mgt.rx_skb[mgtvnic->mgt.rq_tail];
+ rx_skb->len = 0;
+ skb_put(rx_skb, cqe_misc & 0x0000ffff);
+ rx_skb->protocol = eth_type_trans(rx_skb, mgtvnic->nesvnic->netdev);
+ cb = (struct nes_rskb_cb *)&rx_skb->cb[0];
+ pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen, PCI_DMA_FROMDEVICE);
+ cb->busaddr = 0;
+ mgtvnic->mgt.rq_tail++;
+ mgtvnic->mgt.rq_tail &= mgtvnic->mgt.rq_size - 1;
+
+ nes_add_ref_cm_node(nesqp->cm_node);
+ nes_queue_mgt_skbs(rx_skb, mgtvnic->nesvnic, nesqp);
+ } else {
+ printk(KERN_ERR PFX "Invalid QP %d for packed/unaligned handling\n", qp_id);
+ }
+
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX] = 0;
+ cqe_count++;
+ if (++head >= cq_size)
+ head = 0;
+
+ if (cqe_count == 255) {
+ /* Replenish mgt CQ */
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, cq->cq_number | (cqe_count << 16));
+ nesdev->currcq_count += cqe_count;
+ cqe_count = 0;
+ }
+
+ skbs_needed = atomic_inc_return(&mgtvnic->rx_skbs_needed);
+ if (skbs_needed > (mgtvnic->mgt.rq_size >> 1))
+ nes_replenish_mgt_rq(mgtvnic);
+ }
+
+ cq->cq_head = head;
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ cq->cq_number | (cqe_count << 16));
+ nes_read32(nesdev->regs + NES_CQE_ALLOC);
+ nesdev->currcq_count += cqe_count;
+}
+
+/**
+ * nes_init_mgt_qp
+ */
+int nes_init_mgt_qp(struct nes_device *nesdev, struct net_device *netdev, struct nes_vnic *nesvnic)
+{
+ struct nes_vnic_mgt *mgtvnic;
+ u32 counter;
+ void *vmem;
+ dma_addr_t pmem;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 cqp_head;
+ unsigned long flags;
+ struct nes_hw_nic_qp_context *mgt_context;
+ u64 u64temp;
+ struct nes_hw_nic_rq_wqe *mgt_rqe;
+ struct sk_buff *skb;
+ u32 wqe_count;
+ struct nes_rskb_cb *cb;
+ u32 mgt_mem_size;
+ void *mgt_vbase;
+ dma_addr_t mgt_pbase;
+ int i;
+ int ret;
+
+ /* Allocate space the all mgt QPs once */
+ mgtvnic = kzalloc(NES_MGT_QP_COUNT * sizeof(struct nes_vnic_mgt), GFP_KERNEL);
+ if (mgtvnic == NULL) {
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for mgt structure\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate fragment, RQ, and CQ; Reuse CEQ based on the PCI function */
+ /* We are not sending from this NIC so sq is not allocated */
+ mgt_mem_size = 256 +
+ (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe)) +
+ (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_cqe)) +
+ sizeof(struct nes_hw_nic_qp_context);
+ mgt_mem_size = (mgt_mem_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ mgt_vbase = pci_alloc_consistent(nesdev->pcidev, NES_MGT_QP_COUNT * mgt_mem_size, &mgt_pbase);
+ if (!mgt_vbase) {
+ kfree(mgtvnic);
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for mgt host descriptor rings\n");
+ return -ENOMEM;
+ }
+
+ nesvnic->mgt_mem_size = NES_MGT_QP_COUNT * mgt_mem_size;
+ nesvnic->mgt_vbase = mgt_vbase;
+ nesvnic->mgt_pbase = mgt_pbase;
+
+ skb_queue_head_init(&nesvnic->mgt_skb_list);
+ init_waitqueue_head(&nesvnic->mgt_wait_queue);
+ nesvnic->mgt_thread = kthread_run(mgt_thread, nesvnic, "nes_mgt_thread");
+
+ for (i = 0; i < NES_MGT_QP_COUNT; i++) {
+ mgtvnic->nesvnic = nesvnic;
+ mgtvnic->mgt.qp_id = nesdev->mac_index + NES_MGT_QP_OFFSET + i;
+ memset(mgt_vbase, 0, mgt_mem_size);
+ nes_debug(NES_DBG_INIT, "Allocated mgt QP structures at %p (phys = %016lX), size = %u.\n",
+ mgt_vbase, (unsigned long)mgt_pbase, mgt_mem_size);
+
+ vmem = (void *)(((unsigned long)mgt_vbase + (256 - 1)) &
+ ~(unsigned long)(256 - 1));
+ pmem = (dma_addr_t)(((unsigned long long)mgt_pbase + (256 - 1)) &
+ ~(unsigned long long)(256 - 1));
+
+ spin_lock_init(&mgtvnic->mgt.rq_lock);
+
+ /* setup the RQ */
+ mgtvnic->mgt.rq_vbase = vmem;
+ mgtvnic->mgt.rq_pbase = pmem;
+ mgtvnic->mgt.rq_head = 0;
+ mgtvnic->mgt.rq_tail = 0;
+ mgtvnic->mgt.rq_size = NES_MGT_WQ_COUNT;
+
+ /* setup the CQ */
+ vmem += (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe));
+ pmem += (NES_MGT_WQ_COUNT * sizeof(struct nes_hw_nic_rq_wqe));
+
+ mgtvnic->mgt_cq.cq_number = mgtvnic->mgt.qp_id;
+ mgtvnic->mgt_cq.cq_vbase = vmem;
+ mgtvnic->mgt_cq.cq_pbase = pmem;
+ mgtvnic->mgt_cq.cq_head = 0;
+ mgtvnic->mgt_cq.cq_size = NES_MGT_WQ_COUNT;
+
+ mgtvnic->mgt_cq.ce_handler = nes_mgt_ce_handler;
+
+ /* Send CreateCQ request to CQP */
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_head = nesdev->cqp.sq_head;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(
+ NES_CQP_CREATE_CQ | NES_CQP_CQ_CEQ_VALID |
+ ((u32)mgtvnic->mgt_cq.cq_size << 16));
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(
+ mgtvnic->mgt_cq.cq_number | ((u32)nesdev->ceq_index << 16));
+ u64temp = (u64)mgtvnic->mgt_cq.cq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] = 0;
+ u64temp = (unsigned long)&mgtvnic->mgt_cq;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX] = cpu_to_le32((u32)(u64temp >> 1));
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] =
+ cpu_to_le32(((u32)((u64temp) >> 33)) & 0x7FFFFFFF);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX] = 0;
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ /* Send CreateQP request to CQP */
+ mgt_context = (void *)(&mgtvnic->mgt_cq.cq_vbase[mgtvnic->mgt_cq.cq_size]);
+ mgt_context->context_words[NES_NIC_CTX_MISC_IDX] =
+ cpu_to_le32((u32)NES_MGT_CTX_SIZE |
+ ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 12));
+ nes_debug(NES_DBG_INIT, "RX_WINDOW_BUFFER_PAGE_TABLE_SIZE = 0x%08X, RX_WINDOW_BUFFER_SIZE = 0x%08X\n",
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_PAGE_TABLE_SIZE),
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE));
+ if (nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE) != 0)
+ mgt_context->context_words[NES_NIC_CTX_MISC_IDX] |= cpu_to_le32(NES_NIC_BACK_STORE);
+
+ u64temp = (u64)mgtvnic->mgt.rq_pbase;
+ mgt_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ mgt_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+ u64temp = (u64)mgtvnic->mgt.rq_pbase;
+ mgt_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ mgt_context->context_words[NES_NIC_CTX_RQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_CREATE_QP |
+ NES_CQP_QP_TYPE_NIC);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(mgtvnic->mgt.qp_id);
+ u64temp = (u64)mgtvnic->mgt_cq.cq_pbase +
+ (mgtvnic->mgt_cq.cq_size * sizeof(struct nes_hw_nic_cqe));
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ nesdev->cqp.sq_head = cqp_head;
+
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_INIT, "Waiting for create MGT QP%u to complete.\n",
+ mgtvnic->mgt.qp_id);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_INIT, "Create MGT QP%u completed, wait_event_timeout ret = %u.\n",
+ mgtvnic->mgt.qp_id, ret);
+ if (!ret) {
+ nes_debug(NES_DBG_INIT, "MGT QP%u create timeout expired\n", mgtvnic->mgt.qp_id);
+ if (i == 0) {
+ pci_free_consistent(nesdev->pcidev, nesvnic->mgt_mem_size, nesvnic->mgt_vbase,
+ nesvnic->mgt_pbase);
+ kfree(mgtvnic);
+ } else {
+ nes_destroy_mgt(nesvnic);
+ }
+ return -EIO;
+ }
+
+ /* Populate the RQ */
+ for (counter = 0; counter < (NES_MGT_WQ_COUNT - 1); counter++) {
+ skb = dev_alloc_skb(nesvnic->max_frame_size);
+ if (!skb) {
+ nes_debug(NES_DBG_INIT, "%s: out of memory for receive skb\n", netdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = netdev;
+
+ pmem = pci_map_single(nesdev->pcidev, skb->data,
+ nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ cb = (struct nes_rskb_cb *)&skb->cb[0];
+ cb->busaddr = pmem;
+ cb->maplen = nesvnic->max_frame_size;
+
+ mgt_rqe = &mgtvnic->mgt.rq_vbase[counter];
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32((u32)nesvnic->max_frame_size);
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] = cpu_to_le32((u32)pmem);
+ mgt_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] = cpu_to_le32((u32)((u64)pmem >> 32));
+ mgtvnic->mgt.rx_skb[counter] = skb;
+ }
+
+ init_timer(&mgtvnic->rq_wqes_timer);
+ mgtvnic->rq_wqes_timer.function = nes_mgt_rq_wqes_timeout;
+ mgtvnic->rq_wqes_timer.data = (unsigned long)mgtvnic;
+
+ wqe_count = NES_MGT_WQ_COUNT - 1;
+ mgtvnic->mgt.rq_head = wqe_count;
+ barrier();
+ do {
+ counter = min(wqe_count, ((u32)255));
+ wqe_count -= counter;
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, (counter << 24) | mgtvnic->mgt.qp_id);
+ } while (wqe_count);
+
+ nes_write32(nesdev->regs + NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ mgtvnic->mgt_cq.cq_number);
+ nes_read32(nesdev->regs + NES_CQE_ALLOC);
+
+ mgt_vbase += mgt_mem_size;
+ mgt_pbase += mgt_mem_size;
+ nesvnic->mgtvnic[i] = mgtvnic++;
+ }
+ return 0;
+}
+
+
+void nes_destroy_mgt(struct nes_vnic *nesvnic)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_vnic_mgt *mgtvnic;
+ struct nes_vnic_mgt *first_mgtvnic;
+ unsigned long flags;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 cqp_head;
+ struct sk_buff *rx_skb;
+ int i;
+ int ret;
+
+ kthread_stop(nesvnic->mgt_thread);
+
+ /* Free remaining NIC receive buffers */
+ first_mgtvnic = nesvnic->mgtvnic[0];
+ for (i = 0; i < NES_MGT_QP_COUNT; i++) {
+ mgtvnic = nesvnic->mgtvnic[i];
+ if (mgtvnic == NULL)
+ continue;
+
+ while (mgtvnic->mgt.rq_head != mgtvnic->mgt.rq_tail) {
+ rx_skb = mgtvnic->mgt.rx_skb[mgtvnic->mgt.rq_tail];
+ nes_mgt_free_skb(nesdev, rx_skb, PCI_DMA_FROMDEVICE);
+ mgtvnic->mgt.rq_tail++;
+ mgtvnic->mgt.rq_tail &= (mgtvnic->mgt.rq_size - 1);
+ }
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+
+ /* Destroy NIC QP */
+ cqp_head = nesdev->cqp.sq_head;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_NIC));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ mgtvnic->mgt.qp_id);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+
+ /* Destroy NIC CQ */
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_CQ | ((u32)mgtvnic->mgt_cq.cq_size << 16)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (mgtvnic->mgt_cq.cq_number | ((u32)nesdev->ceq_index << 16)));
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ nesdev->cqp.sq_head = cqp_head;
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs + NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_SHUTDOWN, "Waiting for CQP, cqp_head=%u, cqp.sq_head=%u,"
+ " cqp.sq_tail=%u, cqp.sq_size=%u\n",
+ cqp_head, nesdev->cqp.sq_head,
+ nesdev->cqp.sq_tail, nesdev->cqp.sq_size);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+
+ nes_debug(NES_DBG_SHUTDOWN, "Destroy MGT QP returned, wait_event_timeout ret = %u, cqp_head=%u,"
+ " cqp.sq_head=%u, cqp.sq_tail=%u\n",
+ ret, cqp_head, nesdev->cqp.sq_head, nesdev->cqp.sq_tail);
+ if (!ret)
+ nes_debug(NES_DBG_SHUTDOWN, "MGT QP%u destroy timeout expired\n",
+ mgtvnic->mgt.qp_id);
+
+ nesvnic->mgtvnic[i] = NULL;
+ }
+
+ if (nesvnic->mgt_vbase) {
+ pci_free_consistent(nesdev->pcidev, nesvnic->mgt_mem_size, nesvnic->mgt_vbase,
+ nesvnic->mgt_pbase);
+ nesvnic->mgt_vbase = NULL;
+ nesvnic->mgt_pbase = 0;
+ }
+
+ kfree(first_mgtvnic);
+}
diff --git a/drivers/infiniband/hw/nes/nes_mgt.h b/drivers/infiniband/hw/nes/nes_mgt.h
new file mode 100644
index 000000000000..8c8af254555a
--- /dev/null
+++ b/drivers/infiniband/hw/nes/nes_mgt.h
@@ -0,0 +1,97 @@
+/*
+* Copyright (c) 2010 Intel-NE, Inc. All rights reserved.
+*
+* This software is available to you under a choice of one of two
+* licenses. You may choose to be licensed under the terms of the GNU
+* General Public License (GPL) Version 2, available from the file
+* COPYING in the main directory of this source tree, or the
+* OpenIB.org BSD license below:
+*
+* Redistribution and use in source and binary forms, with or
+* without modification, are permitted provided that the following
+* conditions are met:
+*
+* - Redistributions of source code must retain the above
+* copyright notice, this list of conditions and the following
+* disclaimer.
+*
+* - Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials
+* provided with the distribution.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+#ifndef __NES_MGT_H
+#define __NES_MGT_H
+
+#define MPA_FRAMING 6 /* length is 2 bytes, crc is 4 bytes */
+
+int nes_init_mgt_qp(struct nes_device *nesdev, struct net_device *netdev, struct nes_vnic *nesvnic);
+void nes_queue_mgt_skbs(struct sk_buff *skb, struct nes_vnic *nesvnic, struct nes_qp *nesqp);
+void nes_destroy_mgt(struct nes_vnic *nesvnic);
+void nes_destroy_pau_qp(struct nes_device *nesdev, struct nes_qp *nesqp);
+
+struct nes_hw_mgt {
+ struct nes_hw_nic_rq_wqe *rq_vbase; /* virtual address of rq */
+ dma_addr_t rq_pbase; /* PCI memory for host rings */
+ struct sk_buff *rx_skb[NES_NIC_WQ_SIZE];
+ u16 qp_id;
+ u16 sq_head;
+ u16 rq_head;
+ u16 rq_tail;
+ u16 rq_size;
+ u8 replenishing_rq;
+ u8 reserved;
+ spinlock_t rq_lock;
+};
+
+struct nes_vnic_mgt {
+ struct nes_vnic *nesvnic;
+ struct nes_hw_mgt mgt;
+ struct nes_hw_nic_cq mgt_cq;
+ atomic_t rx_skbs_needed;
+ struct timer_list rq_wqes_timer;
+ atomic_t rx_skb_timer_running;
+};
+
+#define MAX_FPDU_FRAGS 4
+struct pau_fpdu_frag {
+ struct sk_buff *skb;
+ u64 physaddr;
+ u32 frag_len;
+ bool cmplt;
+};
+
+struct pau_fpdu_info {
+ struct nes_qp *nesqp;
+ struct nes_cqp_request *cqp_request;
+ void *hdr_vbase;
+ dma_addr_t hdr_pbase;
+ int hdr_len;
+ u16 data_len;
+ u16 frag_cnt;
+ struct pau_fpdu_frag frags[MAX_FPDU_FRAGS];
+};
+
+enum pau_qh_state {
+ PAU_DEL_QH,
+ PAU_ADD_LB_QH,
+ PAU_READY
+};
+
+struct pau_qh_chg {
+ struct nes_device *nesdev;
+ struct nes_vnic *nesvnic;
+ struct nes_qp *nesqp;
+};
+
+#endif /* __NES_MGT_H */
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index 47b2ee4c01e2..c00d2f3f8966 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -1091,6 +1091,8 @@ static const char nes_ethtool_stringset[][ETH_GSTRING_LEN] = {
"LRO aggregated",
"LRO flushed",
"LRO no_desc",
+ "PAU CreateQPs",
+ "PAU DestroyQPs",
};
#define NES_ETHTOOL_STAT_COUNT ARRAY_SIZE(nes_ethtool_stringset)
@@ -1306,6 +1308,8 @@ static void nes_netdev_get_ethtool_stats(struct net_device *netdev,
target_stat_values[++index] = nesvnic->lro_mgr.stats.aggregated;
target_stat_values[++index] = nesvnic->lro_mgr.stats.flushed;
target_stat_values[++index] = nesvnic->lro_mgr.stats.no_desc;
+ target_stat_values[++index] = atomic_read(&pau_qps_created);
+ target_stat_values[++index] = atomic_read(&pau_qps_destroyed);
}
/**
diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c
index f9c417c6b3b3..cd10968bfa22 100644
--- a/drivers/infiniband/hw/nes/nes_utils.c
+++ b/drivers/infiniband/hw/nes/nes_utils.c
@@ -51,13 +51,34 @@
#include "nes.h"
-
-
static u16 nes_read16_eeprom(void __iomem *addr, u16 offset);
u32 mh_detected;
u32 mh_pauses_sent;
+u32 nes_set_pau(struct nes_device *nesdev)
+{
+ u32 ret = 0;
+ u32 counter;
+
+ nes_write_indexed(nesdev, NES_IDX_GPR2, NES_ENABLE_PAU);
+ nes_write_indexed(nesdev, NES_IDX_GPR_TRIGGER, 1);
+
+ for (counter = 0; counter < NES_PAU_COUNTER; counter++) {
+ udelay(30);
+ if (!nes_read_indexed(nesdev, NES_IDX_GPR2)) {
+ printk(KERN_INFO PFX "PAU is supported.\n");
+ break;
+ }
+ nes_write_indexed(nesdev, NES_IDX_GPR_TRIGGER, 1);
+ }
+ if (counter == NES_PAU_COUNTER) {
+ printk(KERN_INFO PFX "PAU is not supported.\n");
+ return -EPERM;
+ }
+ return ret;
+}
+
/**
* nes_read_eeprom_values -
*/
@@ -187,6 +208,11 @@ int nes_read_eeprom_values(struct nes_device *nesdev, struct nes_adapter *nesada
if (((major_ver == 3) && (minor_ver >= 16)) || (major_ver > 3))
nesadapter->send_term_ok = 1;
+ if (nes_drv_opt & NES_DRV_OPT_ENABLE_PAU) {
+ if (!nes_set_pau(nesdev))
+ nesadapter->allow_unaligned_fpdus = 1;
+ }
+
nesadapter->firmware_version = (((u32)(u8)(eeprom_data>>8)) << 16) +
(u32)((u8)eeprom_data);
@@ -594,6 +620,7 @@ void nes_put_cqp_request(struct nes_device *nesdev,
nes_free_cqp_request(nesdev, cqp_request);
}
+
/**
* nes_post_cqp_request
*/
@@ -604,6 +631,8 @@ void nes_post_cqp_request(struct nes_device *nesdev,
unsigned long flags;
u32 cqp_head;
u64 u64temp;
+ u32 opcode;
+ int ctx_index = NES_CQP_WQE_COMP_CTX_LOW_IDX;
spin_lock_irqsave(&nesdev->cqp.lock, flags);
@@ -614,17 +643,20 @@ void nes_post_cqp_request(struct nes_device *nesdev,
nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
+ opcode = le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX]);
+ if ((opcode & NES_CQP_OPCODE_MASK) == NES_CQP_DOWNLOAD_SEGMENT)
+ ctx_index = NES_CQP_WQE_DL_COMP_CTX_LOW_IDX;
barrier();
u64temp = (unsigned long)cqp_request;
- set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_SCRATCH_LOW_IDX,
- u64temp);
+ set_wqe_64bit_value(cqp_wqe->wqe_words, ctx_index, u64temp);
nes_debug(NES_DBG_CQP, "CQP request (opcode 0x%02X), line 1 = 0x%08X put on CQPs SQ,"
- " request = %p, cqp_head = %u, cqp_tail = %u, cqp_size = %u,"
- " waiting = %d, refcount = %d.\n",
- le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f,
- le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX]), cqp_request,
- nesdev->cqp.sq_head, nesdev->cqp.sq_tail, nesdev->cqp.sq_size,
- cqp_request->waiting, atomic_read(&cqp_request->refcount));
+ " request = %p, cqp_head = %u, cqp_tail = %u, cqp_size = %u,"
+ " waiting = %d, refcount = %d.\n",
+ opcode & NES_CQP_OPCODE_MASK,
+ le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX]), cqp_request,
+ nesdev->cqp.sq_head, nesdev->cqp.sq_tail, nesdev->cqp.sq_size,
+ cqp_request->waiting, atomic_read(&cqp_request->refcount));
+
barrier();
/* Ring doorbell (1 WQEs) */
@@ -645,7 +677,6 @@ void nes_post_cqp_request(struct nes_device *nesdev,
return;
}
-
/**
* nes_arp_table
*/
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 9f2f7d4b1197..5095bc41c6cc 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1458,7 +1458,7 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
struct ib_qp_attr attr;
struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
- int ret;
+ int ret = 0;
atomic_inc(&sw_qps_destroyed);
nesqp->destroyed = 1;
@@ -1511,7 +1511,6 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
if ((nesqp->nesrcq) && (nesqp->nesrcq != nesqp->nesscq))
nes_clean_cq(nesqp, nesqp->nesrcq);
}
-
nes_rem_ref(&nesqp->ibqp);
return 0;
}
@@ -2338,8 +2337,10 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
skip_pages = ((u32)region->offset) >> 12;
- if (ib_copy_from_udata(&req, udata, sizeof(req)))
+ if (ib_copy_from_udata(&req, udata, sizeof(req))) {
+ ib_umem_release(region);
return ERR_PTR(-EFAULT);
+ }
nes_debug(NES_DBG_MR, "Memory Registration type = %08X.\n", req.reg_type);
switch (req.reg_type) {
@@ -2631,6 +2632,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return &nesmr->ibmr;
}
+ ib_umem_release(region);
return ERR_PTR(-ENOSYS);
}
diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
index 2df9993e0cac..fe6b6e92fa90 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.h
+++ b/drivers/infiniband/hw/nes/nes_verbs.h
@@ -139,7 +139,8 @@ struct nes_qp {
struct nes_cq *nesrcq;
struct nes_pd *nespd;
void *cm_node; /* handle of the node this QP is associated with */
- struct ietf_mpa_frame *ietf_frame;
+ void *ietf_frame;
+ u8 ietf_frame_size;
dma_addr_t ietf_frame_pbase;
struct ib_mr *lsmm_mr;
struct nes_hw_qp hwqp;
@@ -154,6 +155,7 @@ struct nes_qp {
u32 mmap_sq_db_index;
u32 mmap_rq_db_index;
spinlock_t lock;
+ spinlock_t pau_lock;
struct nes_qp_context *nesqp_context;
dma_addr_t nesqp_context_pbase;
void *pbl_vbase;
@@ -161,6 +163,8 @@ struct nes_qp {
struct page *page;
struct timer_list terminate_timer;
enum ib_event_type terminate_eventtype;
+ struct sk_buff_head pau_list;
+ u32 pau_rcv_nxt;
u16 active_conn:1;
u16 skip_lsmm:1;
u16 user_mode:1;
@@ -168,7 +172,8 @@ struct nes_qp {
u16 flush_issued:1;
u16 destroyed:1;
u16 sig_all:1;
- u16 rsvd:9;
+ u16 pau_mode:1;
+ u16 rsvd:8;
u16 private_data_len;
u16 term_sq_flush_code;
u16 term_rq_flush_code;
@@ -176,5 +181,8 @@ struct nes_qp {
u8 hw_tcp_state;
u8 term_flags;
u8 sq_kmapped;
+ u8 pau_busy;
+ u8 pau_pending;
+ u8 pau_state;
};
#endif /* NES_VERBS_H */
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index c9624ea87209..b881bdc401f5 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -171,7 +171,9 @@ struct qib_ctxtdata {
/* how many alloc_pages() chunks in rcvegrbuf_pages */
u32 rcvegrbuf_chunks;
/* how many egrbufs per chunk */
- u32 rcvegrbufs_perchunk;
+ u16 rcvegrbufs_perchunk;
+ /* ilog2 of above */
+ u16 rcvegrbufs_perchunk_shift;
/* order for rcvegrbuf_pages */
size_t rcvegrbuf_size;
/* rcvhdrq size (for freeing) */
@@ -221,6 +223,9 @@ struct qib_ctxtdata {
/* ctxt rcvhdrq head offset */
u32 head;
u32 pkt_count;
+ /* lookaside fields */
+ struct qib_qp *lookaside_qp;
+ u32 lookaside_qpn;
/* QPs waiting for context processing */
struct list_head qp_wait_list;
};
@@ -807,6 +812,10 @@ struct qib_devdata {
* supports, less gives more pio bufs/ctxt, etc.
*/
u32 cfgctxts;
+ /*
+ * number of ctxts available for PSM open
+ */
+ u32 freectxts;
/*
* hint that we should update pioavailshadow before
@@ -936,7 +945,9 @@ struct qib_devdata {
/* chip address space used by 4k pio buffers */
u32 align4k;
/* size of each rcvegrbuffer */
- u32 rcvegrbufsize;
+ u16 rcvegrbufsize;
+ /* log2 of above */
+ u16 rcvegrbufsize_shift;
/* localbus width (1, 2,4,8,16,32) from config space */
u32 lbus_width;
/* localbus speed in MHz */
diff --git a/drivers/infiniband/hw/qib/qib_driver.c b/drivers/infiniband/hw/qib/qib_driver.c
index 23e584f4c36c..9a9047f385ae 100644
--- a/drivers/infiniband/hw/qib/qib_driver.c
+++ b/drivers/infiniband/hw/qib/qib_driver.c
@@ -279,10 +279,10 @@ bail:
*/
static inline void *qib_get_egrbuf(const struct qib_ctxtdata *rcd, u32 etail)
{
- const u32 chunk = etail / rcd->rcvegrbufs_perchunk;
- const u32 idx = etail % rcd->rcvegrbufs_perchunk;
+ const u32 chunk = etail >> rcd->rcvegrbufs_perchunk_shift;
+ const u32 idx = etail & ((u32)rcd->rcvegrbufs_perchunk - 1);
- return rcd->rcvegrbuf[chunk] + idx * rcd->dd->rcvegrbufsize;
+ return rcd->rcvegrbuf[chunk] + (idx << rcd->dd->rcvegrbufsize_shift);
}
/*
@@ -310,7 +310,6 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
u32 opcode;
u32 psn;
int diff;
- unsigned long flags;
/* Sanity check packet */
if (tlen < 24)
@@ -365,7 +364,6 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
switch (qp->ibqp.qp_type) {
case IB_QPT_RC:
- spin_lock_irqsave(&qp->s_lock, flags);
ruc_res =
qib_ruc_check_hdr(
ibp, hdr,
@@ -373,11 +371,8 @@ static u32 qib_rcv_hdrerr(struct qib_ctxtdata *rcd, struct qib_pportdata *ppd,
qp,
be32_to_cpu(ohdr->bth[0]));
if (ruc_res) {
- spin_unlock_irqrestore(&qp->s_lock,
- flags);
goto unlock;
}
- spin_unlock_irqrestore(&qp->s_lock, flags);
/* Only deal with RDMA Writes for now */
if (opcode <
@@ -547,6 +542,15 @@ move_along:
updegr = 0;
}
}
+ /*
+ * Notify qib_destroy_qp() if it is waiting
+ * for lookaside_qp to finish.
+ */
+ if (rcd->lookaside_qp) {
+ if (atomic_dec_and_test(&rcd->lookaside_qp->refcount))
+ wake_up(&rcd->lookaside_qp->wait);
+ rcd->lookaside_qp = NULL;
+ }
rcd->head = l;
rcd->pkt_count += i;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 26253039d2c7..77633666f81c 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -1284,6 +1284,7 @@ static int setup_ctxt(struct qib_pportdata *ppd, int ctxt,
strlcpy(rcd->comm, current->comm, sizeof(rcd->comm));
ctxt_fp(fp) = rcd;
qib_stats.sps_ctxts++;
+ dd->freectxts++;
ret = 0;
goto bail;
@@ -1792,6 +1793,7 @@ static int qib_close(struct inode *in, struct file *fp)
if (dd->pageshadow)
unlock_expected_tids(rcd);
qib_stats.sps_ctxts--;
+ dd->freectxts--;
}
mutex_unlock(&qib_mutex);
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index d8ca0a0b970d..781a802a321f 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -3273,6 +3273,8 @@ static int init_6120_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
ret = ib_mtu_enum_to_int(qib_ibmtu);
dd->rcvegrbufsize = ret != -1 ? max(ret, 2048) : QIB_DEFAULT_MTU;
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_6120_tidtemplate(dd);
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index e1f947446c2a..3f1d562ba898 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -4085,6 +4085,8 @@ static int qib_init_7220_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
ret = ib_mtu_enum_to_int(qib_ibmtu);
dd->rcvegrbufsize = ret != -1 ? max(ret, 2048) : QIB_DEFAULT_MTU;
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_7220_tidtemplate(dd);
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 5ea9ece23b33..efd0a110091f 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -2310,12 +2310,15 @@ static int qib_7322_bringup_serdes(struct qib_pportdata *ppd)
val = ppd->cpspec->ibcctrl_a | (QLOGIC_IB_IBCC_LINKINITCMD_DISABLE <<
QLOGIC_IB_IBCC_LINKINITCMD_SHIFT);
+ ppd->cpspec->ibcctrl_a = val;
/*
* Reset the PCS interface to the serdes (and also ibc, which is still
* in reset from above). Writes new value of ibcctrl_a as last step.
*/
qib_7322_mini_pcs_reset(ppd);
qib_write_kreg(dd, kr_scratch, 0ULL);
+ /* clear the linkinit cmds */
+ ppd->cpspec->ibcctrl_a &= ~SYM_MASK(IBCCtrlA_0, LinkInitCmd);
if (!ppd->cpspec->ibcctrl_b) {
unsigned lse = ppd->link_speed_enabled;
@@ -2387,11 +2390,6 @@ static int qib_7322_bringup_serdes(struct qib_pportdata *ppd)
qib_write_kreg_port(ppd, krp_rcvctrl, ppd->p_rcvctrl);
spin_unlock_irqrestore(&dd->cspec->rcvmod_lock, flags);
- /* Hold the link state machine for mezz boards */
- if (IS_QMH(dd) || IS_QME(dd))
- qib_set_ib_7322_lstate(ppd, 0,
- QLOGIC_IB_IBCC_LINKINITCMD_DISABLE);
-
/* Also enable IBSTATUSCHG interrupt. */
val = qib_read_kreg_port(ppd, krp_errmask);
qib_write_kreg_port(ppd, krp_errmask,
@@ -2853,9 +2851,8 @@ static irqreturn_t qib_7322intr(int irq, void *data)
for (i = 0; i < dd->first_user_ctxt; i++) {
if (ctxtrbits & rmask) {
ctxtrbits &= ~rmask;
- if (dd->rcd[i]) {
+ if (dd->rcd[i])
qib_kreceive(dd->rcd[i], NULL, &npkts);
- }
}
rmask <<= 1;
}
@@ -5230,6 +5227,8 @@ static int qib_7322_ib_updown(struct qib_pportdata *ppd, int ibup, u64 ibcs)
QIBL_IB_AUTONEG_INPROG)))
set_7322_ibspeed_fast(ppd, ppd->link_speed_enabled);
if (!(ppd->lflags & QIBL_IB_AUTONEG_INPROG)) {
+ struct qib_qsfp_data *qd =
+ &ppd->cpspec->qsfp_data;
/* unlock the Tx settings, speed may change */
qib_write_kreg_port(ppd, krp_tx_deemph_override,
SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
@@ -5237,6 +5236,12 @@ static int qib_7322_ib_updown(struct qib_pportdata *ppd, int ibup, u64 ibcs)
qib_cancel_sends(ppd);
/* on link down, ensure sane pcs state */
qib_7322_mini_pcs_reset(ppd);
+ /* schedule the qsfp refresh which should turn the link
+ off */
+ if (ppd->dd->flags & QIB_HAS_QSFP) {
+ qd->t_insert = get_jiffies_64();
+ schedule_work(&qd->work);
+ }
spin_lock_irqsave(&ppd->sdma_lock, flags);
if (__qib_sdma_running(ppd))
__qib_sdma_process_event(ppd,
@@ -5587,43 +5592,79 @@ static void qsfp_7322_event(struct work_struct *work)
struct qib_qsfp_data *qd;
struct qib_pportdata *ppd;
u64 pwrup;
+ unsigned long flags;
int ret;
u32 le2;
qd = container_of(work, struct qib_qsfp_data, work);
ppd = qd->ppd;
- pwrup = qd->t_insert + msecs_to_jiffies(QSFP_PWR_LAG_MSEC);
+ pwrup = qd->t_insert +
+ msecs_to_jiffies(QSFP_PWR_LAG_MSEC - QSFP_MODPRS_LAG_MSEC);
- /*
- * Some QSFP's not only do not respond until the full power-up
- * time, but may behave badly if we try. So hold off responding
- * to insertion.
- */
- while (1) {
- u64 now = get_jiffies_64();
- if (time_after64(now, pwrup))
- break;
- msleep(20);
- }
- ret = qib_refresh_qsfp_cache(ppd, &qd->cache);
- /*
- * Need to change LE2 back to defaults if we couldn't
- * read the cable type (to handle cable swaps), so do this
- * even on failure to read cable information. We don't
- * get here for QME, so IS_QME check not needed here.
- */
- if (!ret && !ppd->dd->cspec->r1) {
- if (QSFP_IS_ACTIVE_FAR(qd->cache.tech))
- le2 = LE2_QME;
- else if (qd->cache.atten[1] >= qib_long_atten &&
- QSFP_IS_CU(qd->cache.tech))
- le2 = LE2_5m;
- else
+ /* Delay for 20 msecs to allow ModPrs resistor to setup */
+ mdelay(QSFP_MODPRS_LAG_MSEC);
+
+ if (!qib_qsfp_mod_present(ppd)) {
+ ppd->cpspec->qsfp_data.modpresent = 0;
+ /* Set the physical link to disabled */
+ qib_set_ib_7322_lstate(ppd, 0,
+ QLOGIC_IB_IBCC_LINKINITCMD_DISABLE);
+ spin_lock_irqsave(&ppd->lflags_lock, flags);
+ ppd->lflags &= ~QIBL_LINKV;
+ spin_unlock_irqrestore(&ppd->lflags_lock, flags);
+ } else {
+ /*
+ * Some QSFP's not only do not respond until the full power-up
+ * time, but may behave badly if we try. So hold off responding
+ * to insertion.
+ */
+ while (1) {
+ u64 now = get_jiffies_64();
+ if (time_after64(now, pwrup))
+ break;
+ msleep(20);
+ }
+
+ ret = qib_refresh_qsfp_cache(ppd, &qd->cache);
+
+ /*
+ * Need to change LE2 back to defaults if we couldn't
+ * read the cable type (to handle cable swaps), so do this
+ * even on failure to read cable information. We don't
+ * get here for QME, so IS_QME check not needed here.
+ */
+ if (!ret && !ppd->dd->cspec->r1) {
+ if (QSFP_IS_ACTIVE_FAR(qd->cache.tech))
+ le2 = LE2_QME;
+ else if (qd->cache.atten[1] >= qib_long_atten &&
+ QSFP_IS_CU(qd->cache.tech))
+ le2 = LE2_5m;
+ else
+ le2 = LE2_DEFAULT;
+ } else
le2 = LE2_DEFAULT;
- } else
- le2 = LE2_DEFAULT;
- ibsd_wr_allchans(ppd, 13, (le2 << 7), BMASK(9, 7));
- init_txdds_table(ppd, 0);
+ ibsd_wr_allchans(ppd, 13, (le2 << 7), BMASK(9, 7));
+ /*
+ * We always change parameteters, since we can choose
+ * values for cables without eeproms, and the cable may have
+ * changed from a cable with full or partial eeprom content
+ * to one with partial or no content.
+ */
+ init_txdds_table(ppd, 0);
+ /* The physical link is being re-enabled only when the
+ * previous state was DISABLED and the VALID bit is not
+ * set. This should only happen when the cable has been
+ * physically pulled. */
+ if (!ppd->cpspec->qsfp_data.modpresent &&
+ (ppd->lflags & (QIBL_LINKV | QIBL_IB_LINK_DISABLED))) {
+ ppd->cpspec->qsfp_data.modpresent = 1;
+ qib_set_ib_7322_lstate(ppd, 0,
+ QLOGIC_IB_IBCC_LINKINITCMD_SLEEP);
+ spin_lock_irqsave(&ppd->lflags_lock, flags);
+ ppd->lflags |= QIBL_LINKV;
+ spin_unlock_irqrestore(&ppd->lflags_lock, flags);
+ }
+ }
}
/*
@@ -5727,7 +5768,8 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change)
/* now change the IBC and serdes, overriding generic */
init_txdds_table(ppd, 1);
/* Re-enable the physical state machine on mezz boards
- * now that the correct settings have been set. */
+ * now that the correct settings have been set.
+ * QSFP boards are handles by the QSFP event handler */
if (IS_QMH(dd) || IS_QME(dd))
qib_set_ib_7322_lstate(ppd, 0,
QLOGIC_IB_IBCC_LINKINITCMD_SLEEP);
@@ -6205,6 +6247,8 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
/* we always allocate at least 2048 bytes for eager buffers */
dd->rcvegrbufsize = max(mtu, 2048);
+ BUG_ON(!is_power_of_2(dd->rcvegrbufsize));
+ dd->rcvegrbufsize_shift = ilog2(dd->rcvegrbufsize);
qib_7322_tidtemplate(dd);
@@ -7147,7 +7191,8 @@ static void find_best_ent(struct qib_pportdata *ppd,
}
}
- /* Lookup serdes setting by cable type and attenuation */
+ /* Active cables don't have attenuation so we only set SERDES
+ * settings to account for the attenuation of the board traces. */
if (!override && QSFP_IS_ACTIVE(qd->tech)) {
*sdr_dds = txdds_sdr + ppd->dd->board_atten;
*ddr_dds = txdds_ddr + ppd->dd->board_atten;
@@ -7464,12 +7509,6 @@ static int serdes_7322_init_new(struct qib_pportdata *ppd)
u32 le_val, rxcaldone;
int chan, chan_done = (1 << SERDES_CHANS) - 1;
- /*
- * Initialize the Tx DDS tables. Also done every QSFP event,
- * for adapters with QSFP
- */
- init_txdds_table(ppd, 0);
-
/* Clear cmode-override, may be set from older driver */
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 10, 0 << 14, 1 << 14);
@@ -7655,6 +7694,12 @@ static int serdes_7322_init_new(struct qib_pportdata *ppd)
/* VGA output common mode */
ibsd_wr_allchans(ppd, 12, (3 << 2), BMASK(3, 2));
+ /*
+ * Initialize the Tx DDS tables. Also done every QSFP event,
+ * for adapters with QSFP
+ */
+ init_txdds_table(ppd, 0);
+
return 0;
}
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index a01f3fce8eb3..b093a0b53b2f 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -183,6 +183,9 @@ struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *ppd, u32 ctxt)
rcd->rcvegrbuf_chunks = (rcd->rcvegrcnt +
rcd->rcvegrbufs_perchunk - 1) /
rcd->rcvegrbufs_perchunk;
+ BUG_ON(!is_power_of_2(rcd->rcvegrbufs_perchunk));
+ rcd->rcvegrbufs_perchunk_shift =
+ ilog2(rcd->rcvegrbufs_perchunk);
}
return rcd;
}
@@ -398,6 +401,7 @@ static void enable_chip(struct qib_devdata *dd)
if (rcd)
dd->f_rcvctrl(rcd->ppd, rcvmask, i);
}
+ dd->freectxts = dd->cfgctxts - dd->first_user_ctxt;
}
static void verify_interrupt(unsigned long opaque)
@@ -581,10 +585,6 @@ int qib_init(struct qib_devdata *dd, int reinit)
continue;
}
- /* let link come up, and enable IBC */
- spin_lock_irqsave(&ppd->lflags_lock, flags);
- ppd->lflags &= ~QIBL_IB_LINK_DISABLED;
- spin_unlock_irqrestore(&ppd->lflags_lock, flags);
portok++;
}
diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c
index e16751f8639e..7e7e16fbee99 100644
--- a/drivers/infiniband/hw/qib/qib_qp.c
+++ b/drivers/infiniband/hw/qib/qib_qp.c
@@ -34,6 +34,7 @@
#include <linux/err.h>
#include <linux/vmalloc.h>
+#include <linux/jhash.h>
#include "qib.h"
@@ -204,6 +205,13 @@ static void free_qpn(struct qib_qpn_table *qpt, u32 qpn)
clear_bit(qpn & BITS_PER_PAGE_MASK, map->page);
}
+static inline unsigned qpn_hash(struct qib_ibdev *dev, u32 qpn)
+{
+ return jhash_1word(qpn, dev->qp_rnd) &
+ (dev->qp_table_size - 1);
+}
+
+
/*
* Put the QP into the hash table.
* The hash table holds a reference to the QP.
@@ -211,22 +219,23 @@ static void free_qpn(struct qib_qpn_table *qpt, u32 qpn)
static void insert_qp(struct qib_ibdev *dev, struct qib_qp *qp)
{
struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- unsigned n = qp->ibqp.qp_num % dev->qp_table_size;
unsigned long flags;
+ unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
spin_lock_irqsave(&dev->qpt_lock, flags);
+ atomic_inc(&qp->refcount);
if (qp->ibqp.qp_num == 0)
- ibp->qp0 = qp;
+ rcu_assign_pointer(ibp->qp0, qp);
else if (qp->ibqp.qp_num == 1)
- ibp->qp1 = qp;
+ rcu_assign_pointer(ibp->qp1, qp);
else {
qp->next = dev->qp_table[n];
- dev->qp_table[n] = qp;
+ rcu_assign_pointer(dev->qp_table[n], qp);
}
- atomic_inc(&qp->refcount);
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
}
/*
@@ -236,29 +245,32 @@ static void insert_qp(struct qib_ibdev *dev, struct qib_qp *qp)
static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp)
{
struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct qib_qp *q, **qpp;
+ unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
unsigned long flags;
- qpp = &dev->qp_table[qp->ibqp.qp_num % dev->qp_table_size];
-
spin_lock_irqsave(&dev->qpt_lock, flags);
if (ibp->qp0 == qp) {
- ibp->qp0 = NULL;
atomic_dec(&qp->refcount);
+ rcu_assign_pointer(ibp->qp0, NULL);
} else if (ibp->qp1 == qp) {
- ibp->qp1 = NULL;
atomic_dec(&qp->refcount);
- } else
+ rcu_assign_pointer(ibp->qp1, NULL);
+ } else {
+ struct qib_qp *q, **qpp;
+
+ qpp = &dev->qp_table[n];
for (; (q = *qpp) != NULL; qpp = &q->next)
if (q == qp) {
- *qpp = qp->next;
- qp->next = NULL;
atomic_dec(&qp->refcount);
+ rcu_assign_pointer(*qpp, qp->next);
+ qp->next = NULL;
break;
}
+ }
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
}
/**
@@ -280,21 +292,24 @@ unsigned qib_free_all_qps(struct qib_devdata *dd)
if (!qib_mcast_tree_empty(ibp))
qp_inuse++;
- if (ibp->qp0)
+ rcu_read_lock();
+ if (rcu_dereference(ibp->qp0))
qp_inuse++;
- if (ibp->qp1)
+ if (rcu_dereference(ibp->qp1))
qp_inuse++;
+ rcu_read_unlock();
}
spin_lock_irqsave(&dev->qpt_lock, flags);
for (n = 0; n < dev->qp_table_size; n++) {
qp = dev->qp_table[n];
- dev->qp_table[n] = NULL;
+ rcu_assign_pointer(dev->qp_table[n], NULL);
for (; qp; qp = qp->next)
qp_inuse++;
}
spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ synchronize_rcu();
return qp_inuse;
}
@@ -309,25 +324,28 @@ unsigned qib_free_all_qps(struct qib_devdata *dd)
*/
struct qib_qp *qib_lookup_qpn(struct qib_ibport *ibp, u32 qpn)
{
- struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
- unsigned long flags;
- struct qib_qp *qp;
+ struct qib_qp *qp = NULL;
- spin_lock_irqsave(&dev->qpt_lock, flags);
+ if (unlikely(qpn <= 1)) {
+ rcu_read_lock();
+ if (qpn == 0)
+ qp = rcu_dereference(ibp->qp0);
+ else
+ qp = rcu_dereference(ibp->qp1);
+ } else {
+ struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
+ unsigned n = qpn_hash(dev, qpn);
- if (qpn == 0)
- qp = ibp->qp0;
- else if (qpn == 1)
- qp = ibp->qp1;
- else
- for (qp = dev->qp_table[qpn % dev->qp_table_size]; qp;
- qp = qp->next)
+ rcu_read_lock();
+ for (qp = dev->qp_table[n]; rcu_dereference(qp); qp = qp->next)
if (qp->ibqp.qp_num == qpn)
break;
+ }
if (qp)
- atomic_inc(&qp->refcount);
+ if (unlikely(!atomic_inc_not_zero(&qp->refcount)))
+ qp = NULL;
- spin_unlock_irqrestore(&dev->qpt_lock, flags);
+ rcu_read_unlock();
return qp;
}
@@ -765,8 +783,10 @@ int qib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
}
}
- if (attr_mask & IB_QP_PATH_MTU)
+ if (attr_mask & IB_QP_PATH_MTU) {
qp->path_mtu = pmtu;
+ qp->pmtu = ib_mtu_enum_to_int(pmtu);
+ }
if (attr_mask & IB_QP_RETRY_CNT) {
qp->s_retry_cnt = attr->retry_cnt;
@@ -781,8 +801,12 @@ int qib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
if (attr_mask & IB_QP_MIN_RNR_TIMER)
qp->r_min_rnr_timer = attr->min_rnr_timer;
- if (attr_mask & IB_QP_TIMEOUT)
+ if (attr_mask & IB_QP_TIMEOUT) {
qp->timeout = attr->timeout;
+ qp->timeout_jiffies =
+ usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
+ 1000UL);
+ }
if (attr_mask & IB_QP_QKEY)
qp->qkey = attr->qkey;
@@ -1013,6 +1037,10 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
ret = ERR_PTR(-ENOMEM);
goto bail_swq;
}
+ RCU_INIT_POINTER(qp->next, NULL);
+ qp->timeout_jiffies =
+ usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
+ 1000UL);
if (init_attr->srq)
sz = 0;
else {
diff --git a/drivers/infiniband/hw/qib/qib_qsfp.c b/drivers/infiniband/hw/qib/qib_qsfp.c
index 3374a52232c1..e06c4ed383f1 100644
--- a/drivers/infiniband/hw/qib/qib_qsfp.c
+++ b/drivers/infiniband/hw/qib/qib_qsfp.c
@@ -273,18 +273,12 @@ int qib_refresh_qsfp_cache(struct qib_pportdata *ppd, struct qib_qsfp_cache *cp)
int ret;
int idx;
u16 cks;
- u32 mask;
u8 peek[4];
/* ensure sane contents on invalid reads, for cable swaps */
memset(cp, 0, sizeof(*cp));
- mask = QSFP_GPIO_MOD_PRS_N;
- if (ppd->hw_pidx)
- mask <<= QSFP_GPIO_PORT2_SHIFT;
-
- ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0);
- if (ret & mask) {
+ if (!qib_qsfp_mod_present(ppd)) {
ret = -ENODEV;
goto bail;
}
@@ -444,6 +438,19 @@ const char * const qib_qsfp_devtech[16] = {
static const char *pwr_codes = "1.5W2.0W2.5W3.5W";
+int qib_qsfp_mod_present(struct qib_pportdata *ppd)
+{
+ u32 mask;
+ int ret;
+
+ mask = QSFP_GPIO_MOD_PRS_N <<
+ (ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT);
+ ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0);
+
+ return !((ret & mask) >>
+ ((ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT) + 3));
+}
+
/*
* Initialize structures that control access to QSFP. Called once per port
* on cards that support QSFP.
@@ -452,7 +459,6 @@ void qib_qsfp_init(struct qib_qsfp_data *qd,
void (*fevent)(struct work_struct *))
{
u32 mask, highs;
- int pins;
struct qib_devdata *dd = qd->ppd->dd;
@@ -480,8 +486,7 @@ void qib_qsfp_init(struct qib_qsfp_data *qd,
mask <<= QSFP_GPIO_PORT2_SHIFT;
/* Do not try to wait here. Better to let event handle it */
- pins = dd->f_gpio_mod(dd, 0, 0, 0);
- if (pins & mask)
+ if (!qib_qsfp_mod_present(qd->ppd))
goto bail;
/* We see a module, but it may be unwise to look yet. Just schedule */
qd->t_insert = get_jiffies_64();
diff --git a/drivers/infiniband/hw/qib/qib_qsfp.h b/drivers/infiniband/hw/qib/qib_qsfp.h
index c109bbdc90ac..46002a9417c0 100644
--- a/drivers/infiniband/hw/qib/qib_qsfp.h
+++ b/drivers/infiniband/hw/qib/qib_qsfp.h
@@ -34,6 +34,7 @@
#define QSFP_DEV 0xA0
#define QSFP_PWR_LAG_MSEC 2000
+#define QSFP_MODPRS_LAG_MSEC 20
/*
* Below are masks for various QSFP signals, for Port 1.
@@ -177,10 +178,12 @@ struct qib_qsfp_data {
struct work_struct work;
struct qib_qsfp_cache cache;
u64 t_insert;
+ u8 modpresent;
};
extern int qib_refresh_qsfp_cache(struct qib_pportdata *ppd,
struct qib_qsfp_cache *cp);
+extern int qib_qsfp_mod_present(struct qib_pportdata *ppd);
extern void qib_qsfp_init(struct qib_qsfp_data *qd,
void (*fevent)(struct work_struct *));
extern void qib_qsfp_deinit(struct qib_qsfp_data *qd);
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index eca0c41f1226..894afac26f3b 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -59,8 +59,7 @@ static void start_timer(struct qib_qp *qp)
qp->s_flags |= QIB_S_TIMER;
qp->s_timer.function = rc_timeout;
/* 4.096 usec. * (1 << qp->timeout) */
- qp->s_timer.expires = jiffies +
- usecs_to_jiffies((4096UL * (1UL << qp->timeout)) / 1000UL);
+ qp->s_timer.expires = jiffies + qp->timeout_jiffies;
add_timer(&qp->s_timer);
}
@@ -239,7 +238,7 @@ int qib_make_rc_req(struct qib_qp *qp)
u32 len;
u32 bth0;
u32 bth2;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
char newreq;
unsigned long flags;
int ret = 0;
@@ -272,13 +271,9 @@ int qib_make_rc_req(struct qib_qp *qp)
goto bail;
}
wqe = get_swqe_ptr(qp, qp->s_last);
- while (qp->s_last != qp->s_acked) {
- qib_send_complete(qp, wqe, IB_WC_SUCCESS);
- if (++qp->s_last >= qp->s_size)
- qp->s_last = 0;
- wqe = get_swqe_ptr(qp, qp->s_last);
- }
- qib_send_complete(qp, wqe, IB_WC_WR_FLUSH_ERR);
+ qib_send_complete(qp, wqe, qp->s_last != qp->s_acked ?
+ IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR);
+ /* will get called again */
goto done;
}
@@ -1519,9 +1514,7 @@ read_middle:
* 4.096 usec. * (1 << qp->timeout)
*/
qp->s_flags |= QIB_S_TIMER;
- mod_timer(&qp->s_timer, jiffies +
- usecs_to_jiffies((4096UL * (1UL << qp->timeout)) /
- 1000UL));
+ mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies);
if (qp->s_flags & QIB_S_WAIT_ACK) {
qp->s_flags &= ~QIB_S_WAIT_ACK;
qib_schedule_send(qp);
@@ -1732,7 +1725,7 @@ static int qib_rc_rcv_error(struct qib_other_headers *ohdr,
* same request.
*/
offset = ((psn - e->psn) & QIB_PSN_MASK) *
- ib_mtu_enum_to_int(qp->path_mtu);
+ qp->pmtu;
len = be32_to_cpu(reth->length);
if (unlikely(offset + len != e->rdma_sge.sge_length))
goto unlock_done;
@@ -1876,7 +1869,7 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
u32 psn;
u32 pad;
struct ib_wc wc;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
int diff;
struct ib_reth *reth;
unsigned long flags;
@@ -1892,10 +1885,8 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
}
opcode = be32_to_cpu(ohdr->bth[0]);
- spin_lock_irqsave(&qp->s_lock, flags);
if (qib_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
- goto sunlock;
- spin_unlock_irqrestore(&qp->s_lock, flags);
+ return;
psn = be32_to_cpu(ohdr->bth[2]);
opcode >>= 24;
@@ -1955,8 +1946,6 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct qib_ib_header *hdr,
break;
}
- memset(&wc, 0, sizeof wc);
-
if (qp->state == IB_QPS_RTR && !(qp->r_flags & QIB_R_COMM_EST)) {
qp->r_flags |= QIB_R_COMM_EST;
if (qp->ibqp.event_handler) {
@@ -2009,16 +1998,19 @@ send_middle:
goto rnr_nak;
qp->r_rcv_len = 0;
if (opcode == OP(SEND_ONLY))
- goto send_last;
- /* FALLTHROUGH */
+ goto no_immediate_data;
+ /* FALLTHROUGH for SEND_ONLY_WITH_IMMEDIATE */
case OP(SEND_LAST_WITH_IMMEDIATE):
send_last_imm:
wc.ex.imm_data = ohdr->u.imm_data;
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
- /* FALLTHROUGH */
+ goto send_last;
case OP(SEND_LAST):
case OP(RDMA_WRITE_LAST):
+no_immediate_data:
+ wc.wc_flags = 0;
+ wc.ex.imm_data = 0;
send_last:
/* Get the number of bytes the message was padded by. */
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
@@ -2051,6 +2043,12 @@ send_last:
wc.src_qp = qp->remote_qpn;
wc.slid = qp->remote_ah_attr.dlid;
wc.sl = qp->remote_ah_attr.sl;
+ /* zero fields that are N/A */
+ wc.vendor_err = 0;
+ wc.pkey_index = 0;
+ wc.dlid_path_bits = 0;
+ wc.port_num = 0;
+ wc.csum_ok = 0;
/* Signal completion event if the solicited bit is set. */
qib_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
(ohdr->bth[0] &
@@ -2089,7 +2087,7 @@ send_last:
if (opcode == OP(RDMA_WRITE_FIRST))
goto send_middle;
else if (opcode == OP(RDMA_WRITE_ONLY))
- goto send_last;
+ goto no_immediate_data;
ret = qib_get_rwqe(qp, 1);
if (ret < 0)
goto nack_op_err;
diff --git a/drivers/infiniband/hw/qib/qib_ruc.c b/drivers/infiniband/hw/qib/qib_ruc.c
index eb78d9367f06..b4b37e47321a 100644
--- a/drivers/infiniband/hw/qib/qib_ruc.c
+++ b/drivers/infiniband/hw/qib/qib_ruc.c
@@ -260,12 +260,15 @@ static int gid_ok(union ib_gid *gid, __be64 gid_prefix, __be64 id)
/*
*
- * This should be called with the QP s_lock held.
+ * This should be called with the QP r_lock held.
+ *
+ * The s_lock will be acquired around the qib_migrate_qp() call.
*/
int qib_ruc_check_hdr(struct qib_ibport *ibp, struct qib_ib_header *hdr,
int has_grh, struct qib_qp *qp, u32 bth0)
{
__be64 guid;
+ unsigned long flags;
if (qp->s_mig_state == IB_MIG_ARMED && (bth0 & IB_BTH_MIG_REQ)) {
if (!has_grh) {
@@ -295,7 +298,9 @@ int qib_ruc_check_hdr(struct qib_ibport *ibp, struct qib_ib_header *hdr,
if (be16_to_cpu(hdr->lrh[3]) != qp->alt_ah_attr.dlid ||
ppd_from_ibp(ibp)->port != qp->alt_ah_attr.port_num)
goto err;
+ spin_lock_irqsave(&qp->s_lock, flags);
qib_migrate_qp(qp);
+ spin_unlock_irqrestore(&qp->s_lock, flags);
} else {
if (!has_grh) {
if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
diff --git a/drivers/infiniband/hw/qib/qib_srq.c b/drivers/infiniband/hw/qib/qib_srq.c
index c3ec8efc2ed8..d6235931a1ba 100644
--- a/drivers/infiniband/hw/qib/qib_srq.c
+++ b/drivers/infiniband/hw/qib/qib_srq.c
@@ -107,6 +107,11 @@ struct ib_srq *qib_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
+ ret = ERR_PTR(-ENOSYS);
+ goto done;
+ }
+
if (srq_init_attr->attr.max_sge == 0 ||
srq_init_attr->attr.max_sge > ib_qib_max_srq_sges ||
srq_init_attr->attr.max_wr == 0 ||
diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c
index 14d129de4320..78fbd56879d4 100644
--- a/drivers/infiniband/hw/qib/qib_sysfs.c
+++ b/drivers/infiniband/hw/qib/qib_sysfs.c
@@ -515,8 +515,7 @@ static ssize_t show_nfreectxts(struct device *device,
struct qib_devdata *dd = dd_from_dev(dev);
/* Return the number of free user ports (contexts) available. */
- return scnprintf(buf, PAGE_SIZE, "%u\n", dd->cfgctxts -
- dd->first_user_ctxt - (u32)qib_stats.sps_ctxts);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", dd->freectxts);
}
static ssize_t show_serial(struct device *device,
diff --git a/drivers/infiniband/hw/qib/qib_uc.c b/drivers/infiniband/hw/qib/qib_uc.c
index 32ccf3c824ca..847e7afdfd94 100644
--- a/drivers/infiniband/hw/qib/qib_uc.c
+++ b/drivers/infiniband/hw/qib/qib_uc.c
@@ -51,7 +51,7 @@ int qib_make_uc_req(struct qib_qp *qp)
u32 hwords;
u32 bth0;
u32 len;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
int ret = 0;
spin_lock_irqsave(&qp->s_lock, flags);
@@ -243,13 +243,12 @@ void qib_uc_rcv(struct qib_ibport *ibp, struct qib_ib_header *hdr,
int has_grh, void *data, u32 tlen, struct qib_qp *qp)
{
struct qib_other_headers *ohdr;
- unsigned long flags;
u32 opcode;
u32 hdrsize;
u32 psn;
u32 pad;
struct ib_wc wc;
- u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+ u32 pmtu = qp->pmtu;
struct ib_reth *reth;
int ret;
@@ -263,14 +262,11 @@ void qib_uc_rcv(struct qib_ibport *ibp, struct qib_ib_header *hdr,
}
opcode = be32_to_cpu(ohdr->bth[0]);
- spin_lock_irqsave(&qp->s_lock, flags);
if (qib_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
- goto sunlock;
- spin_unlock_irqrestore(&qp->s_lock, flags);
+ return;
psn = be32_to_cpu(ohdr->bth[2]);
opcode >>= 24;
- memset(&wc, 0, sizeof wc);
/* Compare the PSN verses the expected PSN. */
if (unlikely(qib_cmp24(psn, qp->r_psn) != 0)) {
@@ -370,7 +366,7 @@ send_first:
}
qp->r_rcv_len = 0;
if (opcode == OP(SEND_ONLY))
- goto send_last;
+ goto no_immediate_data;
else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE))
goto send_last_imm;
/* FALLTHROUGH */
@@ -389,8 +385,11 @@ send_last_imm:
wc.ex.imm_data = ohdr->u.imm_data;
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
- /* FALLTHROUGH */
+ goto send_last;
case OP(SEND_LAST):
+no_immediate_data:
+ wc.ex.imm_data = 0;
+ wc.wc_flags = 0;
send_last:
/* Get the number of bytes the message was padded by. */
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
@@ -418,6 +417,12 @@ last_imm:
wc.src_qp = qp->remote_qpn;
wc.slid = qp->remote_ah_attr.dlid;
wc.sl = qp->remote_ah_attr.sl;
+ /* zero fields that are N/A */
+ wc.vendor_err = 0;
+ wc.pkey_index = 0;
+ wc.dlid_path_bits = 0;
+ wc.port_num = 0;
+ wc.csum_ok = 0;
/* Signal completion event if the solicited bit is set. */
qib_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
(ohdr->bth[0] &
@@ -546,6 +551,4 @@ op_err:
qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
return;
-sunlock:
- spin_unlock_irqrestore(&qp->s_lock, flags);
}
diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c
index 7689e49c13c9..2bc1d2b96298 100644
--- a/drivers/infiniband/hw/qib/qib_user_pages.c
+++ b/drivers/infiniband/hw/qib/qib_user_pages.c
@@ -74,7 +74,7 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages,
goto bail_release;
}
- current->mm->locked_vm += num_pages;
+ current->mm->pinned_vm += num_pages;
ret = 0;
goto bail;
@@ -151,7 +151,7 @@ void qib_release_user_pages(struct page **p, size_t num_pages)
__qib_release_user_pages(p, num_pages, 1);
if (current->mm) {
- current->mm->locked_vm -= num_pages;
+ current->mm->pinned_vm -= num_pages;
up_write(&current->mm->mmap_sem);
}
}
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 9fab40488850..9627cb737125 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -38,11 +38,12 @@
#include <linux/utsname.h>
#include <linux/rculist.h>
#include <linux/mm.h>
+#include <linux/random.h>
#include "qib.h"
#include "qib_common.h"
-static unsigned int ib_qib_qp_table_size = 251;
+static unsigned int ib_qib_qp_table_size = 256;
module_param_named(qp_table_size, ib_qib_qp_table_size, uint, S_IRUGO);
MODULE_PARM_DESC(qp_table_size, "QP table size");
@@ -659,17 +660,25 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen)
if (atomic_dec_return(&mcast->refcount) <= 1)
wake_up(&mcast->wait);
} else {
- qp = qib_lookup_qpn(ibp, qp_num);
- if (!qp)
- goto drop;
+ if (rcd->lookaside_qp) {
+ if (rcd->lookaside_qpn != qp_num) {
+ if (atomic_dec_and_test(
+ &rcd->lookaside_qp->refcount))
+ wake_up(
+ &rcd->lookaside_qp->wait);
+ rcd->lookaside_qp = NULL;
+ }
+ }
+ if (!rcd->lookaside_qp) {
+ qp = qib_lookup_qpn(ibp, qp_num);
+ if (!qp)
+ goto drop;
+ rcd->lookaside_qp = qp;
+ rcd->lookaside_qpn = qp_num;
+ } else
+ qp = rcd->lookaside_qp;
ibp->n_unicast_rcv++;
qib_qp_rcv(rcd, hdr, lnh == QIB_LRH_GRH, data, tlen, qp);
- /*
- * Notify qib_destroy_qp() if it is waiting
- * for us to finish.
- */
- if (atomic_dec_and_test(&qp->refcount))
- wake_up(&qp->wait);
}
return;
@@ -1974,6 +1983,8 @@ static void init_ibport(struct qib_pportdata *ppd)
ibp->z_excessive_buffer_overrun_errors =
cntrs.excessive_buffer_overrun_errors;
ibp->z_vl15_dropped = cntrs.vl15_dropped;
+ RCU_INIT_POINTER(ibp->qp0, NULL);
+ RCU_INIT_POINTER(ibp->qp1, NULL);
}
/**
@@ -1990,12 +2001,15 @@ int qib_register_ib_device(struct qib_devdata *dd)
int ret;
dev->qp_table_size = ib_qib_qp_table_size;
- dev->qp_table = kzalloc(dev->qp_table_size * sizeof *dev->qp_table,
+ get_random_bytes(&dev->qp_rnd, sizeof(dev->qp_rnd));
+ dev->qp_table = kmalloc(dev->qp_table_size * sizeof *dev->qp_table,
GFP_KERNEL);
if (!dev->qp_table) {
ret = -ENOMEM;
goto err_qpt;
}
+ for (i = 0; i < dev->qp_table_size; i++)
+ RCU_INIT_POINTER(dev->qp_table[i], NULL);
for (i = 0; i < dd->num_pports; i++)
init_ibport(ppd + i);
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index 95e5b47223b3..0c19ef0c4123 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -485,6 +485,7 @@ struct qib_qp {
u8 alt_timeout; /* Alternate path timeout for this QP */
u8 port_num;
enum ib_mtu path_mtu;
+ u32 pmtu; /* decoded from path_mtu */
u32 remote_qpn;
u32 qkey; /* QKEY for this QP (for UD or RD) */
u32 s_size; /* send work queue size */
@@ -495,6 +496,7 @@ struct qib_qp {
u32 s_last; /* last completed entry */
u32 s_ssn; /* SSN of tail entry */
u32 s_lsn; /* limit sequence number (credit) */
+ unsigned long timeout_jiffies; /* computed from timeout */
struct qib_swqe *s_wq; /* send work queue */
struct qib_swqe *s_wqe;
struct qib_rq r_rq; /* receive work queue */
@@ -723,7 +725,8 @@ struct qib_ibdev {
dma_addr_t pio_hdrs_phys;
/* list of QPs waiting for RNR timer */
spinlock_t pending_lock; /* protect wait lists, PMA counters, etc. */
- unsigned qp_table_size; /* size of the hash table */
+ u32 qp_table_size; /* size of the hash table */
+ u32 qp_rnd; /* random bytes for hash */
spinlock_t qpt_lock;
u32 n_piowait;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index c74548a586ea..231c2f2f52f8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -84,7 +84,7 @@ static void ipoib_cm_dma_unmap_rx(struct ipoib_dev_priv *priv, int frags,
ib_dma_unmap_single(priv->ca, mapping[0], IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE);
for (i = 0; i < frags; ++i)
- ib_dma_unmap_single(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_page(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE);
}
static int ipoib_cm_post_receive_srq(struct net_device *dev, int id)
@@ -183,7 +183,7 @@ partial_error:
ib_dma_unmap_single(priv->ca, mapping[0], IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE);
for (; i > 0; --i)
- ib_dma_unmap_single(priv->ca, mapping[i], PAGE_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_page(priv->ca, mapping[i], PAGE_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
return NULL;
@@ -1497,6 +1497,7 @@ static void ipoib_cm_create_srq(struct net_device *dev, int max_sge)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_srq_init_attr srq_init_attr = {
+ .srq_type = IB_SRQT_BASIC,
.attr = {
.max_wr = ipoib_recvq_size,
.max_sge = max_sge
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 86eae229dc49..0e2fe4631ba8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -212,16 +212,15 @@ static int ipoib_path_seq_show(struct seq_file *file, void *iter_ptr)
gid_buf, path.pathrec.dlid ? "yes" : "no");
if (path.pathrec.dlid) {
- rate = ib_rate_to_mult(path.pathrec.rate) * 25;
+ rate = ib_rate_to_mbps(path.pathrec.rate);
seq_printf(file,
" DLID: 0x%04x\n"
" SL: %12d\n"
- " rate: %*d%s Gb/sec\n",
+ " rate: %8d.%d Gb/sec\n",
be16_to_cpu(path.pathrec.dlid),
path.pathrec.sl,
- 10 - ((rate % 10) ? 2 : 0),
- rate / 10, rate % 10 ? ".5" : "");
+ rate / 1000, rate % 1000);
}
seq_putc(file, '\n');
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 84e8c293a715..c42b8f390c0b 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -151,7 +151,6 @@ int iser_initialize_task_headers(struct iscsi_task *task,
tx_desc->tx_sg[0].length = ISER_HEADERS_LEN;
tx_desc->tx_sg[0].lkey = device->mr->lkey;
- iser_task->headers_initialized = 1;
iser_task->iser_conn = iser_conn;
return 0;
}
@@ -166,8 +165,7 @@ iscsi_iser_task_init(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- if (!iser_task->headers_initialized)
- if (iser_initialize_task_headers(task, &iser_task->desc))
+ if (iser_initialize_task_headers(task, &iser_task->desc))
return -ENOMEM;
/* mgmt task */
@@ -278,6 +276,13 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
+ struct iser_tx_desc *tx_desc = &iser_task->desc;
+
+ struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
+ struct iser_device *device = iser_conn->ib_conn->device;
+
+ ib_dma_unmap_single(device->ib_device,
+ tx_desc->dma_addr, ISER_HEADERS_LEN, DMA_TO_DEVICE);
/* mgmt tasks do not need special cleanup */
if (!task->sc)
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index db6f3ce9f3bf..db7ea3704da7 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -257,7 +257,8 @@ struct iser_conn {
struct list_head conn_list; /* entry in ig conn list */
char *login_buf;
- u64 login_dma;
+ char *login_req_buf, *login_resp_buf;
+ u64 login_req_dma, login_resp_dma;
unsigned int rx_desc_head;
struct iser_rx_desc *rx_descs;
struct ib_recv_wr rx_wr[ISER_MIN_POSTED_RX];
@@ -277,7 +278,6 @@ struct iscsi_iser_task {
struct iser_regd_buf rdma_regd[ISER_DIRS_NUM];/* regd rdma buf */
struct iser_data_buf data[ISER_DIRS_NUM]; /* orig. data des*/
struct iser_data_buf data_copy[ISER_DIRS_NUM];/* contig. copy */
- int headers_initialized;
};
struct iser_page_vec {
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index f299de6b419b..a607542fc796 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -221,8 +221,14 @@ void iser_free_rx_descriptors(struct iser_conn *ib_conn)
struct iser_device *device = ib_conn->device;
if (ib_conn->login_buf) {
- ib_dma_unmap_single(device->ib_device, ib_conn->login_dma,
- ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
+ if (ib_conn->login_req_dma)
+ ib_dma_unmap_single(device->ib_device,
+ ib_conn->login_req_dma,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE);
+ if (ib_conn->login_resp_dma)
+ ib_dma_unmap_single(device->ib_device,
+ ib_conn->login_resp_dma,
+ ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
kfree(ib_conn->login_buf);
}
@@ -394,6 +400,7 @@ int iser_send_control(struct iscsi_conn *conn,
unsigned long data_seg_len;
int err = 0;
struct iser_device *device;
+ struct iser_conn *ib_conn = iser_conn->ib_conn;
/* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL;
@@ -409,9 +416,19 @@ int iser_send_control(struct iscsi_conn *conn,
iser_err("data present on non login task!!!\n");
goto send_control_error;
}
- memcpy(iser_conn->ib_conn->login_buf, task->data,
+
+ ib_dma_sync_single_for_cpu(device->ib_device,
+ ib_conn->login_req_dma, task->data_count,
+ DMA_TO_DEVICE);
+
+ memcpy(iser_conn->ib_conn->login_req_buf, task->data,
task->data_count);
- tx_dsg->addr = iser_conn->ib_conn->login_dma;
+
+ ib_dma_sync_single_for_device(device->ib_device,
+ ib_conn->login_req_dma, task->data_count,
+ DMA_TO_DEVICE);
+
+ tx_dsg->addr = iser_conn->ib_conn->login_req_dma;
tx_dsg->length = task->data_count;
tx_dsg->lkey = device->mr->lkey;
mdesc->num_sge = 2;
@@ -445,8 +462,8 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
int rx_buflen, outstanding, count, err;
/* differentiate between login to all other PDUs */
- if ((char *)rx_desc == ib_conn->login_buf) {
- rx_dma = ib_conn->login_dma;
+ if ((char *)rx_desc == ib_conn->login_resp_buf) {
+ rx_dma = ib_conn->login_resp_dma;
rx_buflen = ISER_RX_LOGIN_SIZE;
} else {
rx_dma = rx_desc->dma_addr;
@@ -473,7 +490,7 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
* for the posted rx bufs refcount to become zero handles everything */
conn->ib_conn->post_recv_buf_count--;
- if (rx_dma == ib_conn->login_dma)
+ if (rx_dma == ib_conn->login_resp_dma)
return;
outstanding = ib_conn->post_recv_buf_count;
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ede1475bee09..e28877c4ce15 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -155,20 +155,39 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
{
struct iser_device *device;
struct ib_qp_init_attr init_attr;
- int ret = -ENOMEM;
+ int req_err, resp_err, ret = -ENOMEM;
struct ib_fmr_pool_param params;
BUG_ON(ib_conn->device == NULL);
device = ib_conn->device;
- ib_conn->login_buf = kmalloc(ISER_RX_LOGIN_SIZE, GFP_KERNEL);
+ ib_conn->login_buf = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN +
+ ISER_RX_LOGIN_SIZE, GFP_KERNEL);
if (!ib_conn->login_buf)
goto out_err;
- ib_conn->login_dma = ib_dma_map_single(ib_conn->device->ib_device,
- (void *)ib_conn->login_buf, ISER_RX_LOGIN_SIZE,
- DMA_FROM_DEVICE);
+ ib_conn->login_req_buf = ib_conn->login_buf;
+ ib_conn->login_resp_buf = ib_conn->login_buf + ISCSI_DEF_MAX_RECV_SEG_LEN;
+
+ ib_conn->login_req_dma = ib_dma_map_single(ib_conn->device->ib_device,
+ (void *)ib_conn->login_req_buf,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE);
+
+ ib_conn->login_resp_dma = ib_dma_map_single(ib_conn->device->ib_device,
+ (void *)ib_conn->login_resp_buf,
+ ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
+
+ req_err = ib_dma_mapping_error(device->ib_device, ib_conn->login_req_dma);
+ resp_err = ib_dma_mapping_error(device->ib_device, ib_conn->login_resp_dma);
+
+ if (req_err || resp_err) {
+ if (req_err)
+ ib_conn->login_req_dma = 0;
+ if (resp_err)
+ ib_conn->login_resp_dma = 0;
+ goto out_err;
+ }
ib_conn->page_vec = kmalloc(sizeof(struct iser_page_vec) +
(sizeof(u64) * (ISCSI_ISER_SG_TABLESIZE +1)),
@@ -658,11 +677,11 @@ int iser_post_recvl(struct iser_conn *ib_conn)
struct ib_sge sge;
int ib_ret;
- sge.addr = ib_conn->login_dma;
+ sge.addr = ib_conn->login_resp_dma;
sge.length = ISER_RX_LOGIN_SIZE;
sge.lkey = ib_conn->device->mr->lkey;
- rx_wr.wr_id = (unsigned long)ib_conn->login_buf;
+ rx_wr.wr_id = (unsigned long)ib_conn->login_resp_buf;
rx_wr.sg_list = &sge;
rx_wr.num_sge = 1;
rx_wr.next = NULL;
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 23e82e46656d..001b147c7f95 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -3,7 +3,7 @@
#
menu "Input device support"
- depends on !S390
+ depends on !S390 && !UML
config INPUT
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index a1aa35a053b7..22d875fde53a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -134,6 +134,16 @@ config INPUT_MAX8925_ONKEY
To compile this driver as a module, choose M here: the module
will be called max8925_onkey.
+config INPUT_MC13783_PWRBUTTON
+ tristate "MC13783 ON buttons"
+ depends on MFD_MC13783
+ help
+ Support the ON buttons of MC13783 PMIC as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mc13783-pwrbutton.
+
config INPUT_MMA8450
tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
depends on I2C
@@ -217,22 +227,6 @@ config INPUT_ATLAS_BTNS
To compile this driver as a module, choose M here: the module will
be called atlas_btns.
-config INPUT_ATI_REMOTE
- tristate "ATI / X10 USB RF remote control"
- depends on USB_ARCH_HAS_HCD
- select USB
- help
- Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
- These are RF remotes with USB receivers.
- The ATI remote comes with many of ATI's All-In-Wonder video cards.
- The X10 "Lola" remote is available at:
- <http://www.x10.com/products/lola_sg1.htm>
- This driver provides mouse pointer, left and right mouse buttons,
- and maps all the other remote buttons to keypress events.
-
- To compile this driver as a module, choose M here: the module will be
- called ati_remote.
-
config INPUT_ATI_REMOTE2
tristate "ATI / Philips USB RF remote control"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 53a8d0faad52..a244fc6a781c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
-obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
@@ -29,6 +28,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
+obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c
new file mode 100644
index 000000000000..09b052288657
--- /dev/null
+++ b/drivers/input/misc/mc13783-pwrbutton.c
@@ -0,0 +1,282 @@
+/**
+ * Copyright (C) 2011 Philippe Rétornaz
+ *
+ * Based on twl4030-pwrbutton driver by:
+ * Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Felipe Balbi <felipe.balbi@nokia.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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct mc13783_pwrb {
+ struct input_dev *pwr;
+ struct mc13xxx *mc13783;
+#define MC13783_PWRB_B1_POL_INVERT (1 << 0)
+#define MC13783_PWRB_B2_POL_INVERT (1 << 1)
+#define MC13783_PWRB_B3_POL_INVERT (1 << 2)
+ int flags;
+ unsigned short keymap[3];
+};
+
+#define MC13783_REG_INTERRUPT_SENSE_1 5
+#define MC13783_IRQSENSE1_ONOFD1S (1 << 3)
+#define MC13783_IRQSENSE1_ONOFD2S (1 << 4)
+#define MC13783_IRQSENSE1_ONOFD3S (1 << 5)
+
+#define MC13783_REG_POWER_CONTROL_2 15
+#define MC13783_POWER_CONTROL_2_ON1BDBNC 4
+#define MC13783_POWER_CONTROL_2_ON2BDBNC 6
+#define MC13783_POWER_CONTROL_2_ON3BDBNC 8
+#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1)
+#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2)
+#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3)
+
+static irqreturn_t button_irq(int irq, void *_priv)
+{
+ struct mc13783_pwrb *priv = _priv;
+ int val;
+
+ mc13xxx_irq_ack(priv->mc13783, irq);
+ mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val);
+
+ switch (irq) {
+ case MC13783_IRQ_ONOFD1:
+ val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B1_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[0], val);
+ break;
+
+ case MC13783_IRQ_ONOFD2:
+ val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B2_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[1], val);
+ break;
+
+ case MC13783_IRQ_ONOFD3:
+ val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B3_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[2], val);
+ break;
+ }
+
+ input_sync(priv->pwr);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev)
+{
+ const struct mc13xxx_buttons_platform_data *pdata;
+ struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
+ struct input_dev *pwr;
+ struct mc13783_pwrb *priv;
+ int err = 0;
+ int reg = 0;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ return -ENOMEM;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ goto free_input_dev;
+ }
+
+ reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC;
+ reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC;
+ reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC;
+
+ priv->pwr = pwr;
+ priv->mc13783 = mc13783;
+
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[0] = pdata->b1on_key;
+ if (pdata->b1on_key != KEY_RESERVED)
+ __set_bit(pdata->b1on_key, pwr->keybit);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B1_POL_INVERT;
+
+ if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1,
+ button_irq, "b1on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_priv;
+ }
+ }
+
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[1] = pdata->b2on_key;
+ if (pdata->b2on_key != KEY_RESERVED)
+ __set_bit(pdata->b2on_key, pwr->keybit);
+
+ if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B2_POL_INVERT;
+
+ if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2,
+ button_irq, "b2on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_irq_b1;
+ }
+ }
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[2] = pdata->b3on_key;
+ if (pdata->b3on_key != KEY_RESERVED)
+ __set_bit(pdata->b3on_key, pwr->keybit);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B3_POL_INVERT;
+
+ if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3,
+ button_irq, "b3on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq: %d\n", err);
+ goto free_irq_b2;
+ }
+ }
+
+ mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg);
+
+ mc13xxx_unlock(mc13783);
+
+ pwr->name = "mc13783_pwrbutton";
+ pwr->phys = "mc13783_pwrbutton/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ pwr->keycode = priv->keymap;
+ pwr->keycodemax = ARRAY_SIZE(priv->keymap);
+ pwr->keycodesize = sizeof(priv->keymap[0]);
+ __set_bit(EV_KEY, pwr->evbit);
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ goto free_irq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+free_irq:
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv);
+
+free_irq_b2:
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv);
+
+free_irq_b1:
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv);
+
+free_priv:
+ mc13xxx_unlock(mc13783);
+ kfree(priv);
+
+free_input_dev:
+ input_free_device(pwr);
+
+ return err;
+}
+
+static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev)
+{
+ struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
+ const struct mc13xxx_buttons_platform_data *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ mc13xxx_lock(priv->mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv);
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv);
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv);
+
+ mc13xxx_unlock(priv->mc13783);
+
+ input_unregister_device(priv->pwr);
+ kfree(priv);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+struct platform_driver mc13783_pwrbutton_driver = {
+ .probe = mc13783_pwrbutton_probe,
+ .remove = __devexit_p(mc13783_pwrbutton_remove),
+ .driver = {
+ .name = "mc13783-pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mc13783_pwrbutton_init(void)
+{
+ return platform_driver_register(&mc13783_pwrbutton_driver);
+}
+module_init(mc13783_pwrbutton_init);
+
+static void __exit mc13783_pwrbutton_exit(void)
+{
+ platform_driver_unregister(&mc13783_pwrbutton_driver);
+}
+module_exit(mc13783_pwrbutton_exit);
+
+MODULE_ALIAS("platform:mc13783-pwrbutton");
+MODULE_DESCRIPTION("MC13783 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Philippe Retornaz");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index c5bc62d85bb6..ede02743eac1 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -35,7 +35,7 @@ MODULE_PARM_DESC(sample_tolerance,
struct mc13783_ts_priv {
struct input_dev *idev;
- struct mc13783 *mc13783;
+ struct mc13xxx *mc13xxx;
struct delayed_work work;
struct workqueue_struct *workq;
unsigned int sample[4];
@@ -45,7 +45,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data)
{
struct mc13783_ts_priv *priv = data;
- mc13783_irq_ack(priv->mc13783, irq);
+ mc13xxx_irq_ack(priv->mc13xxx, irq);
/*
* Kick off reading coordinates. Note that if work happens already
@@ -121,10 +121,10 @@ static void mc13783_ts_work(struct work_struct *work)
{
struct mc13783_ts_priv *priv =
container_of(work, struct mc13783_ts_priv, work.work);
- unsigned int mode = MC13783_ADC_MODE_TS;
+ unsigned int mode = MC13XXX_ADC_MODE_TS;
unsigned int channel = 12;
- if (mc13783_adc_do_conversion(priv->mc13783,
+ if (mc13xxx_adc_do_conversion(priv->mc13xxx,
mode, channel, priv->sample) == 0)
mc13783_ts_report_sample(priv);
}
@@ -134,21 +134,21 @@ static int mc13783_ts_open(struct input_dev *dev)
struct mc13783_ts_priv *priv = input_get_drvdata(dev);
int ret;
- mc13783_lock(priv->mc13783);
+ mc13xxx_lock(priv->mc13xxx);
- mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS);
+ mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);
- ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS,
+ ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,
mc13783_ts_handler, MC13783_TS_NAME, priv);
if (ret)
goto out;
- ret = mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,
- MC13783_ADC0_TSMOD_MASK, MC13783_ADC0_TSMOD0);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);
if (ret)
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
out:
- mc13783_unlock(priv->mc13783);
+ mc13xxx_unlock(priv->mc13xxx);
return ret;
}
@@ -156,11 +156,11 @@ static void mc13783_ts_close(struct input_dev *dev)
{
struct mc13783_ts_priv *priv = input_get_drvdata(dev);
- mc13783_lock(priv->mc13783);
- mc13783_reg_rmw(priv->mc13783, MC13783_ADC0,
- MC13783_ADC0_TSMOD_MASK, 0);
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv);
- mc13783_unlock(priv->mc13783);
+ mc13xxx_lock(priv->mc13xxx);
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, 0);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+ mc13xxx_unlock(priv->mc13xxx);
cancel_delayed_work_sync(&priv->work);
}
@@ -177,7 +177,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
goto err_free_mem;
INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
- priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
priv->idev = idev;
/*
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 7d7eaa15e773..5414253b185a 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -112,4 +112,23 @@ config IRQ_REMAP
To use x2apic mode in the CPU's which support x2APIC enhancements or
to support platforms with CPU's having > 8 bit APIC ID, say Y.
+# OMAP IOMMU support
+config OMAP_IOMMU
+ bool "OMAP IOMMU Support"
+ depends on ARCH_OMAP
+ select IOMMU_API
+
+config OMAP_IOVMM
+ tristate "OMAP IO Virtual Memory Manager Support"
+ depends on OMAP_IOMMU
+
+config OMAP_IOMMU_DEBUG
+ tristate "Export OMAP IOMMU/IOVMM internals in DebugFS"
+ depends on OMAP_IOVMM && DEBUG_FS
+ help
+ Select this to see extensive information about
+ the internal state of OMAP IOMMU/IOVMM in debugfs.
+
+ Say N unless you know you need this.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 6394994a2b9d..2f4448794bc7 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -4,3 +4,6 @@ obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o
+obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
+obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
+obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 0e4227f457af..4ee277a8521a 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1283,7 +1283,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom,
if (!pte || !IOMMU_PTE_PRESENT(*pte))
continue;
- dma_ops_reserve_addresses(dma_dom, i << PAGE_SHIFT, 1);
+ dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, 1);
}
update_domain(&dma_dom->domain);
@@ -2495,7 +2495,7 @@ static unsigned device_dma_ops_init(void)
void __init amd_iommu_init_api(void)
{
- register_iommu(&amd_iommu_ops);
+ bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
}
int __init amd_iommu_init_dma_ops(void)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index be1953c239b0..bb161d2fa03c 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3642,7 +3642,7 @@ int __init intel_iommu_init(void)
init_iommu_pm_ops();
- register_iommu(&intel_iommu_ops);
+ bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
bus_register_notifier(&pci_bus_type, &device_nb);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 6e6b6a11b3ce..2fb2963df553 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -16,6 +16,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
+#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/types.h>
#include <linux/module.h>
@@ -23,32 +25,78 @@
#include <linux/errno.h>
#include <linux/iommu.h>
-static struct iommu_ops *iommu_ops;
+static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
+{
+}
-void register_iommu(struct iommu_ops *ops)
+/**
+ * bus_set_iommu - set iommu-callbacks for the bus
+ * @bus: bus.
+ * @ops: the callbacks provided by the iommu-driver
+ *
+ * This function is called by an iommu driver to set the iommu methods
+ * used for a particular bus. Drivers for devices on that bus can use
+ * the iommu-api after these ops are registered.
+ * This special function is needed because IOMMUs are usually devices on
+ * the bus itself, so the iommu drivers are not initialized when the bus
+ * is set up. With this function the iommu-driver can set the iommu-ops
+ * afterwards.
+ */
+int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops)
{
- if (iommu_ops)
- BUG();
+ if (bus->iommu_ops != NULL)
+ return -EBUSY;
+
+ bus->iommu_ops = ops;
+
+ /* Do IOMMU specific setup for this bus-type */
+ iommu_bus_init(bus, ops);
- iommu_ops = ops;
+ return 0;
}
+EXPORT_SYMBOL_GPL(bus_set_iommu);
-bool iommu_found(void)
+bool iommu_present(struct bus_type *bus)
{
- return iommu_ops != NULL;
+ return bus->iommu_ops != NULL;
}
-EXPORT_SYMBOL_GPL(iommu_found);
+EXPORT_SYMBOL_GPL(iommu_present);
-struct iommu_domain *iommu_domain_alloc(void)
+/**
+ * iommu_set_fault_handler() - set a fault handler for an iommu domain
+ * @domain: iommu domain
+ * @handler: fault handler
+ *
+ * This function should be used by IOMMU users which want to be notified
+ * whenever an IOMMU fault happens.
+ *
+ * The fault handler itself should return 0 on success, and an appropriate
+ * error code otherwise.
+ */
+void iommu_set_fault_handler(struct iommu_domain *domain,
+ iommu_fault_handler_t handler)
+{
+ BUG_ON(!domain);
+
+ domain->handler = handler;
+}
+EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
+
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
{
struct iommu_domain *domain;
int ret;
+ if (bus == NULL || bus->iommu_ops == NULL)
+ return NULL;
+
domain = kmalloc(sizeof(*domain), GFP_KERNEL);
if (!domain)
return NULL;
- ret = iommu_ops->domain_init(domain);
+ domain->ops = bus->iommu_ops;
+
+ ret = domain->ops->domain_init(domain);
if (ret)
goto out_free;
@@ -63,62 +111,78 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc);
void iommu_domain_free(struct iommu_domain *domain)
{
- iommu_ops->domain_destroy(domain);
+ if (likely(domain->ops->domain_destroy != NULL))
+ domain->ops->domain_destroy(domain);
+
kfree(domain);
}
EXPORT_SYMBOL_GPL(iommu_domain_free);
int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
{
- return iommu_ops->attach_dev(domain, dev);
+ if (unlikely(domain->ops->attach_dev == NULL))
+ return -ENODEV;
+
+ return domain->ops->attach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_attach_device);
void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
{
- iommu_ops->detach_dev(domain, dev);
+ if (unlikely(domain->ops->detach_dev == NULL))
+ return;
+
+ domain->ops->detach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_detach_device);
phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
unsigned long iova)
{
- return iommu_ops->iova_to_phys(domain, iova);
+ if (unlikely(domain->ops->iova_to_phys == NULL))
+ return 0;
+
+ return domain->ops->iova_to_phys(domain, iova);
}
EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
int iommu_domain_has_cap(struct iommu_domain *domain,
unsigned long cap)
{
- return iommu_ops->domain_has_cap(domain, cap);
+ if (unlikely(domain->ops->domain_has_cap == NULL))
+ return 0;
+
+ return domain->ops->domain_has_cap(domain, cap);
}
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, int gfp_order, int prot)
{
- unsigned long invalid_mask;
size_t size;
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
+ if (unlikely(domain->ops->map == NULL))
+ return -ENODEV;
- BUG_ON((iova | paddr) & invalid_mask);
+ size = PAGE_SIZE << gfp_order;
- return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
+ BUG_ON(!IS_ALIGNED(iova | paddr, size));
+
+ return domain->ops->map(domain, iova, paddr, gfp_order, prot);
}
EXPORT_SYMBOL_GPL(iommu_map);
int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
{
- unsigned long invalid_mask;
size_t size;
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
+ if (unlikely(domain->ops->unmap == NULL))
+ return -ENODEV;
+
+ size = PAGE_SIZE << gfp_order;
- BUG_ON(iova & invalid_mask);
+ BUG_ON(!IS_ALIGNED(iova, size));
- return iommu_ops->unmap(domain, iova, gfp_order);
+ return domain->ops->unmap(domain, iova, gfp_order);
}
EXPORT_SYMBOL_GPL(iommu_unmap);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 1a584e077c61..5865dd2e28f9 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -543,6 +543,13 @@ static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
}
ret = __flush_iotlb(domain);
+
+ /*
+ * the IOMMU API requires us to return the order of the unmapped
+ * page (on success).
+ */
+ if (!ret)
+ ret = order;
fail:
spin_unlock_irqrestore(&msm_iommu_lock, flags);
return ret;
@@ -721,7 +728,7 @@ static void __init setup_iommu_tex_classes(void)
static int __init msm_iommu_init(void)
{
setup_iommu_tex_classes();
- register_iommu(&msm_iommu_ops);
+ bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
return 0;
}
diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c
new file mode 100644
index 000000000000..9c192e79f806
--- /dev/null
+++ b/drivers/iommu/omap-iommu-debug.c
@@ -0,0 +1,418 @@
+/*
+ * omap iommu: debugfs interface
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.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/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include <plat/iopgtable.h>
+
+#define MAXCOLUMN 100 /* for short messages */
+
+static DEFINE_MUTEX(iommu_debug_lock);
+
+static struct dentry *iommu_debug_root;
+
+static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ u32 ver = omap_iommu_arch_version();
+ char buf[MAXCOLUMN], *p = buf;
+
+ p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+
+static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ ssize_t bytes;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ bytes = omap_iommu_dump_ctx(obj, p, count);
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);
+
+ mutex_unlock(&iommu_debug_lock);
+ kfree(buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ ssize_t bytes, rest;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ p += sprintf(p, "%8s %8s\n", "cam:", "ram:");
+ p += sprintf(p, "-----------------------------------------\n");
+ rest = count - (p - buf);
+ p += omap_dump_tlb_entries(obj, p, rest);
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ kfree(buf);
+
+ return bytes;
+}
+
+static ssize_t debug_write_pagetable(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct iotlb_entry e;
+ struct cr_regs cr;
+ int err;
+ struct omap_iommu *obj = file->private_data;
+ char buf[MAXCOLUMN], *p = buf;
+
+ count = min(count, sizeof(buf));
+
+ mutex_lock(&iommu_debug_lock);
+ if (copy_from_user(p, userbuf, count)) {
+ mutex_unlock(&iommu_debug_lock);
+ return -EFAULT;
+ }
+
+ sscanf(p, "%x %x", &cr.cam, &cr.ram);
+ if (!cr.cam || !cr.ram) {
+ mutex_unlock(&iommu_debug_lock);
+ return -EINVAL;
+ }
+
+ omap_iotlb_cr_to_e(&cr, &e);
+ err = omap_iopgtable_store_entry(obj, &e);
+ if (err)
+ dev_err(obj->dev, "%s: fail to store cr\n", __func__);
+
+ mutex_unlock(&iommu_debug_lock);
+ return count;
+}
+
+#define dump_ioptable_entry_one(lv, da, val) \
+ ({ \
+ int __err = 0; \
+ ssize_t bytes; \
+ const int maxcol = 22; \
+ const char *str = "%d: %08x %08x\n"; \
+ bytes = snprintf(p, maxcol, str, lv, da, val); \
+ p += bytes; \
+ len -= bytes; \
+ if (len < maxcol) \
+ __err = -ENOMEM; \
+ __err; \
+ })
+
+static ssize_t dump_ioptable(struct omap_iommu *obj, char *buf, ssize_t len)
+{
+ int i;
+ u32 *iopgd;
+ char *p = buf;
+
+ spin_lock(&obj->page_table_lock);
+
+ iopgd = iopgd_offset(obj, 0);
+ for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
+ int j, err;
+ u32 *iopte;
+ u32 da;
+
+ if (!*iopgd)
+ continue;
+
+ if (!(*iopgd & IOPGD_TABLE)) {
+ da = i << IOPGD_SHIFT;
+
+ err = dump_ioptable_entry_one(1, da, *iopgd);
+ if (err)
+ goto out;
+ continue;
+ }
+
+ iopte = iopte_offset(iopgd, 0);
+
+ for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
+ if (!*iopte)
+ continue;
+
+ da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
+ err = dump_ioptable_entry_one(2, da, *iopgd);
+ if (err)
+ goto out;
+ }
+ }
+out:
+ spin_unlock(&obj->page_table_lock);
+
+ return p - buf;
+}
+
+static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ size_t bytes;
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
+ p += sprintf(p, "-----------------------------------------\n");
+
+ mutex_lock(&iommu_debug_lock);
+
+ bytes = PAGE_SIZE - (p - buf);
+ p += dump_ioptable(obj, p, bytes);
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ struct iovm_struct *tmp;
+ int uninitialized_var(i);
+ ssize_t bytes;
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n",
+ "No", "start", "end", "size", "flags");
+ p += sprintf(p, "-------------------------------------------------\n");
+
+ mutex_lock(&iommu_debug_lock);
+
+ list_for_each_entry(tmp, &obj->mmap, list) {
+ size_t len;
+ const char *str = "%3d %08x-%08x %6x %8x\n";
+ const int maxcol = 39;
+
+ len = tmp->da_end - tmp->da_start;
+ p += snprintf(p, maxcol, str,
+ i, tmp->da_start, tmp->da_end, len, tmp->flags);
+
+ if (PAGE_SIZE - (p - buf) < maxcol)
+ break;
+ i++;
+ }
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ char *p, *buf;
+ struct iovm_struct *area;
+ ssize_t bytes;
+
+ count = min_t(ssize_t, count, PAGE_SIZE);
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ area = omap_find_iovm_area(obj, (u32)ppos);
+ if (IS_ERR(area)) {
+ bytes = -EINVAL;
+ goto err_out;
+ }
+ memcpy(p, area->va, count);
+ p += count;
+
+ bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+err_out:
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return bytes;
+}
+
+static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct omap_iommu *obj = file->private_data;
+ struct iovm_struct *area;
+ char *p, *buf;
+
+ count = min_t(size_t, count, PAGE_SIZE);
+
+ buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ p = buf;
+
+ mutex_lock(&iommu_debug_lock);
+
+ if (copy_from_user(p, userbuf, count)) {
+ count = -EFAULT;
+ goto err_out;
+ }
+
+ area = omap_find_iovm_area(obj, (u32)ppos);
+ if (IS_ERR(area)) {
+ count = -EINVAL;
+ goto err_out;
+ }
+ memcpy(area->va, p, count);
+err_out:
+ mutex_unlock(&iommu_debug_lock);
+ free_page((unsigned long)buf);
+
+ return count;
+}
+
+static int debug_open_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUG_FOPS(name) \
+ static const struct file_operations debug_##name##_fops = { \
+ .open = debug_open_generic, \
+ .read = debug_read_##name, \
+ .write = debug_write_##name, \
+ .llseek = generic_file_llseek, \
+ };
+
+#define DEBUG_FOPS_RO(name) \
+ static const struct file_operations debug_##name##_fops = { \
+ .open = debug_open_generic, \
+ .read = debug_read_##name, \
+ .llseek = generic_file_llseek, \
+ };
+
+DEBUG_FOPS_RO(ver);
+DEBUG_FOPS_RO(regs);
+DEBUG_FOPS_RO(tlb);
+DEBUG_FOPS(pagetable);
+DEBUG_FOPS_RO(mmap);
+DEBUG_FOPS(mem);
+
+#define __DEBUG_ADD_FILE(attr, mode) \
+ { \
+ struct dentry *dent; \
+ dent = debugfs_create_file(#attr, mode, parent, \
+ obj, &debug_##attr##_fops); \
+ if (!dent) \
+ return -ENOMEM; \
+ }
+
+#define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600)
+#define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400)
+
+static int iommu_debug_register(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_iommu *obj = platform_get_drvdata(pdev);
+ struct dentry *d, *parent;
+
+ if (!obj || !obj->dev)
+ return -EINVAL;
+
+ d = debugfs_create_dir(obj->name, iommu_debug_root);
+ if (!d)
+ return -ENOMEM;
+ parent = d;
+
+ d = debugfs_create_u8("nr_tlb_entries", 400, parent,
+ (u8 *)&obj->nr_tlb_entries);
+ if (!d)
+ return -ENOMEM;
+
+ DEBUG_ADD_FILE_RO(ver);
+ DEBUG_ADD_FILE_RO(regs);
+ DEBUG_ADD_FILE_RO(tlb);
+ DEBUG_ADD_FILE(pagetable);
+ DEBUG_ADD_FILE_RO(mmap);
+ DEBUG_ADD_FILE(mem);
+
+ return 0;
+}
+
+static int __init iommu_debug_init(void)
+{
+ struct dentry *d;
+ int err;
+
+ d = debugfs_create_dir("iommu", NULL);
+ if (!d)
+ return -ENOMEM;
+ iommu_debug_root = d;
+
+ err = omap_foreach_iommu_device(d, iommu_debug_register);
+ if (err)
+ goto err_out;
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(iommu_debug_root);
+ return err;
+}
+module_init(iommu_debug_init)
+
+static void __exit iommu_debugfs_exit(void)
+{
+ debugfs_remove_recursive(iommu_debug_root);
+}
+module_exit(iommu_debugfs_exit)
+
+MODULE_DESCRIPTION("omap iommu: debugfs interface");
+MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
new file mode 100644
index 000000000000..8f32b2bf7587
--- /dev/null
+++ b/drivers/iommu/omap-iommu.c
@@ -0,0 +1,1245 @@
+/*
+ * omap iommu: tlb and pagetable primitives
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
+ * Paul Mundt and Toshihiro Kobayashi
+ *
+ * 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/slab.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/iommu.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+
+#include <plat/iommu.h>
+
+#include <plat/iopgtable.h>
+
+#define for_each_iotlb_cr(obj, n, __i, cr) \
+ for (__i = 0; \
+ (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
+ __i++)
+
+/**
+ * struct omap_iommu_domain - omap iommu domain
+ * @pgtable: the page table
+ * @iommu_dev: an omap iommu device attached to this domain. only a single
+ * iommu device can be attached for now.
+ * @lock: domain lock, should be taken when attaching/detaching
+ */
+struct omap_iommu_domain {
+ u32 *pgtable;
+ struct omap_iommu *iommu_dev;
+ spinlock_t lock;
+};
+
+/* accommodate the difference between omap1 and omap2/3 */
+static const struct iommu_functions *arch_iommu;
+
+static struct platform_driver omap_iommu_driver;
+static struct kmem_cache *iopte_cachep;
+
+/**
+ * omap_install_iommu_arch - Install archtecure specific iommu functions
+ * @ops: a pointer to architecture specific iommu functions
+ *
+ * There are several kind of iommu algorithm(tlb, pagetable) among
+ * omap series. This interface installs such an iommu algorighm.
+ **/
+int omap_install_iommu_arch(const struct iommu_functions *ops)
+{
+ if (arch_iommu)
+ return -EBUSY;
+
+ arch_iommu = ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_install_iommu_arch);
+
+/**
+ * omap_uninstall_iommu_arch - Uninstall archtecure specific iommu functions
+ * @ops: a pointer to architecture specific iommu functions
+ *
+ * This interface uninstalls the iommu algorighm installed previously.
+ **/
+void omap_uninstall_iommu_arch(const struct iommu_functions *ops)
+{
+ if (arch_iommu != ops)
+ pr_err("%s: not your arch\n", __func__);
+
+ arch_iommu = NULL;
+}
+EXPORT_SYMBOL_GPL(omap_uninstall_iommu_arch);
+
+/**
+ * omap_iommu_save_ctx - Save registers for pm off-mode support
+ * @obj: target iommu
+ **/
+void omap_iommu_save_ctx(struct omap_iommu *obj)
+{
+ arch_iommu->save_ctx(obj);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
+
+/**
+ * omap_iommu_restore_ctx - Restore registers for pm off-mode support
+ * @obj: target iommu
+ **/
+void omap_iommu_restore_ctx(struct omap_iommu *obj)
+{
+ arch_iommu->restore_ctx(obj);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
+
+/**
+ * omap_iommu_arch_version - Return running iommu arch version
+ **/
+u32 omap_iommu_arch_version(void)
+{
+ return arch_iommu->version;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_arch_version);
+
+static int iommu_enable(struct omap_iommu *obj)
+{
+ int err;
+
+ if (!obj)
+ return -EINVAL;
+
+ if (!arch_iommu)
+ return -ENODEV;
+
+ clk_enable(obj->clk);
+
+ err = arch_iommu->enable(obj);
+
+ clk_disable(obj->clk);
+ return err;
+}
+
+static void iommu_disable(struct omap_iommu *obj)
+{
+ if (!obj)
+ return;
+
+ clk_enable(obj->clk);
+
+ arch_iommu->disable(obj);
+
+ clk_disable(obj->clk);
+}
+
+/*
+ * TLB operations
+ */
+void omap_iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e)
+{
+ BUG_ON(!cr || !e);
+
+ arch_iommu->cr_to_e(cr, e);
+}
+EXPORT_SYMBOL_GPL(omap_iotlb_cr_to_e);
+
+static inline int iotlb_cr_valid(struct cr_regs *cr)
+{
+ if (!cr)
+ return -EINVAL;
+
+ return arch_iommu->cr_valid(cr);
+}
+
+static inline struct cr_regs *iotlb_alloc_cr(struct omap_iommu *obj,
+ struct iotlb_entry *e)
+{
+ if (!e)
+ return NULL;
+
+ return arch_iommu->alloc_cr(obj, e);
+}
+
+static u32 iotlb_cr_to_virt(struct cr_regs *cr)
+{
+ return arch_iommu->cr_to_virt(cr);
+}
+
+static u32 get_iopte_attr(struct iotlb_entry *e)
+{
+ return arch_iommu->get_pte_attr(e);
+}
+
+static u32 iommu_report_fault(struct omap_iommu *obj, u32 *da)
+{
+ return arch_iommu->fault_isr(obj, da);
+}
+
+static void iotlb_lock_get(struct omap_iommu *obj, struct iotlb_lock *l)
+{
+ u32 val;
+
+ val = iommu_read_reg(obj, MMU_LOCK);
+
+ l->base = MMU_LOCK_BASE(val);
+ l->vict = MMU_LOCK_VICT(val);
+
+}
+
+static void iotlb_lock_set(struct omap_iommu *obj, struct iotlb_lock *l)
+{
+ u32 val;
+
+ val = (l->base << MMU_LOCK_BASE_SHIFT);
+ val |= (l->vict << MMU_LOCK_VICT_SHIFT);
+
+ iommu_write_reg(obj, val, MMU_LOCK);
+}
+
+static void iotlb_read_cr(struct omap_iommu *obj, struct cr_regs *cr)
+{
+ arch_iommu->tlb_read_cr(obj, cr);
+}
+
+static void iotlb_load_cr(struct omap_iommu *obj, struct cr_regs *cr)
+{
+ arch_iommu->tlb_load_cr(obj, cr);
+
+ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
+ iommu_write_reg(obj, 1, MMU_LD_TLB);
+}
+
+/**
+ * iotlb_dump_cr - Dump an iommu tlb entry into buf
+ * @obj: target iommu
+ * @cr: contents of cam and ram register
+ * @buf: output buffer
+ **/
+static inline ssize_t iotlb_dump_cr(struct omap_iommu *obj, struct cr_regs *cr,
+ char *buf)
+{
+ BUG_ON(!cr || !buf);
+
+ return arch_iommu->dump_cr(obj, cr, buf);
+}
+
+/* only used in iotlb iteration for-loop */
+static struct cr_regs __iotlb_read_cr(struct omap_iommu *obj, int n)
+{
+ struct cr_regs cr;
+ struct iotlb_lock l;
+
+ iotlb_lock_get(obj, &l);
+ l.vict = n;
+ iotlb_lock_set(obj, &l);
+ iotlb_read_cr(obj, &cr);
+
+ return cr;
+}
+
+/**
+ * load_iotlb_entry - Set an iommu tlb entry
+ * @obj: target iommu
+ * @e: an iommu tlb entry info
+ **/
+#ifdef PREFETCH_IOTLB
+static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int err = 0;
+ struct iotlb_lock l;
+ struct cr_regs *cr;
+
+ if (!obj || !obj->nr_tlb_entries || !e)
+ return -EINVAL;
+
+ clk_enable(obj->clk);
+
+ iotlb_lock_get(obj, &l);
+ if (l.base == obj->nr_tlb_entries) {
+ dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+ if (!e->prsvd) {
+ int i;
+ struct cr_regs tmp;
+
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp)
+ if (!iotlb_cr_valid(&tmp))
+ break;
+
+ if (i == obj->nr_tlb_entries) {
+ dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ iotlb_lock_get(obj, &l);
+ } else {
+ l.vict = l.base;
+ iotlb_lock_set(obj, &l);
+ }
+
+ cr = iotlb_alloc_cr(obj, e);
+ if (IS_ERR(cr)) {
+ clk_disable(obj->clk);
+ return PTR_ERR(cr);
+ }
+
+ iotlb_load_cr(obj, cr);
+ kfree(cr);
+
+ if (e->prsvd)
+ l.base++;
+ /* increment victim for next tlb load */
+ if (++l.vict == obj->nr_tlb_entries)
+ l.vict = l.base;
+ iotlb_lock_set(obj, &l);
+out:
+ clk_disable(obj->clk);
+ return err;
+}
+
+#else /* !PREFETCH_IOTLB */
+
+static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ return 0;
+}
+
+#endif /* !PREFETCH_IOTLB */
+
+static int prefetch_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ return load_iotlb_entry(obj, e);
+}
+
+/**
+ * flush_iotlb_page - Clear an iommu tlb entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ *
+ * Clear an iommu tlb entry which includes 'da' address.
+ **/
+static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
+{
+ int i;
+ struct cr_regs cr;
+
+ clk_enable(obj->clk);
+
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
+ u32 start;
+ size_t bytes;
+
+ if (!iotlb_cr_valid(&cr))
+ continue;
+
+ start = iotlb_cr_to_virt(&cr);
+ bytes = iopgsz_to_bytes(cr.cam & 3);
+
+ if ((start <= da) && (da < start + bytes)) {
+ dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n",
+ __func__, start, da, bytes);
+ iotlb_load_cr(obj, &cr);
+ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
+ }
+ }
+ clk_disable(obj->clk);
+
+ if (i == obj->nr_tlb_entries)
+ dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
+}
+
+/**
+ * flush_iotlb_all - Clear all iommu tlb entries
+ * @obj: target iommu
+ **/
+static void flush_iotlb_all(struct omap_iommu *obj)
+{
+ struct iotlb_lock l;
+
+ clk_enable(obj->clk);
+
+ l.base = 0;
+ l.vict = 0;
+ iotlb_lock_set(obj, &l);
+
+ iommu_write_reg(obj, 1, MMU_GFLUSH);
+
+ clk_disable(obj->clk);
+}
+
+#if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
+
+ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes)
+{
+ if (!obj || !buf)
+ return -EINVAL;
+
+ clk_enable(obj->clk);
+
+ bytes = arch_iommu->dump_ctx(obj, buf, bytes);
+
+ clk_disable(obj->clk);
+
+ return bytes;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_dump_ctx);
+
+static int
+__dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
+{
+ int i;
+ struct iotlb_lock saved;
+ struct cr_regs tmp;
+ struct cr_regs *p = crs;
+
+ clk_enable(obj->clk);
+ iotlb_lock_get(obj, &saved);
+
+ for_each_iotlb_cr(obj, num, i, tmp) {
+ if (!iotlb_cr_valid(&tmp))
+ continue;
+ *p++ = tmp;
+ }
+
+ iotlb_lock_set(obj, &saved);
+ clk_disable(obj->clk);
+
+ return p - crs;
+}
+
+/**
+ * omap_dump_tlb_entries - dump cr arrays to given buffer
+ * @obj: target iommu
+ * @buf: output buffer
+ **/
+size_t omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t bytes)
+{
+ int i, num;
+ struct cr_regs *cr;
+ char *p = buf;
+
+ num = bytes / sizeof(*cr);
+ num = min(obj->nr_tlb_entries, num);
+
+ cr = kcalloc(num, sizeof(*cr), GFP_KERNEL);
+ if (!cr)
+ return 0;
+
+ num = __dump_tlb_entries(obj, cr, num);
+ for (i = 0; i < num; i++)
+ p += iotlb_dump_cr(obj, cr + i, p);
+ kfree(cr);
+
+ return p - buf;
+}
+EXPORT_SYMBOL_GPL(omap_dump_tlb_entries);
+
+int omap_foreach_iommu_device(void *data, int (*fn)(struct device *, void *))
+{
+ return driver_for_each_device(&omap_iommu_driver.driver,
+ NULL, data, fn);
+}
+EXPORT_SYMBOL_GPL(omap_foreach_iommu_device);
+
+#endif /* CONFIG_OMAP_IOMMU_DEBUG_MODULE */
+
+/*
+ * H/W pagetable operations
+ */
+static void flush_iopgd_range(u32 *first, u32 *last)
+{
+ /* FIXME: L2 cache should be taken care of if it exists */
+ do {
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
+ : : "r" (first));
+ first += L1_CACHE_BYTES / sizeof(*first);
+ } while (first <= last);
+}
+
+static void flush_iopte_range(u32 *first, u32 *last)
+{
+ /* FIXME: L2 cache should be taken care of if it exists */
+ do {
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
+ : : "r" (first));
+ first += L1_CACHE_BYTES / sizeof(*first);
+ } while (first <= last);
+}
+
+static void iopte_free(u32 *iopte)
+{
+ /* Note: freed iopte's must be clean ready for re-use */
+ kmem_cache_free(iopte_cachep, iopte);
+}
+
+static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
+{
+ u32 *iopte;
+
+ /* a table has already existed */
+ if (*iopgd)
+ goto pte_ready;
+
+ /*
+ * do the allocation outside the page table lock
+ */
+ spin_unlock(&obj->page_table_lock);
+ iopte = kmem_cache_zalloc(iopte_cachep, GFP_KERNEL);
+ spin_lock(&obj->page_table_lock);
+
+ if (!*iopgd) {
+ if (!iopte)
+ return ERR_PTR(-ENOMEM);
+
+ *iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
+ flush_iopgd_range(iopgd, iopgd);
+
+ dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
+ } else {
+ /* We raced, free the reduniovant table */
+ iopte_free(iopte);
+ }
+
+pte_ready:
+ iopte = iopte_offset(iopgd, da);
+
+ dev_vdbg(obj->dev,
+ "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
+ __func__, da, iopgd, *iopgd, iopte, *iopte);
+
+ return iopte;
+}
+
+static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+
+ if ((da | pa) & ~IOSECTION_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSECTION_SIZE);
+ return -EINVAL;
+ }
+
+ *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
+ flush_iopgd_range(iopgd, iopgd);
+ return 0;
+}
+
+static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ int i;
+
+ if ((da | pa) & ~IOSUPER_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSUPER_SIZE);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
+ flush_iopgd_range(iopgd, iopgd + 15);
+ return 0;
+}
+
+static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ u32 *iopte = iopte_alloc(obj, iopgd, da);
+
+ if (IS_ERR(iopte))
+ return PTR_ERR(iopte);
+
+ *iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
+ flush_iopte_range(iopte, iopte);
+
+ dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
+ __func__, da, pa, iopte, *iopte);
+
+ return 0;
+}
+
+static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
+{
+ u32 *iopgd = iopgd_offset(obj, da);
+ u32 *iopte = iopte_alloc(obj, iopgd, da);
+ int i;
+
+ if ((da | pa) & ~IOLARGE_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOLARGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (IS_ERR(iopte))
+ return PTR_ERR(iopte);
+
+ for (i = 0; i < 16; i++)
+ *(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
+ flush_iopte_range(iopte, iopte + 15);
+ return 0;
+}
+
+static int
+iopgtable_store_entry_core(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int (*fn)(struct omap_iommu *, u32, u32, u32);
+ u32 prot;
+ int err;
+
+ if (!obj || !e)
+ return -EINVAL;
+
+ switch (e->pgsz) {
+ case MMU_CAM_PGSZ_16M:
+ fn = iopgd_alloc_super;
+ break;
+ case MMU_CAM_PGSZ_1M:
+ fn = iopgd_alloc_section;
+ break;
+ case MMU_CAM_PGSZ_64K:
+ fn = iopte_alloc_large;
+ break;
+ case MMU_CAM_PGSZ_4K:
+ fn = iopte_alloc_page;
+ break;
+ default:
+ fn = NULL;
+ BUG();
+ break;
+ }
+
+ prot = get_iopte_attr(e);
+
+ spin_lock(&obj->page_table_lock);
+ err = fn(obj, e->da, e->pa, prot);
+ spin_unlock(&obj->page_table_lock);
+
+ return err;
+}
+
+/**
+ * omap_iopgtable_store_entry - Make an iommu pte entry
+ * @obj: target iommu
+ * @e: an iommu tlb entry info
+ **/
+int omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e)
+{
+ int err;
+
+ flush_iotlb_page(obj, e->da);
+ err = iopgtable_store_entry_core(obj, e);
+ if (!err)
+ prefetch_iotlb_entry(obj, e);
+ return err;
+}
+EXPORT_SYMBOL_GPL(omap_iopgtable_store_entry);
+
+/**
+ * iopgtable_lookup_entry - Lookup an iommu pte entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ * @ppgd: iommu pgd entry pointer to be returned
+ * @ppte: iommu pte entry pointer to be returned
+ **/
+static void
+iopgtable_lookup_entry(struct omap_iommu *obj, u32 da, u32 **ppgd, u32 **ppte)
+{
+ u32 *iopgd, *iopte = NULL;
+
+ iopgd = iopgd_offset(obj, da);
+ if (!*iopgd)
+ goto out;
+
+ if (iopgd_is_table(*iopgd))
+ iopte = iopte_offset(iopgd, da);
+out:
+ *ppgd = iopgd;
+ *ppte = iopte;
+}
+
+static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
+{
+ size_t bytes;
+ u32 *iopgd = iopgd_offset(obj, da);
+ int nent = 1;
+
+ if (!*iopgd)
+ return 0;
+
+ if (iopgd_is_table(*iopgd)) {
+ int i;
+ u32 *iopte = iopte_offset(iopgd, da);
+
+ bytes = IOPTE_SIZE;
+ if (*iopte & IOPTE_LARGE) {
+ nent *= 16;
+ /* rewind to the 1st entry */
+ iopte = iopte_offset(iopgd, (da & IOLARGE_MASK));
+ }
+ bytes *= nent;
+ memset(iopte, 0, nent * sizeof(*iopte));
+ flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte));
+
+ /*
+ * do table walk to check if this table is necessary or not
+ */
+ iopte = iopte_offset(iopgd, 0);
+ for (i = 0; i < PTRS_PER_IOPTE; i++)
+ if (iopte[i])
+ goto out;
+
+ iopte_free(iopte);
+ nent = 1; /* for the next L1 entry */
+ } else {
+ bytes = IOPGD_SIZE;
+ if ((*iopgd & IOPGD_SUPER) == IOPGD_SUPER) {
+ nent *= 16;
+ /* rewind to the 1st entry */
+ iopgd = iopgd_offset(obj, (da & IOSUPER_MASK));
+ }
+ bytes *= nent;
+ }
+ memset(iopgd, 0, nent * sizeof(*iopgd));
+ flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd));
+out:
+ return bytes;
+}
+
+/**
+ * iopgtable_clear_entry - Remove an iommu pte entry
+ * @obj: target iommu
+ * @da: iommu device virtual address
+ **/
+static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
+{
+ size_t bytes;
+
+ spin_lock(&obj->page_table_lock);
+
+ bytes = iopgtable_clear_entry_core(obj, da);
+ flush_iotlb_page(obj, da);
+
+ spin_unlock(&obj->page_table_lock);
+
+ return bytes;
+}
+
+static void iopgtable_clear_entry_all(struct omap_iommu *obj)
+{
+ int i;
+
+ spin_lock(&obj->page_table_lock);
+
+ for (i = 0; i < PTRS_PER_IOPGD; i++) {
+ u32 da;
+ u32 *iopgd;
+
+ da = i << IOPGD_SHIFT;
+ iopgd = iopgd_offset(obj, da);
+
+ if (!*iopgd)
+ continue;
+
+ if (iopgd_is_table(*iopgd))
+ iopte_free(iopte_offset(iopgd, 0));
+
+ *iopgd = 0;
+ flush_iopgd_range(iopgd, iopgd);
+ }
+
+ flush_iotlb_all(obj);
+
+ spin_unlock(&obj->page_table_lock);
+}
+
+/*
+ * Device IOMMU generic operations
+ */
+static irqreturn_t iommu_fault_handler(int irq, void *data)
+{
+ u32 da, errs;
+ u32 *iopgd, *iopte;
+ struct omap_iommu *obj = data;
+ struct iommu_domain *domain = obj->domain;
+
+ if (!obj->refcount)
+ return IRQ_NONE;
+
+ clk_enable(obj->clk);
+ errs = iommu_report_fault(obj, &da);
+ clk_disable(obj->clk);
+ if (errs == 0)
+ return IRQ_HANDLED;
+
+ /* Fault callback or TLB/PTE Dynamic loading */
+ if (!report_iommu_fault(domain, obj->dev, da, 0))
+ return IRQ_HANDLED;
+
+ iommu_disable(obj);
+
+ iopgd = iopgd_offset(obj, da);
+
+ if (!iopgd_is_table(*iopgd)) {
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
+ "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
+ return IRQ_NONE;
+ }
+
+ iopte = iopte_offset(iopgd, da);
+
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
+ "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
+ iopte, *iopte);
+
+ return IRQ_NONE;
+}
+
+static int device_match_by_alias(struct device *dev, void *data)
+{
+ struct omap_iommu *obj = to_iommu(dev);
+ const char *name = data;
+
+ pr_debug("%s: %s %s\n", __func__, obj->name, name);
+
+ return strcmp(obj->name, name) == 0;
+}
+
+/**
+ * omap_find_iommu_device() - find an omap iommu device by name
+ * @name: name of the iommu device
+ *
+ * The generic iommu API requires the caller to provide the device
+ * he wishes to attach to a certain iommu domain.
+ *
+ * Drivers generally should not bother with this as it should just
+ * be taken care of by the DMA-API using dev_archdata.
+ *
+ * This function is provided as an interim solution until the latter
+ * materializes, and omap3isp is fully migrated to the DMA-API.
+ */
+struct device *omap_find_iommu_device(const char *name)
+{
+ return driver_find_device(&omap_iommu_driver.driver, NULL,
+ (void *)name,
+ device_match_by_alias);
+}
+EXPORT_SYMBOL_GPL(omap_find_iommu_device);
+
+/**
+ * omap_iommu_attach() - attach iommu device to an iommu domain
+ * @dev: target omap iommu device
+ * @iopgd: page table
+ **/
+static struct omap_iommu *omap_iommu_attach(struct device *dev, u32 *iopgd)
+{
+ int err = -ENOMEM;
+ struct omap_iommu *obj = to_iommu(dev);
+
+ spin_lock(&obj->iommu_lock);
+
+ /* an iommu device can only be attached once */
+ if (++obj->refcount > 1) {
+ dev_err(dev, "%s: already attached!\n", obj->name);
+ err = -EBUSY;
+ goto err_enable;
+ }
+
+ obj->iopgd = iopgd;
+ err = iommu_enable(obj);
+ if (err)
+ goto err_enable;
+ flush_iotlb_all(obj);
+
+ if (!try_module_get(obj->owner))
+ goto err_module;
+
+ spin_unlock(&obj->iommu_lock);
+
+ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
+ return obj;
+
+err_module:
+ if (obj->refcount == 1)
+ iommu_disable(obj);
+err_enable:
+ obj->refcount--;
+ spin_unlock(&obj->iommu_lock);
+ return ERR_PTR(err);
+}
+
+/**
+ * omap_iommu_detach - release iommu device
+ * @obj: target iommu
+ **/
+static void omap_iommu_detach(struct omap_iommu *obj)
+{
+ if (!obj || IS_ERR(obj))
+ return;
+
+ spin_lock(&obj->iommu_lock);
+
+ if (--obj->refcount == 0)
+ iommu_disable(obj);
+
+ module_put(obj->owner);
+
+ obj->iopgd = NULL;
+
+ spin_unlock(&obj->iommu_lock);
+
+ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
+}
+
+/*
+ * OMAP Device MMU(IOMMU) detection
+ */
+static int __devinit omap_iommu_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ int irq;
+ struct omap_iommu *obj;
+ struct resource *res;
+ struct iommu_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdev->num_resources != 2)
+ return -EINVAL;
+
+ obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ obj->clk = clk_get(&pdev->dev, pdata->clk_name);
+ if (IS_ERR(obj->clk))
+ goto err_clk;
+
+ obj->nr_tlb_entries = pdata->nr_tlb_entries;
+ obj->name = pdata->name;
+ obj->dev = &pdev->dev;
+ obj->ctx = (void *)obj + sizeof(*obj);
+ obj->da_start = pdata->da_start;
+ obj->da_end = pdata->da_end;
+
+ spin_lock_init(&obj->iommu_lock);
+ mutex_init(&obj->mmap_lock);
+ spin_lock_init(&obj->page_table_lock);
+ INIT_LIST_HEAD(&obj->mmap);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto err_mem;
+ }
+
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!res) {
+ err = -EIO;
+ goto err_mem;
+ }
+
+ obj->regbase = ioremap(res->start, resource_size(res));
+ if (!obj->regbase) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -ENODEV;
+ goto err_irq;
+ }
+ err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
+ dev_name(&pdev->dev), obj);
+ if (err < 0)
+ goto err_irq;
+ platform_set_drvdata(pdev, obj);
+
+ dev_info(&pdev->dev, "%s registered\n", obj->name);
+ return 0;
+
+err_irq:
+ iounmap(obj->regbase);
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+err_mem:
+ clk_put(obj->clk);
+err_clk:
+ kfree(obj);
+ return err;
+}
+
+static int __devexit omap_iommu_remove(struct platform_device *pdev)
+{
+ int irq;
+ struct resource *res;
+ struct omap_iommu *obj = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ iopgtable_clear_entry_all(obj);
+
+ irq = platform_get_irq(pdev, 0);
+ free_irq(irq, obj);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+ iounmap(obj->regbase);
+
+ clk_put(obj->clk);
+ dev_info(&pdev->dev, "%s removed\n", obj->name);
+ kfree(obj);
+ return 0;
+}
+
+static struct platform_driver omap_iommu_driver = {
+ .probe = omap_iommu_probe,
+ .remove = __devexit_p(omap_iommu_remove),
+ .driver = {
+ .name = "omap-iommu",
+ },
+};
+
+static void iopte_cachep_ctor(void *iopte)
+{
+ clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
+}
+
+static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
+ phys_addr_t pa, int order, int prot)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ size_t bytes = PAGE_SIZE << order;
+ struct iotlb_entry e;
+ int omap_pgsz;
+ u32 ret, flags;
+
+ /* we only support mapping a single iommu page for now */
+ omap_pgsz = bytes_to_iopgsz(bytes);
+ if (omap_pgsz < 0) {
+ dev_err(dev, "invalid size to map: %d\n", bytes);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "mapping da 0x%lx to pa 0x%x size 0x%x\n", da, pa, bytes);
+
+ flags = omap_pgsz | prot;
+
+ iotlb_init_entry(&e, da, pa, flags);
+
+ ret = omap_iopgtable_store_entry(oiommu, &e);
+ if (ret)
+ dev_err(dev, "omap_iopgtable_store_entry failed: %d\n", ret);
+
+ return ret;
+}
+
+static int omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
+ int order)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ size_t unmap_size;
+
+ dev_dbg(dev, "unmapping da 0x%lx order %d\n", da, order);
+
+ unmap_size = iopgtable_clear_entry(oiommu, da);
+
+ return unmap_size ? get_order(unmap_size) : -EINVAL;
+}
+
+static int
+omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu;
+ int ret = 0;
+
+ spin_lock(&omap_domain->lock);
+
+ /* only a single device is supported per domain for now */
+ if (omap_domain->iommu_dev) {
+ dev_err(dev, "iommu domain is already attached\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* get a handle to and enable the omap iommu */
+ oiommu = omap_iommu_attach(dev, omap_domain->pgtable);
+ if (IS_ERR(oiommu)) {
+ ret = PTR_ERR(oiommu);
+ dev_err(dev, "can't get omap iommu: %d\n", ret);
+ goto out;
+ }
+
+ omap_domain->iommu_dev = oiommu;
+ oiommu->domain = domain;
+
+out:
+ spin_unlock(&omap_domain->lock);
+ return ret;
+}
+
+static void omap_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = to_iommu(dev);
+
+ spin_lock(&omap_domain->lock);
+
+ /* only a single device is supported per domain for now */
+ if (omap_domain->iommu_dev != oiommu) {
+ dev_err(dev, "invalid iommu device\n");
+ goto out;
+ }
+
+ iopgtable_clear_entry_all(oiommu);
+
+ omap_iommu_detach(oiommu);
+
+ omap_domain->iommu_dev = NULL;
+
+out:
+ spin_unlock(&omap_domain->lock);
+}
+
+static int omap_iommu_domain_init(struct iommu_domain *domain)
+{
+ struct omap_iommu_domain *omap_domain;
+
+ omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
+ if (!omap_domain) {
+ pr_err("kzalloc failed\n");
+ goto out;
+ }
+
+ omap_domain->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_KERNEL);
+ if (!omap_domain->pgtable) {
+ pr_err("kzalloc failed\n");
+ goto fail_nomem;
+ }
+
+ /*
+ * should never fail, but please keep this around to ensure
+ * we keep the hardware happy
+ */
+ BUG_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE));
+
+ clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE);
+ spin_lock_init(&omap_domain->lock);
+
+ domain->priv = omap_domain;
+
+ return 0;
+
+fail_nomem:
+ kfree(omap_domain);
+out:
+ return -ENOMEM;
+}
+
+/* assume device was already detached */
+static void omap_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+
+ domain->priv = NULL;
+
+ kfree(omap_domain->pgtable);
+ kfree(omap_domain);
+}
+
+static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long da)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu *oiommu = omap_domain->iommu_dev;
+ struct device *dev = oiommu->dev;
+ u32 *pgd, *pte;
+ phys_addr_t ret = 0;
+
+ iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
+
+ if (pte) {
+ if (iopte_is_small(*pte))
+ ret = omap_iommu_translate(*pte, da, IOPTE_MASK);
+ else if (iopte_is_large(*pte))
+ ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
+ else
+ dev_err(dev, "bogus pte 0x%x", *pte);
+ } else {
+ if (iopgd_is_section(*pgd))
+ ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
+ else if (iopgd_is_super(*pgd))
+ ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
+ else
+ dev_err(dev, "bogus pgd 0x%x", *pgd);
+ }
+
+ return ret;
+}
+
+static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static struct iommu_ops omap_iommu_ops = {
+ .domain_init = omap_iommu_domain_init,
+ .domain_destroy = omap_iommu_domain_destroy,
+ .attach_dev = omap_iommu_attach_dev,
+ .detach_dev = omap_iommu_detach_dev,
+ .map = omap_iommu_map,
+ .unmap = omap_iommu_unmap,
+ .iova_to_phys = omap_iommu_iova_to_phys,
+ .domain_has_cap = omap_iommu_domain_has_cap,
+};
+
+static int __init omap_iommu_init(void)
+{
+ struct kmem_cache *p;
+ const unsigned long flags = SLAB_HWCACHE_ALIGN;
+ size_t align = 1 << 10; /* L2 pagetable alignement */
+
+ p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
+ iopte_cachep_ctor);
+ if (!p)
+ return -ENOMEM;
+ iopte_cachep = p;
+
+ bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
+
+ return platform_driver_register(&omap_iommu_driver);
+}
+module_init(omap_iommu_init);
+
+static void __exit omap_iommu_exit(void)
+{
+ kmem_cache_destroy(iopte_cachep);
+
+ platform_driver_unregister(&omap_iommu_driver);
+}
+module_exit(omap_iommu_exit);
+
+MODULE_DESCRIPTION("omap iommu: tlb and pagetable primitives");
+MODULE_ALIAS("platform:omap-iommu");
+MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c
new file mode 100644
index 000000000000..e8fdb8830f69
--- /dev/null
+++ b/drivers/iommu/omap-iovmm.c
@@ -0,0 +1,742 @@
+/*
+ * omap iommu: simple virtual address space management
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.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/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/scatterlist.h>
+#include <linux/iommu.h>
+
+#include <asm/cacheflush.h>
+#include <asm/mach/map.h>
+
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include <plat/iopgtable.h>
+
+static struct kmem_cache *iovm_area_cachep;
+
+/* return the offset of the first scatterlist entry in a sg table */
+static unsigned int sgtable_offset(const struct sg_table *sgt)
+{
+ if (!sgt || !sgt->nents)
+ return 0;
+
+ return sgt->sgl->offset;
+}
+
+/* return total bytes of sg buffers */
+static size_t sgtable_len(const struct sg_table *sgt)
+{
+ unsigned int i, total = 0;
+ struct scatterlist *sg;
+
+ if (!sgt)
+ return 0;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+
+ bytes = sg->length + sg->offset;
+
+ if (!iopgsz_ok(bytes)) {
+ pr_err("%s: sg[%d] not iommu pagesize(%u %u)\n",
+ __func__, i, bytes, sg->offset);
+ return 0;
+ }
+
+ if (i && sg->offset) {
+ pr_err("%s: sg[%d] offset not allowed in internal "
+ "entries\n", __func__, i);
+ return 0;
+ }
+
+ total += bytes;
+ }
+
+ return total;
+}
+#define sgtable_ok(x) (!!sgtable_len(x))
+
+static unsigned max_alignment(u32 addr)
+{
+ int i;
+ unsigned pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, };
+ for (i = 0; i < ARRAY_SIZE(pagesize) && addr & (pagesize[i] - 1); i++)
+ ;
+ return (i < ARRAY_SIZE(pagesize)) ? pagesize[i] : 0;
+}
+
+/*
+ * calculate the optimal number sg elements from total bytes based on
+ * iommu superpages
+ */
+static unsigned sgtable_nents(size_t bytes, u32 da, u32 pa)
+{
+ unsigned nr_entries = 0, ent_sz;
+
+ if (!IS_ALIGNED(bytes, PAGE_SIZE)) {
+ pr_err("%s: wrong size %08x\n", __func__, bytes);
+ return 0;
+ }
+
+ while (bytes) {
+ ent_sz = max_alignment(da | pa);
+ ent_sz = min_t(unsigned, ent_sz, iopgsz_max(bytes));
+ nr_entries++;
+ da += ent_sz;
+ pa += ent_sz;
+ bytes -= ent_sz;
+ }
+
+ return nr_entries;
+}
+
+/* allocate and initialize sg_table header(a kind of 'superblock') */
+static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags,
+ u32 da, u32 pa)
+{
+ unsigned int nr_entries;
+ int err;
+ struct sg_table *sgt;
+
+ if (!bytes)
+ return ERR_PTR(-EINVAL);
+
+ if (!IS_ALIGNED(bytes, PAGE_SIZE))
+ return ERR_PTR(-EINVAL);
+
+ if (flags & IOVMF_LINEAR) {
+ nr_entries = sgtable_nents(bytes, da, pa);
+ if (!nr_entries)
+ return ERR_PTR(-EINVAL);
+ } else
+ nr_entries = bytes / PAGE_SIZE;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL);
+ if (err) {
+ kfree(sgt);
+ return ERR_PTR(err);
+ }
+
+ pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries);
+
+ return sgt;
+}
+
+/* free sg_table header(a kind of superblock) */
+static void sgtable_free(struct sg_table *sgt)
+{
+ if (!sgt)
+ return;
+
+ sg_free_table(sgt);
+ kfree(sgt);
+
+ pr_debug("%s: sgt:%p\n", __func__, sgt);
+}
+
+/* map 'sglist' to a contiguous mpu virtual area and return 'va' */
+static void *vmap_sg(const struct sg_table *sgt)
+{
+ u32 va;
+ size_t total;
+ unsigned int i;
+ struct scatterlist *sg;
+ struct vm_struct *new;
+ const struct mem_type *mtype;
+
+ mtype = get_mem_type(MT_DEVICE);
+ if (!mtype)
+ return ERR_PTR(-EINVAL);
+
+ total = sgtable_len(sgt);
+ if (!total)
+ return ERR_PTR(-EINVAL);
+
+ new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+ va = (u32)new->addr;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+ u32 pa;
+ int err;
+
+ pa = sg_phys(sg) - sg->offset;
+ bytes = sg->length + sg->offset;
+
+ BUG_ON(bytes != PAGE_SIZE);
+
+ err = ioremap_page(va, pa, mtype);
+ if (err)
+ goto err_out;
+
+ va += bytes;
+ }
+
+ flush_cache_vmap((unsigned long)new->addr,
+ (unsigned long)(new->addr + total));
+ return new->addr;
+
+err_out:
+ WARN_ON(1); /* FIXME: cleanup some mpu mappings */
+ vunmap(new->addr);
+ return ERR_PTR(-EAGAIN);
+}
+
+static inline void vunmap_sg(const void *va)
+{
+ vunmap(va);
+}
+
+static struct iovm_struct *__find_iovm_area(struct omap_iommu *obj,
+ const u32 da)
+{
+ struct iovm_struct *tmp;
+
+ list_for_each_entry(tmp, &obj->mmap, list) {
+ if ((da >= tmp->da_start) && (da < tmp->da_end)) {
+ size_t len;
+
+ len = tmp->da_end - tmp->da_start;
+
+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n",
+ __func__, tmp->da_start, da, tmp->da_end, len,
+ tmp->flags);
+
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * omap_find_iovm_area - find iovma which includes @da
+ * @da: iommu device virtual address
+ *
+ * Find the existing iovma starting at @da
+ */
+struct iovm_struct *omap_find_iovm_area(struct omap_iommu *obj, u32 da)
+{
+ struct iovm_struct *area;
+
+ mutex_lock(&obj->mmap_lock);
+ area = __find_iovm_area(obj, da);
+ mutex_unlock(&obj->mmap_lock);
+
+ return area;
+}
+EXPORT_SYMBOL_GPL(omap_find_iovm_area);
+
+/*
+ * This finds the hole(area) which fits the requested address and len
+ * in iovmas mmap, and returns the new allocated iovma.
+ */
+static struct iovm_struct *alloc_iovm_area(struct omap_iommu *obj, u32 da,
+ size_t bytes, u32 flags)
+{
+ struct iovm_struct *new, *tmp;
+ u32 start, prev_end, alignment;
+
+ if (!obj || !bytes)
+ return ERR_PTR(-EINVAL);
+
+ start = da;
+ alignment = PAGE_SIZE;
+
+ if (~flags & IOVMF_DA_FIXED) {
+ /* Don't map address 0 */
+ start = obj->da_start ? obj->da_start : alignment;
+
+ if (flags & IOVMF_LINEAR)
+ alignment = iopgsz_max(bytes);
+ start = roundup(start, alignment);
+ } else if (start < obj->da_start || start > obj->da_end ||
+ obj->da_end - start < bytes) {
+ return ERR_PTR(-EINVAL);
+ }
+
+ tmp = NULL;
+ if (list_empty(&obj->mmap))
+ goto found;
+
+ prev_end = 0;
+ list_for_each_entry(tmp, &obj->mmap, list) {
+
+ if (prev_end > start)
+ break;
+
+ if (tmp->da_start > start && (tmp->da_start - start) >= bytes)
+ goto found;
+
+ if (tmp->da_end >= start && ~flags & IOVMF_DA_FIXED)
+ start = roundup(tmp->da_end + 1, alignment);
+
+ prev_end = tmp->da_end;
+ }
+
+ if ((start >= prev_end) && (obj->da_end - start >= bytes))
+ goto found;
+
+ dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n",
+ __func__, da, bytes, flags);
+
+ return ERR_PTR(-EINVAL);
+
+found:
+ new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ new->iommu = obj;
+ new->da_start = start;
+ new->da_end = start + bytes;
+ new->flags = flags;
+
+ /*
+ * keep ascending order of iovmas
+ */
+ if (tmp)
+ list_add_tail(&new->list, &tmp->list);
+ else
+ list_add(&new->list, &obj->mmap);
+
+ dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n",
+ __func__, new->da_start, start, new->da_end, bytes, flags);
+
+ return new;
+}
+
+static void free_iovm_area(struct omap_iommu *obj, struct iovm_struct *area)
+{
+ size_t bytes;
+
+ BUG_ON(!obj || !area);
+
+ bytes = area->da_end - area->da_start;
+
+ dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n",
+ __func__, area->da_start, area->da_end, bytes, area->flags);
+
+ list_del(&area->list);
+ kmem_cache_free(iovm_area_cachep, area);
+}
+
+/**
+ * omap_da_to_va - convert (d) to (v)
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ * @va: mpu virtual address
+ *
+ * Returns mpu virtual addr which corresponds to a given device virtual addr
+ */
+void *omap_da_to_va(struct omap_iommu *obj, u32 da)
+{
+ void *va = NULL;
+ struct iovm_struct *area;
+
+ mutex_lock(&obj->mmap_lock);
+
+ area = __find_iovm_area(obj, da);
+ if (!area) {
+ dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
+ goto out;
+ }
+ va = area->va;
+out:
+ mutex_unlock(&obj->mmap_lock);
+
+ return va;
+}
+EXPORT_SYMBOL_GPL(omap_da_to_va);
+
+static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va)
+{
+ unsigned int i;
+ struct scatterlist *sg;
+ void *va = _va;
+ void *va_end;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ struct page *pg;
+ const size_t bytes = PAGE_SIZE;
+
+ /*
+ * iommu 'superpage' isn't supported with 'omap_iommu_vmalloc()'
+ */
+ pg = vmalloc_to_page(va);
+ BUG_ON(!pg);
+ sg_set_page(sg, pg, bytes, 0);
+
+ va += bytes;
+ }
+
+ va_end = _va + PAGE_SIZE * i;
+}
+
+static inline void sgtable_drain_vmalloc(struct sg_table *sgt)
+{
+ /*
+ * Actually this is not necessary at all, just exists for
+ * consistency of the code readability.
+ */
+ BUG_ON(!sgt);
+}
+
+/* create 'da' <-> 'pa' mapping from 'sgt' */
+static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
+ const struct sg_table *sgt, u32 flags)
+{
+ int err;
+ unsigned int i, j;
+ struct scatterlist *sg;
+ u32 da = new->da_start;
+ int order;
+
+ if (!domain || !sgt)
+ return -EINVAL;
+
+ BUG_ON(!sgtable_ok(sgt));
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ u32 pa;
+ size_t bytes;
+
+ pa = sg_phys(sg) - sg->offset;
+ bytes = sg->length + sg->offset;
+
+ flags &= ~IOVMF_PGSZ_MASK;
+
+ if (bytes_to_iopgsz(bytes) < 0)
+ goto err_out;
+
+ order = get_order(bytes);
+
+ pr_debug("%s: [%d] %08x %08x(%x)\n", __func__,
+ i, da, pa, bytes);
+
+ err = iommu_map(domain, da, pa, order, flags);
+ if (err)
+ goto err_out;
+
+ da += bytes;
+ }
+ return 0;
+
+err_out:
+ da = new->da_start;
+
+ for_each_sg(sgt->sgl, sg, i, j) {
+ size_t bytes;
+
+ bytes = sg->length + sg->offset;
+ order = get_order(bytes);
+
+ /* ignore failures.. we're already handling one */
+ iommu_unmap(domain, da, order);
+
+ da += bytes;
+ }
+ return err;
+}
+
+/* release 'da' <-> 'pa' mapping */
+static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj,
+ struct iovm_struct *area)
+{
+ u32 start;
+ size_t total = area->da_end - area->da_start;
+ const struct sg_table *sgt = area->sgt;
+ struct scatterlist *sg;
+ int i, err;
+
+ BUG_ON(!sgtable_ok(sgt));
+ BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE));
+
+ start = area->da_start;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes;
+ int order;
+
+ bytes = sg->length + sg->offset;
+ order = get_order(bytes);
+
+ err = iommu_unmap(domain, start, order);
+ if (err < 0)
+ break;
+
+ dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n",
+ __func__, start, bytes, area->flags);
+
+ BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
+
+ total -= bytes;
+ start += bytes;
+ }
+ BUG_ON(total);
+}
+
+/* template function for all unmapping */
+static struct sg_table *unmap_vm_area(struct iommu_domain *domain,
+ struct omap_iommu *obj, const u32 da,
+ void (*fn)(const void *), u32 flags)
+{
+ struct sg_table *sgt = NULL;
+ struct iovm_struct *area;
+
+ if (!IS_ALIGNED(da, PAGE_SIZE)) {
+ dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da);
+ return NULL;
+ }
+
+ mutex_lock(&obj->mmap_lock);
+
+ area = __find_iovm_area(obj, da);
+ if (!area) {
+ dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
+ goto out;
+ }
+
+ if ((area->flags & flags) != flags) {
+ dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__,
+ area->flags);
+ goto out;
+ }
+ sgt = (struct sg_table *)area->sgt;
+
+ unmap_iovm_area(domain, obj, area);
+
+ fn(area->va);
+
+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__,
+ area->da_start, da, area->da_end,
+ area->da_end - area->da_start, area->flags);
+
+ free_iovm_area(obj, area);
+out:
+ mutex_unlock(&obj->mmap_lock);
+
+ return sgt;
+}
+
+static u32 map_iommu_region(struct iommu_domain *domain, struct omap_iommu *obj,
+ u32 da, const struct sg_table *sgt, void *va,
+ size_t bytes, u32 flags)
+{
+ int err = -ENOMEM;
+ struct iovm_struct *new;
+
+ mutex_lock(&obj->mmap_lock);
+
+ new = alloc_iovm_area(obj, da, bytes, flags);
+ if (IS_ERR(new)) {
+ err = PTR_ERR(new);
+ goto err_alloc_iovma;
+ }
+ new->va = va;
+ new->sgt = sgt;
+
+ if (map_iovm_area(domain, new, sgt, new->flags))
+ goto err_map;
+
+ mutex_unlock(&obj->mmap_lock);
+
+ dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n",
+ __func__, new->da_start, bytes, new->flags, va);
+
+ return new->da_start;
+
+err_map:
+ free_iovm_area(obj, new);
+err_alloc_iovma:
+ mutex_unlock(&obj->mmap_lock);
+ return err;
+}
+
+static inline u32
+__iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj,
+ u32 da, const struct sg_table *sgt,
+ void *va, size_t bytes, u32 flags)
+{
+ return map_iommu_region(domain, obj, da, sgt, va, bytes, flags);
+}
+
+/**
+ * omap_iommu_vmap - (d)-(p)-(v) address mapper
+ * @obj: objective iommu
+ * @sgt: address of scatter gather table
+ * @flags: iovma and page property
+ *
+ * Creates 1-n-1 mapping with given @sgt and returns @da.
+ * All @sgt element must be io page size aligned.
+ */
+u32 omap_iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
+ const struct sg_table *sgt, u32 flags)
+{
+ size_t bytes;
+ void *va = NULL;
+
+ if (!obj || !obj->dev || !sgt)
+ return -EINVAL;
+
+ bytes = sgtable_len(sgt);
+ if (!bytes)
+ return -EINVAL;
+ bytes = PAGE_ALIGN(bytes);
+
+ if (flags & IOVMF_MMIO) {
+ va = vmap_sg(sgt);
+ if (IS_ERR(va))
+ return PTR_ERR(va);
+ }
+
+ flags |= IOVMF_DISCONT;
+ flags |= IOVMF_MMIO;
+
+ da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags);
+ if (IS_ERR_VALUE(da))
+ vunmap_sg(va);
+
+ return da + sgtable_offset(sgt);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vmap);
+
+/**
+ * omap_iommu_vunmap - release virtual mapping obtained by 'omap_iommu_vmap()'
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ *
+ * Free the iommu virtually contiguous memory area starting at
+ * @da, which was returned by 'omap_iommu_vmap()'.
+ */
+struct sg_table *
+omap_iommu_vunmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da)
+{
+ struct sg_table *sgt;
+ /*
+ * 'sgt' is allocated before 'omap_iommu_vmalloc()' is called.
+ * Just returns 'sgt' to the caller to free
+ */
+ da &= PAGE_MASK;
+ sgt = unmap_vm_area(domain, obj, da, vunmap_sg,
+ IOVMF_DISCONT | IOVMF_MMIO);
+ if (!sgt)
+ dev_dbg(obj->dev, "%s: No sgt\n", __func__);
+ return sgt;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vunmap);
+
+/**
+ * omap_iommu_vmalloc - (d)-(p)-(v) address allocator and mapper
+ * @obj: objective iommu
+ * @da: contiguous iommu virtual memory
+ * @bytes: allocation size
+ * @flags: iovma and page property
+ *
+ * Allocate @bytes linearly and creates 1-n-1 mapping and returns
+ * @da again, which might be adjusted if 'IOVMF_DA_FIXED' is not set.
+ */
+u32
+omap_iommu_vmalloc(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
+ size_t bytes, u32 flags)
+{
+ void *va;
+ struct sg_table *sgt;
+
+ if (!obj || !obj->dev || !bytes)
+ return -EINVAL;
+
+ bytes = PAGE_ALIGN(bytes);
+
+ va = vmalloc(bytes);
+ if (!va)
+ return -ENOMEM;
+
+ flags |= IOVMF_DISCONT;
+ flags |= IOVMF_ALLOC;
+
+ sgt = sgtable_alloc(bytes, flags, da, 0);
+ if (IS_ERR(sgt)) {
+ da = PTR_ERR(sgt);
+ goto err_sgt_alloc;
+ }
+ sgtable_fill_vmalloc(sgt, va);
+
+ da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags);
+ if (IS_ERR_VALUE(da))
+ goto err_iommu_vmap;
+
+ return da;
+
+err_iommu_vmap:
+ sgtable_drain_vmalloc(sgt);
+ sgtable_free(sgt);
+err_sgt_alloc:
+ vfree(va);
+ return da;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vmalloc);
+
+/**
+ * omap_iommu_vfree - release memory allocated by 'omap_iommu_vmalloc()'
+ * @obj: objective iommu
+ * @da: iommu device virtual address
+ *
+ * Frees the iommu virtually continuous memory area starting at
+ * @da, as obtained from 'omap_iommu_vmalloc()'.
+ */
+void omap_iommu_vfree(struct iommu_domain *domain, struct omap_iommu *obj,
+ const u32 da)
+{
+ struct sg_table *sgt;
+
+ sgt = unmap_vm_area(domain, obj, da, vfree,
+ IOVMF_DISCONT | IOVMF_ALLOC);
+ if (!sgt)
+ dev_dbg(obj->dev, "%s: No sgt\n", __func__);
+ sgtable_free(sgt);
+}
+EXPORT_SYMBOL_GPL(omap_iommu_vfree);
+
+static int __init iovmm_init(void)
+{
+ const unsigned long flags = SLAB_HWCACHE_ALIGN;
+ struct kmem_cache *p;
+
+ p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0,
+ flags, NULL);
+ if (!p)
+ return -ENOMEM;
+ iovm_area_cachep = p;
+
+ return 0;
+}
+module_init(iovmm_init);
+
+static void __exit iovmm_exit(void)
+{
+ kmem_cache_destroy(iovm_area_cachep);
+}
+module_exit(iovmm_exit);
+
+MODULE_DESCRIPTION("omap iommu: simple virtual address space management");
+MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig
index 4fb601670de3..a233ed53913a 100644
--- a/drivers/isdn/Kconfig
+++ b/drivers/isdn/Kconfig
@@ -5,7 +5,7 @@
menuconfig ISDN
bool "ISDN support"
depends on NET
- depends on !S390
+ depends on !S390 && !UML
---help---
ISDN ("Integrated Services Digital Network", called RNIS in France)
is a fully digital telephone service that can be used for voice and
diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c
index 37e685eafd24..c4897e1075d8 100644
--- a/drivers/isdn/hisax/callc.c
+++ b/drivers/isdn/hisax/callc.c
@@ -65,7 +65,7 @@ hisax_findcard(int driverid)
return (struct IsdnCardState *) 0;
}
-static __attribute__((format(printf, 3, 4))) void
+static __printf(3, 4) void
link_debug(struct Channel *chanp, int direction, char *fmt, ...)
{
va_list args;
@@ -1068,7 +1068,7 @@ init_d_st(struct Channel *chanp)
return 0;
}
-static __attribute__((format(printf, 2, 3))) void
+static __printf(2, 3) void
callc_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index 0a5c42a3f125..aff45a11a92d 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -1287,9 +1287,9 @@ int jiftime(char *s, long mark);
int HiSax_command(isdn_ctrl * ic);
int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
-__attribute__((format(printf, 3, 4)))
+__printf(3, 4)
void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
-__attribute__((format(printf, 3, 0)))
+__printf(3, 0)
void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
void HiSax_reportcard(int cardnr, int sel);
int QuickHex(char *txt, u_char * p, int cnt);
diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h
index 425d86116f2b..66ddcab19bba 100644
--- a/drivers/isdn/hisax/isdnl1.h
+++ b/drivers/isdn/hisax/isdnl1.h
@@ -21,7 +21,7 @@
#define B_XMTBUFREADY 1
#define B_ACKPENDING 2
-__attribute__((format(printf, 2, 3)))
+__printf(2, 3)
void debugl1(struct IsdnCardState *cs, char *fmt, ...);
void DChannel_proc_xmt(struct IsdnCardState *cs);
void DChannel_proc_rcv(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c
index ad291f21b201..1c24e4457b6f 100644
--- a/drivers/isdn/hisax/isdnl3.c
+++ b/drivers/isdn/hisax/isdnl3.c
@@ -66,7 +66,7 @@ static char *strL3Event[] =
"EV_TIMEOUT",
};
-static __attribute__((format(printf, 2, 3))) void
+static __printf(2, 3) void
l3m_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
index b0d9ab1f21c0..6a8acf65777d 100644
--- a/drivers/isdn/hisax/l3dss1.c
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -353,7 +353,7 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
{ l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
return;
}
-#ifdef HISAX_DE_AOC
+#ifdef CONFIG_DE_AOC
{
#define FOO1(s,a,b) \
@@ -422,9 +422,9 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
#undef FOO1
}
-#else /* not HISAX_DE_AOC */
+#else /* not CONFIG_DE_AOC */
l3_debug(st, "invoke break");
-#endif /* not HISAX_DE_AOC */
+#endif /* not CONFIG_DE_AOC */
break;
case 2: /* return result */
/* if no process available handle separately */
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
index 44082637a09f..db247b79e561 100644
--- a/drivers/isdn/hisax/st5481_d.c
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -167,7 +167,7 @@ static struct FsmNode L1FnList[] __initdata =
{ST_L1_F8, EV_IND_RSY, l1_ignore},
};
-static __attribute__((format(printf, 2, 3)))
+static __printf(2, 3)
void l1m_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
@@ -270,7 +270,7 @@ static char *strDoutEvent[] =
"EV_DOUT_UNDERRUN",
};
-static __attribute__((format(printf, 2, 3)))
+static __printf(2, 3)
void dout_debug(struct FsmInst *fi, char *fmt, ...)
{
va_list args;
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index dc7caaddecf4..ff203a421863 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -375,6 +375,18 @@ config LEDS_ASIC3
cannot be used. This driver supports hardware blinking with an on+off
period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700.
+config LEDS_RENESAS_TPU
+ bool "LED support for Renesas TPU"
+ depends on LEDS_CLASS && HAVE_CLK && GENERIC_GPIO
+ help
+ This option enables build of the LED TPU platform driver,
+ suitable to drive any TPU channel on newer Renesas SoCs.
+ The driver controls the GPIO pin connected to the LED via
+ the GPIO framework and expects the LED to be connected to
+ a pin that can be driven in both GPIO mode and using TPU
+ pin function. The latter to support brightness control.
+ Brightness control is supported but hardware blinking is not.
+
config LEDS_TRIGGERS
bool "LED Trigger support"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a0a1b89d78a8..e4f6bf568880 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
+obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index dc3d3d83191a..661b692573e7 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -267,9 +267,14 @@ void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
+ del_timer_sync(&led_cdev->blink_timer);
+
if (led_cdev->blink_set &&
- !led_cdev->blink_set(led_cdev, delay_on, delay_off))
+ !led_cdev->blink_set(led_cdev, delay_on, delay_off)) {
+ led_cdev->blink_delay_on = *delay_on;
+ led_cdev->blink_delay_off = *delay_off;
return;
+ }
/* blink with 1 Hz as default if nothing specified */
if (!*delay_on && !*delay_off)
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 4bebae733349..6f1ff93d7cec 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -261,9 +261,12 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp)
if (trigger) {
trigger->name = name;
err = led_trigger_register(trigger);
- if (err < 0)
+ if (err < 0) {
+ kfree(trigger);
+ trigger = NULL;
printk(KERN_WARNING "LED trigger %s failed to register"
" (%d)\n", name, err);
+ }
} else
printk(KERN_WARNING "LED trigger %s failed to register"
" (no memory)\n", name);
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index 22f847c890c9..fbd5d88ccd8f 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -107,9 +107,10 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
}
led->cdev->name = led->name;
- led->cdev->default_trigger = led->default_trigger;
+ led->cdev->flags = LED_CORE_SUSPENDRESUME;
led->cdev->brightness_set = brightness_set;
led->cdev->blink_set = blink_set;
+ led->cdev->default_trigger = led->default_trigger;
ret = led_classdev_register(&pdev->dev, led->cdev);
if (ret < 0)
@@ -136,12 +137,44 @@ static int __devexit asic3_led_remove(struct platform_device *pdev)
return mfd_cell_disable(pdev);
}
+static int asic3_led_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ int ret;
+
+ ret = 0;
+ if (cell->suspend)
+ ret = (*cell->suspend)(pdev);
+
+ return ret;
+}
+
+static int asic3_led_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ int ret;
+
+ ret = 0;
+ if (cell->resume)
+ ret = (*cell->resume)(pdev);
+
+ return ret;
+}
+
+static const struct dev_pm_ops asic3_led_pm_ops = {
+ .suspend = asic3_led_suspend,
+ .resume = asic3_led_resume,
+};
+
static struct platform_driver asic3_led_driver = {
.probe = asic3_led_probe,
.remove = __devexit_p(asic3_led_remove),
.driver = {
.name = "leds-asic3",
.owner = THIS_MODULE,
+ .pm = &asic3_led_pm_ops,
},
};
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 3d8bc327a68d..504cc26c7e4b 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -121,7 +121,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
}
led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
- state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
+ state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
else
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index 3dd7090a9a9b..4dc510fdfa06 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -421,7 +421,6 @@ err_class_register:
err_reg_init:
regulator_put(drvdata->regulator);
err_regulator_get:
- i2c_set_clientdata(client, NULL);
kfree(drvdata);
err_out:
return err;
@@ -449,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id);
static struct i2c_driver lm3530_i2c_driver = {
.probe = lm3530_probe,
- .remove = lm3530_remove,
+ .remove = __devexit_p(lm3530_remove),
.id_table = lm3530_id,
.driver = {
.name = LM3530_NAME,
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 9fc122c81f06..cb641f1b3342 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -97,6 +97,9 @@
/* Status */
#define LP5521_EXT_CLK_USED 0x08
+/* default R channel current register value */
+#define LP5521_REG_R_CURR_DEFAULT 0xAF
+
struct lp5521_engine {
int id;
u8 mode;
@@ -175,14 +178,14 @@ static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode)
mode = LP5521_CMD_DIRECT;
ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state);
+ if (ret < 0)
+ return ret;
/* set mode only for this engine */
engine_state &= ~(engine->engine_mask);
mode &= engine->engine_mask;
engine_state |= mode;
- ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state);
-
- return ret;
+ return lp5521_write(client, LP5521_REG_OP_MODE, engine_state);
}
static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern)
@@ -643,6 +646,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
struct lp5521_chip *chip;
struct lp5521_platform_data *pdata;
int ret, i, led;
+ u8 buf;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -681,6 +685,20 @@ static int __devinit lp5521_probe(struct i2c_client *client,
* Exact value is not available. 10 - 20ms
* appears to be enough for reset.
*/
+
+ /*
+ * Make sure that the chip is reset by reading back the r channel
+ * current reg. This is dummy read is required on some platforms -
+ * otherwise further access to the R G B channels in the
+ * LP5521_REG_ENABLE register will not have any effect - strange!
+ */
+ lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
+ if (buf != LP5521_REG_R_CURR_DEFAULT) {
+ dev_err(&client->dev, "error in reseting chip\n");
+ goto fail2;
+ }
+ usleep_range(10000, 20000);
+
ret = lp5521_detect(client);
if (ret) {
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index f369e56d6547..b3393a9f2139 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -21,13 +21,13 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
-#include <linux/mfd/mc13783.h>
+#include <linux/mfd/mc13xxx.h>
#include <linux/slab.h>
struct mc13783_led {
struct led_classdev cdev;
struct work_struct work;
- struct mc13783 *master;
+ struct mc13xxx *master;
enum led_brightness new_brightness;
int id;
};
@@ -111,11 +111,11 @@ static void mc13783_led_work(struct work_struct *work)
break;
}
- mc13783_lock(led->master);
+ mc13xxx_lock(led->master);
- mc13783_reg_rmw(led->master, reg, mask, value);
+ mc13xxx_reg_rmw(led->master, reg, mask, value);
- mc13783_unlock(led->master);
+ mc13xxx_unlock(led->master);
}
static void mc13783_led_set(struct led_classdev *led_cdev,
@@ -172,23 +172,23 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current)
break;
}
- mc13783_lock(led->master);
+ mc13xxx_lock(led->master);
- ret = mc13783_reg_rmw(led->master, reg, mask << shift,
+ ret = mc13xxx_reg_rmw(led->master, reg, mask << shift,
value << shift);
- mc13783_unlock(led->master);
+ mc13xxx_unlock(led->master);
return ret;
}
static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
int ret = 0;
int reg = 0;
- mc13783_lock(dev);
+ mc13xxx_lock(dev);
if (pdata->flags & MC13783_LED_TC1HALF)
reg |= MC13783_LED_C1_TC1HALF_BIT;
@@ -196,7 +196,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_SLEWLIMTC)
reg |= MC13783_LED_Cx_SLEWLIM_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
if (ret)
goto out;
@@ -206,7 +206,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_SLEWLIMBL)
reg |= MC13783_LED_Cx_SLEWLIM_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
if (ret)
goto out;
@@ -216,7 +216,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC1)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
if (ret)
goto out;
@@ -226,7 +226,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC2)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
if (ret)
goto out;
@@ -236,7 +236,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
if (pdata->flags & MC13783_LED_TRIODE_TC3)
reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
if (ret)
goto out;
@@ -255,17 +255,17 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) <<
MC13783_LED_C0_ABREF;
- ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
+ ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
out:
- mc13783_unlock(dev);
+ mc13xxx_unlock(dev);
return ret;
}
static int __devinit mc13783_led_probe(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13783_led_platform_data *led_cur;
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx_led_platform_data *led_cur;
struct mc13783_led *led, *led_dat;
int ret, i;
int init_led = 0;
@@ -351,9 +351,9 @@ err_free:
static int __devexit mc13783_led_remove(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mc13783_led *led = platform_get_drvdata(pdev);
- struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
int i;
for (i = 0; i < pdata->num_leds; i++) {
@@ -361,16 +361,16 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev)
cancel_work_sync(&led[i].work);
}
- mc13783_lock(dev);
+ mc13xxx_lock(dev);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
- mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
+ mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
- mc13783_unlock(dev);
+ mc13xxx_unlock(dev);
kfree(led);
return 0;
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
new file mode 100644
index 000000000000..3ee540eb127e
--- /dev/null
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -0,0 +1,357 @@
+/*
+ * LED control using Renesas TPU
+ *
+ * Copyright (C) 2011 Magnus Damm
+ *
+ * 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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/printk.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/leds.h>
+#include <linux/platform_data/leds-renesas-tpu.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+
+enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN };
+enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON };
+
+struct r_tpu_priv {
+ struct led_classdev ldev;
+ void __iomem *mapbase;
+ struct clk *clk;
+ struct platform_device *pdev;
+ enum r_tpu_pin pin_state;
+ enum r_tpu_timer timer_state;
+ unsigned long min_rate;
+ unsigned int refresh_rate;
+ struct work_struct work;
+ enum led_brightness new_brightness;
+};
+
+static DEFINE_SPINLOCK(r_tpu_lock);
+
+#define TSTR -1 /* Timer start register (shared register) */
+#define TCR 0 /* Timer control register (+0x00) */
+#define TMDR 1 /* Timer mode register (+0x04) */
+#define TIOR 2 /* Timer I/O control register (+0x08) */
+#define TIER 3 /* Timer interrupt enable register (+0x0c) */
+#define TSR 4 /* Timer status register (+0x10) */
+#define TCNT 5 /* Timer counter (+0x14) */
+#define TGRA 6 /* Timer general register A (+0x18) */
+#define TGRB 7 /* Timer general register B (+0x1c) */
+#define TGRC 8 /* Timer general register C (+0x20) */
+#define TGRD 9 /* Timer general register D (+0x24) */
+
+static inline unsigned short r_tpu_read(struct r_tpu_priv *p, int reg_nr)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs = reg_nr << 2;
+
+ if (reg_nr == TSTR)
+ return ioread16(base - cfg->channel_offset);
+
+ return ioread16(base + offs);
+}
+
+static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr,
+ unsigned short value)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs = reg_nr << 2;
+
+ if (reg_nr == TSTR) {
+ iowrite16(value, base - cfg->channel_offset);
+ return;
+ }
+
+ iowrite16(value, base + offs);
+}
+
+static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ unsigned long flags, value;
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&r_tpu_lock, flags);
+ value = r_tpu_read(p, TSTR);
+
+ if (start)
+ value |= 1 << cfg->timer_bit;
+ else
+ value &= ~(1 << cfg->timer_bit);
+
+ r_tpu_write(p, TSTR, value);
+ spin_unlock_irqrestore(&r_tpu_lock, flags);
+}
+
+static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+ int prescaler[] = { 1, 4, 16, 64 };
+ int k, ret;
+ unsigned long rate, tmp;
+
+ if (p->timer_state == R_TPU_TIMER_ON)
+ return 0;
+
+ /* wake up device and enable clock */
+ pm_runtime_get_sync(&p->pdev->dev);
+ ret = clk_enable(p->clk);
+ if (ret) {
+ dev_err(&p->pdev->dev, "cannot enable clock\n");
+ return ret;
+ }
+
+ /* make sure channel is disabled */
+ r_tpu_start_stop_ch(p, 0);
+
+ /* get clock rate after enabling it */
+ rate = clk_get_rate(p->clk);
+
+ /* pick the lowest acceptable rate */
+ for (k = 0; k < ARRAY_SIZE(prescaler); k++)
+ if ((rate / prescaler[k]) < p->min_rate)
+ break;
+
+ if (!k) {
+ dev_err(&p->pdev->dev, "clock rate mismatch\n");
+ goto err0;
+ }
+ dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n",
+ rate, prescaler[k - 1]);
+
+ /* clear TCNT on TGRB match, count on rising edge, set prescaler */
+ r_tpu_write(p, TCR, 0x0040 | (k - 1));
+
+ /* output 0 until TGRA, output 1 until TGRB */
+ r_tpu_write(p, TIOR, 0x0002);
+
+ rate /= prescaler[k - 1] * p->refresh_rate;
+ r_tpu_write(p, TGRB, rate);
+ dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate);
+
+ tmp = (cfg->max_brightness - brightness) * rate;
+ r_tpu_write(p, TGRA, tmp / cfg->max_brightness);
+ dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness);
+
+ /* PWM mode */
+ r_tpu_write(p, TMDR, 0x0002);
+
+ /* enable channel */
+ r_tpu_start_stop_ch(p, 1);
+
+ p->timer_state = R_TPU_TIMER_ON;
+ return 0;
+ err0:
+ clk_disable(p->clk);
+ pm_runtime_put_sync(&p->pdev->dev);
+ return -ENOTSUPP;
+}
+
+static void r_tpu_disable(struct r_tpu_priv *p)
+{
+ if (p->timer_state == R_TPU_TIMER_UNUSED)
+ return;
+
+ /* disable channel */
+ r_tpu_start_stop_ch(p, 0);
+
+ /* stop clock and mark device as idle */
+ clk_disable(p->clk);
+ pm_runtime_put_sync(&p->pdev->dev);
+
+ p->timer_state = R_TPU_TIMER_UNUSED;
+}
+
+static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
+ enum led_brightness brightness)
+{
+ struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
+
+ if (p->pin_state == new_state) {
+ if (p->pin_state == R_TPU_PIN_GPIO)
+ gpio_set_value(cfg->pin_gpio, brightness);
+ return;
+ }
+
+ if (p->pin_state == R_TPU_PIN_GPIO)
+ gpio_free(cfg->pin_gpio);
+
+ if (p->pin_state == R_TPU_PIN_GPIO_FN)
+ gpio_free(cfg->pin_gpio_fn);
+
+ if (new_state == R_TPU_PIN_GPIO) {
+ gpio_request(cfg->pin_gpio, cfg->name);
+ gpio_direction_output(cfg->pin_gpio, !!brightness);
+ }
+ if (new_state == R_TPU_PIN_GPIO_FN)
+ gpio_request(cfg->pin_gpio_fn, cfg->name);
+
+ p->pin_state = new_state;
+}
+
+static void r_tpu_work(struct work_struct *work)
+{
+ struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work);
+ enum led_brightness brightness = p->new_brightness;
+
+ r_tpu_disable(p);
+
+ /* off and maximum are handled as GPIO pins, in between PWM */
+ if ((brightness == 0) || (brightness == p->ldev.max_brightness))
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness);
+ else {
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0);
+ r_tpu_enable(p, brightness);
+ }
+}
+
+static void r_tpu_set_brightness(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev);
+ p->new_brightness = brightness;
+ schedule_work(&p->work);
+}
+
+static int __devinit r_tpu_probe(struct platform_device *pdev)
+{
+ struct led_renesas_tpu_config *cfg = pdev->dev.platform_data;
+ struct r_tpu_priv *p;
+ struct resource *res;
+ int ret = -ENXIO;
+
+ if (!cfg) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ goto err0;
+ }
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ goto err1;
+ }
+
+ /* map memory, let mapbase point to our channel */
+ p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (p->mapbase == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ goto err1;
+ }
+
+ /* get hold of clock */
+ p->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(p->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err2;
+ }
+
+ p->pdev = pdev;
+ p->pin_state = R_TPU_PIN_UNUSED;
+ p->timer_state = R_TPU_TIMER_UNUSED;
+ p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100;
+ r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF);
+ platform_set_drvdata(pdev, p);
+
+ INIT_WORK(&p->work, r_tpu_work);
+
+ p->ldev.name = cfg->name;
+ p->ldev.brightness = LED_OFF;
+ p->ldev.max_brightness = cfg->max_brightness;
+ p->ldev.brightness_set = r_tpu_set_brightness;
+ p->ldev.flags |= LED_CORE_SUSPENDRESUME;
+ ret = led_classdev_register(&pdev->dev, &p->ldev);
+ if (ret < 0)
+ goto err3;
+
+ /* max_brightness may be updated by the LED core code */
+ p->min_rate = p->ldev.max_brightness * p->refresh_rate;
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+
+ err3:
+ r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
+ clk_put(p->clk);
+ err2:
+ iounmap(p->mapbase);
+ err1:
+ kfree(p);
+ err0:
+ return ret;
+}
+
+static int __devexit r_tpu_remove(struct platform_device *pdev)
+{
+ struct r_tpu_priv *p = platform_get_drvdata(pdev);
+
+ r_tpu_set_brightness(&p->ldev, LED_OFF);
+ led_classdev_unregister(&p->ldev);
+ cancel_work_sync(&p->work);
+ r_tpu_disable(p);
+ r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
+
+ pm_runtime_disable(&pdev->dev);
+ clk_put(p->clk);
+
+ iounmap(p->mapbase);
+ kfree(p);
+ return 0;
+}
+
+static struct platform_driver r_tpu_device_driver = {
+ .probe = r_tpu_probe,
+ .remove = __devexit_p(r_tpu_remove),
+ .driver = {
+ .name = "leds-renesas-tpu",
+ }
+};
+
+static int __init r_tpu_init(void)
+{
+ return platform_driver_register(&r_tpu_device_driver);
+}
+
+static void __exit r_tpu_exit(void)
+{
+ platform_driver_unregister(&r_tpu_device_driver);
+}
+
+module_init(r_tpu_init);
+module_exit(r_tpu_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas TPU LED Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c
index 2535933c49f8..b5fdcb78a75b 100644
--- a/drivers/lguest/core.c
+++ b/drivers/lguest/core.c
@@ -232,6 +232,13 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
}
}
+ /*
+ * All long-lived kernel loops need to check with this horrible
+ * thing called the freezer. If the Host is trying to suspend,
+ * it stops us.
+ */
+ try_to_freeze();
+
/* Check for signals */
if (signal_pending(current))
return -ERESTARTSYS;
@@ -246,13 +253,6 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
try_deliver_interrupt(cpu, irq, more);
/*
- * All long-lived kernel loops need to check with this horrible
- * thing called the freezer. If the Host is trying to suspend,
- * it stops us.
- */
- try_to_freeze();
-
- /*
* Just make absolutely sure the Guest is still alive. One of
* those hypercalls could have been fatal, for example.
*/
@@ -313,7 +313,7 @@ static int __init init(void)
int err;
/* Lguest can't run under Xen, VMI or itself. It does Tricky Stuff. */
- if (paravirt_enabled()) {
+ if (get_kernel_rpl() != 0) {
printk("lguest is afraid of being a guest\n");
return -EPERM;
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index f75a66e7d312..faa4741df6d3 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -208,6 +208,16 @@ config DM_DEBUG
If unsure, say N.
+config DM_BUFIO
+ tristate
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ ---help---
+ This interface allows you to do buffered I/O on a device and acts
+ as a cache, holding recently-read blocks in memory and performing
+ delayed writes.
+
+source "drivers/md/persistent-data/Kconfig"
+
config DM_CRYPT
tristate "Crypt target support"
depends on BLK_DEV_DM
@@ -233,6 +243,32 @@ config DM_SNAPSHOT
---help---
Allow volume managers to take writable snapshots of a device.
+config DM_THIN_PROVISIONING
+ tristate "Thin provisioning target (EXPERIMENTAL)"
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ select DM_PERSISTENT_DATA
+ ---help---
+ Provides thin provisioning and snapshots that share a data store.
+
+config DM_DEBUG_BLOCK_STACK_TRACING
+ boolean "Keep stack trace of thin provisioning block lock holders"
+ depends on STACKTRACE_SUPPORT && DM_THIN_PROVISIONING
+ select STACKTRACE
+ ---help---
+ Enable this for messages that may help debug problems with the
+ block manager locking used by thin provisioning.
+
+ 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/Makefile b/drivers/md/Makefile
index 448838b1f92a..046860c7a166 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -10,6 +10,7 @@ dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \
dm-mirror-y += dm-raid1.o
dm-log-userspace-y \
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
+dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
obj-$(CONFIG_BLK_DEV_MD) += md-mod.o
obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
+obj-$(CONFIG_DM_BUFIO) += dm-bufio.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
@@ -34,10 +36,12 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o
obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
+obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
obj-$(CONFIG_DM_RAID) += dm-raid.o
+obj-$(CONFIG_DM_THIN_PROVISIONING) += dm-thin-pool.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
new file mode 100644
index 000000000000..cb246667dd52
--- /dev/null
+++ b/drivers/md/dm-bufio.c
@@ -0,0 +1,1699 @@
+/*
+ * Copyright (C) 2009-2011 Red Hat, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-bufio.h"
+
+#include <linux/device-mapper.h>
+#include <linux/dm-io.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/shrinker.h>
+
+#define DM_MSG_PREFIX "bufio"
+
+/*
+ * Memory management policy:
+ * Limit the number of buffers to DM_BUFIO_MEMORY_PERCENT of main memory
+ * or DM_BUFIO_VMALLOC_PERCENT of vmalloc memory (whichever is lower).
+ * Always allocate at least DM_BUFIO_MIN_BUFFERS buffers.
+ * Start background writeback when there are DM_BUFIO_WRITEBACK_PERCENT
+ * dirty buffers.
+ */
+#define DM_BUFIO_MIN_BUFFERS 8
+
+#define DM_BUFIO_MEMORY_PERCENT 2
+#define DM_BUFIO_VMALLOC_PERCENT 25
+#define DM_BUFIO_WRITEBACK_PERCENT 75
+
+/*
+ * Check buffer ages in this interval (seconds)
+ */
+#define DM_BUFIO_WORK_TIMER_SECS 10
+
+/*
+ * Free buffers when they are older than this (seconds)
+ */
+#define DM_BUFIO_DEFAULT_AGE_SECS 60
+
+/*
+ * The number of bvec entries that are embedded directly in the buffer.
+ * If the chunk size is larger, dm-io is used to do the io.
+ */
+#define DM_BUFIO_INLINE_VECS 16
+
+/*
+ * Buffer hash
+ */
+#define DM_BUFIO_HASH_BITS 20
+#define DM_BUFIO_HASH(block) \
+ ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \
+ ((1 << DM_BUFIO_HASH_BITS) - 1))
+
+/*
+ * Don't try to use kmem_cache_alloc for blocks larger than this.
+ * For explanation, see alloc_buffer_data below.
+ */
+#define DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT (PAGE_SIZE >> 1)
+#define DM_BUFIO_BLOCK_SIZE_GFP_LIMIT (PAGE_SIZE << (MAX_ORDER - 1))
+
+/*
+ * dm_buffer->list_mode
+ */
+#define LIST_CLEAN 0
+#define LIST_DIRTY 1
+#define LIST_SIZE 2
+
+/*
+ * Linking of buffers:
+ * All buffers are linked to cache_hash with their hash_list field.
+ *
+ * Clean buffers that are not being written (B_WRITING not set)
+ * are linked to lru[LIST_CLEAN] with their lru_list field.
+ *
+ * Dirty and clean buffers that are being written are linked to
+ * lru[LIST_DIRTY] with their lru_list field. When the write
+ * finishes, the buffer cannot be relinked immediately (because we
+ * are in an interrupt context and relinking requires process
+ * context), so some clean-not-writing buffers can be held on
+ * dirty_lru too. They are later added to lru in the process
+ * context.
+ */
+struct dm_bufio_client {
+ struct mutex lock;
+
+ struct list_head lru[LIST_SIZE];
+ unsigned long n_buffers[LIST_SIZE];
+
+ struct block_device *bdev;
+ unsigned block_size;
+ unsigned char sectors_per_block_bits;
+ unsigned char pages_per_block_bits;
+ unsigned char blocks_per_page_bits;
+ unsigned aux_size;
+ void (*alloc_callback)(struct dm_buffer *);
+ void (*write_callback)(struct dm_buffer *);
+
+ struct dm_io_client *dm_io;
+
+ struct list_head reserved_buffers;
+ unsigned need_reserved_buffers;
+
+ struct hlist_head *cache_hash;
+ wait_queue_head_t free_buffer_wait;
+
+ int async_write_error;
+
+ struct list_head client_list;
+ struct shrinker shrinker;
+};
+
+/*
+ * Buffer state bits.
+ */
+#define B_READING 0
+#define B_WRITING 1
+#define B_DIRTY 2
+
+/*
+ * Describes how the block was allocated:
+ * kmem_cache_alloc(), __get_free_pages() or vmalloc().
+ * See the comment at alloc_buffer_data.
+ */
+enum data_mode {
+ DATA_MODE_SLAB = 0,
+ DATA_MODE_GET_FREE_PAGES = 1,
+ DATA_MODE_VMALLOC = 2,
+ DATA_MODE_LIMIT = 3
+};
+
+struct dm_buffer {
+ struct hlist_node hash_list;
+ struct list_head lru_list;
+ sector_t block;
+ void *data;
+ enum data_mode data_mode;
+ unsigned char list_mode; /* LIST_* */
+ unsigned hold_count;
+ int read_error;
+ int write_error;
+ unsigned long state;
+ unsigned long last_accessed;
+ struct dm_bufio_client *c;
+ struct bio bio;
+ struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
+};
+
+/*----------------------------------------------------------------*/
+
+static struct kmem_cache *dm_bufio_caches[PAGE_SHIFT - SECTOR_SHIFT];
+static char *dm_bufio_cache_names[PAGE_SHIFT - SECTOR_SHIFT];
+
+static inline int dm_bufio_cache_index(struct dm_bufio_client *c)
+{
+ unsigned ret = c->blocks_per_page_bits - 1;
+
+ BUG_ON(ret >= ARRAY_SIZE(dm_bufio_caches));
+
+ return ret;
+}
+
+#define DM_BUFIO_CACHE(c) (dm_bufio_caches[dm_bufio_cache_index(c)])
+#define DM_BUFIO_CACHE_NAME(c) (dm_bufio_cache_names[dm_bufio_cache_index(c)])
+
+#define dm_bufio_in_request() (!!current->bio_list)
+
+static void dm_bufio_lock(struct dm_bufio_client *c)
+{
+ mutex_lock_nested(&c->lock, dm_bufio_in_request());
+}
+
+static int dm_bufio_trylock(struct dm_bufio_client *c)
+{
+ return mutex_trylock(&c->lock);
+}
+
+static void dm_bufio_unlock(struct dm_bufio_client *c)
+{
+ mutex_unlock(&c->lock);
+}
+
+/*
+ * FIXME Move to sched.h?
+ */
+#ifdef CONFIG_PREEMPT_VOLUNTARY
+# define dm_bufio_cond_resched() \
+do { \
+ if (unlikely(need_resched())) \
+ _cond_resched(); \
+} while (0)
+#else
+# define dm_bufio_cond_resched() do { } while (0)
+#endif
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Default cache size: available memory divided by the ratio.
+ */
+static unsigned long dm_bufio_default_cache_size;
+
+/*
+ * Total cache size set by the user.
+ */
+static unsigned long dm_bufio_cache_size;
+
+/*
+ * A copy of dm_bufio_cache_size because dm_bufio_cache_size can change
+ * at any time. If it disagrees, the user has changed cache size.
+ */
+static unsigned long dm_bufio_cache_size_latch;
+
+static DEFINE_SPINLOCK(param_spinlock);
+
+/*
+ * Buffers are freed after this timeout
+ */
+static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
+
+static unsigned long dm_bufio_peak_allocated;
+static unsigned long dm_bufio_allocated_kmem_cache;
+static unsigned long dm_bufio_allocated_get_free_pages;
+static unsigned long dm_bufio_allocated_vmalloc;
+static unsigned long dm_bufio_current_allocated;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Per-client cache: dm_bufio_cache_size / dm_bufio_client_count
+ */
+static unsigned long dm_bufio_cache_size_per_client;
+
+/*
+ * The current number of clients.
+ */
+static int dm_bufio_client_count;
+
+/*
+ * The list of all clients.
+ */
+static LIST_HEAD(dm_bufio_all_clients);
+
+/*
+ * This mutex protects dm_bufio_cache_size_latch,
+ * dm_bufio_cache_size_per_client and dm_bufio_client_count
+ */
+static DEFINE_MUTEX(dm_bufio_clients_lock);
+
+/*----------------------------------------------------------------*/
+
+static void adjust_total_allocated(enum data_mode data_mode, long diff)
+{
+ static unsigned long * const class_ptr[DATA_MODE_LIMIT] = {
+ &dm_bufio_allocated_kmem_cache,
+ &dm_bufio_allocated_get_free_pages,
+ &dm_bufio_allocated_vmalloc,
+ };
+
+ spin_lock(&param_spinlock);
+
+ *class_ptr[data_mode] += diff;
+
+ dm_bufio_current_allocated += diff;
+
+ if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
+ dm_bufio_peak_allocated = dm_bufio_current_allocated;
+
+ spin_unlock(&param_spinlock);
+}
+
+/*
+ * Change the number of clients and recalculate per-client limit.
+ */
+static void __cache_size_refresh(void)
+{
+ BUG_ON(!mutex_is_locked(&dm_bufio_clients_lock));
+ BUG_ON(dm_bufio_client_count < 0);
+
+ dm_bufio_cache_size_latch = dm_bufio_cache_size;
+
+ barrier();
+
+ /*
+ * Use default if set to 0 and report the actual cache size used.
+ */
+ if (!dm_bufio_cache_size_latch) {
+ (void)cmpxchg(&dm_bufio_cache_size, 0,
+ dm_bufio_default_cache_size);
+ dm_bufio_cache_size_latch = dm_bufio_default_cache_size;
+ }
+
+ dm_bufio_cache_size_per_client = dm_bufio_cache_size_latch /
+ (dm_bufio_client_count ? : 1);
+}
+
+/*
+ * Allocating buffer data.
+ *
+ * Small buffers are allocated with kmem_cache, to use space optimally.
+ *
+ * For large buffers, we choose between get_free_pages and vmalloc.
+ * Each has advantages and disadvantages.
+ *
+ * __get_free_pages can randomly fail if the memory is fragmented.
+ * __vmalloc won't randomly fail, but vmalloc space is limited (it may be
+ * as low as 128M) so using it for caching is not appropriate.
+ *
+ * If the allocation may fail we use __get_free_pages. Memory fragmentation
+ * won't have a fatal effect here, but it just causes flushes of some other
+ * buffers and more I/O will be performed. Don't use __get_free_pages if it
+ * always fails (i.e. order >= MAX_ORDER).
+ *
+ * If the allocation shouldn't fail we use __vmalloc. This is only for the
+ * initial reserve allocation, so there's no risk of wasting all vmalloc
+ * space.
+ */
+static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
+ enum data_mode *data_mode)
+{
+ if (c->block_size <= DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT) {
+ *data_mode = DATA_MODE_SLAB;
+ return kmem_cache_alloc(DM_BUFIO_CACHE(c), gfp_mask);
+ }
+
+ if (c->block_size <= DM_BUFIO_BLOCK_SIZE_GFP_LIMIT &&
+ gfp_mask & __GFP_NORETRY) {
+ *data_mode = DATA_MODE_GET_FREE_PAGES;
+ return (void *)__get_free_pages(gfp_mask,
+ c->pages_per_block_bits);
+ }
+
+ *data_mode = DATA_MODE_VMALLOC;
+ return __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+}
+
+/*
+ * Free buffer's data.
+ */
+static void free_buffer_data(struct dm_bufio_client *c,
+ void *data, enum data_mode data_mode)
+{
+ switch (data_mode) {
+ case DATA_MODE_SLAB:
+ kmem_cache_free(DM_BUFIO_CACHE(c), data);
+ break;
+
+ case DATA_MODE_GET_FREE_PAGES:
+ free_pages((unsigned long)data, c->pages_per_block_bits);
+ break;
+
+ case DATA_MODE_VMALLOC:
+ vfree(data);
+ break;
+
+ default:
+ DMCRIT("dm_bufio_free_buffer_data: bad data mode: %d",
+ data_mode);
+ BUG();
+ }
+}
+
+/*
+ * Allocate buffer and its data.
+ */
+static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
+{
+ struct dm_buffer *b = kmalloc(sizeof(struct dm_buffer) + c->aux_size,
+ gfp_mask);
+
+ if (!b)
+ return NULL;
+
+ b->c = c;
+
+ b->data = alloc_buffer_data(c, gfp_mask, &b->data_mode);
+ if (!b->data) {
+ kfree(b);
+ return NULL;
+ }
+
+ adjust_total_allocated(b->data_mode, (long)c->block_size);
+
+ return b;
+}
+
+/*
+ * Free buffer and its data.
+ */
+static void free_buffer(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ adjust_total_allocated(b->data_mode, -(long)c->block_size);
+
+ free_buffer_data(c, b->data, b->data_mode);
+ kfree(b);
+}
+
+/*
+ * Link buffer to the hash list and clean or dirty queue.
+ */
+static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty)
+{
+ struct dm_bufio_client *c = b->c;
+
+ c->n_buffers[dirty]++;
+ b->block = block;
+ b->list_mode = dirty;
+ list_add(&b->lru_list, &c->lru[dirty]);
+ hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]);
+ b->last_accessed = jiffies;
+}
+
+/*
+ * Unlink buffer from the hash list and dirty or clean queue.
+ */
+static void __unlink_buffer(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ BUG_ON(!c->n_buffers[b->list_mode]);
+
+ c->n_buffers[b->list_mode]--;
+ hlist_del(&b->hash_list);
+ list_del(&b->lru_list);
+}
+
+/*
+ * Place the buffer to the head of dirty or clean LRU queue.
+ */
+static void __relink_lru(struct dm_buffer *b, int dirty)
+{
+ struct dm_bufio_client *c = b->c;
+
+ BUG_ON(!c->n_buffers[b->list_mode]);
+
+ c->n_buffers[b->list_mode]--;
+ c->n_buffers[dirty]++;
+ b->list_mode = dirty;
+ list_del(&b->lru_list);
+ list_add(&b->lru_list, &c->lru[dirty]);
+}
+
+/*----------------------------------------------------------------
+ * Submit I/O on the buffer.
+ *
+ * Bio interface is faster but it has some problems:
+ * the vector list is limited (increasing this limit increases
+ * memory-consumption per buffer, so it is not viable);
+ *
+ * the memory must be direct-mapped, not vmalloced;
+ *
+ * the I/O driver can reject requests spuriously if it thinks that
+ * the requests are too big for the device or if they cross a
+ * controller-defined memory boundary.
+ *
+ * If the buffer is small enough (up to DM_BUFIO_INLINE_VECS pages) and
+ * it is not vmalloced, try using the bio interface.
+ *
+ * If the buffer is big, if it is vmalloced or if the underlying device
+ * rejects the bio because it is too large, use dm-io layer to do the I/O.
+ * The dm-io layer splits the I/O into multiple requests, avoiding the above
+ * shortcomings.
+ *--------------------------------------------------------------*/
+
+/*
+ * dm-io completion routine. It just calls b->bio.bi_end_io, pretending
+ * that the request was handled directly with bio interface.
+ */
+static void dmio_complete(unsigned long error, void *context)
+{
+ struct dm_buffer *b = context;
+
+ b->bio.bi_end_io(&b->bio, error ? -EIO : 0);
+}
+
+static void use_dmio(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ int r;
+ struct dm_io_request io_req = {
+ .bi_rw = rw,
+ .notify.fn = dmio_complete,
+ .notify.context = b,
+ .client = b->c->dm_io,
+ };
+ struct dm_io_region region = {
+ .bdev = b->c->bdev,
+ .sector = block << b->c->sectors_per_block_bits,
+ .count = b->c->block_size >> SECTOR_SHIFT,
+ };
+
+ if (b->data_mode != DATA_MODE_VMALLOC) {
+ io_req.mem.type = DM_IO_KMEM;
+ io_req.mem.ptr.addr = b->data;
+ } else {
+ io_req.mem.type = DM_IO_VMA;
+ io_req.mem.ptr.vma = b->data;
+ }
+
+ b->bio.bi_end_io = end_io;
+
+ r = dm_io(&io_req, 1, &region, NULL);
+ if (r)
+ end_io(&b->bio, r);
+}
+
+static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ char *ptr;
+ int len;
+
+ bio_init(&b->bio);
+ b->bio.bi_io_vec = b->bio_vec;
+ b->bio.bi_max_vecs = DM_BUFIO_INLINE_VECS;
+ b->bio.bi_sector = block << b->c->sectors_per_block_bits;
+ b->bio.bi_bdev = b->c->bdev;
+ b->bio.bi_end_io = end_io;
+
+ /*
+ * We assume that if len >= PAGE_SIZE ptr is page-aligned.
+ * If len < PAGE_SIZE the buffer doesn't cross page boundary.
+ */
+ ptr = b->data;
+ len = b->c->block_size;
+
+ if (len >= PAGE_SIZE)
+ BUG_ON((unsigned long)ptr & (PAGE_SIZE - 1));
+ else
+ BUG_ON((unsigned long)ptr & (len - 1));
+
+ do {
+ if (!bio_add_page(&b->bio, virt_to_page(ptr),
+ len < PAGE_SIZE ? len : PAGE_SIZE,
+ virt_to_phys(ptr) & (PAGE_SIZE - 1))) {
+ BUG_ON(b->c->block_size <= PAGE_SIZE);
+ use_dmio(b, rw, block, end_io);
+ return;
+ }
+
+ len -= PAGE_SIZE;
+ ptr += PAGE_SIZE;
+ } while (len > 0);
+
+ submit_bio(rw, &b->bio);
+}
+
+static void submit_io(struct dm_buffer *b, int rw, sector_t block,
+ bio_end_io_t *end_io)
+{
+ if (rw == WRITE && b->c->write_callback)
+ b->c->write_callback(b);
+
+ if (b->c->block_size <= DM_BUFIO_INLINE_VECS * PAGE_SIZE &&
+ b->data_mode != DATA_MODE_VMALLOC)
+ use_inline_bio(b, rw, block, end_io);
+ else
+ use_dmio(b, rw, block, end_io);
+}
+
+/*----------------------------------------------------------------
+ * Writing dirty buffers
+ *--------------------------------------------------------------*/
+
+/*
+ * The endio routine for write.
+ *
+ * Set the error, clear B_WRITING bit and wake anyone who was waiting on
+ * it.
+ */
+static void write_endio(struct bio *bio, int error)
+{
+ struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
+
+ b->write_error = error;
+ if (error) {
+ struct dm_bufio_client *c = b->c;
+ (void)cmpxchg(&c->async_write_error, 0, error);
+ }
+
+ BUG_ON(!test_bit(B_WRITING, &b->state));
+
+ smp_mb__before_clear_bit();
+ clear_bit(B_WRITING, &b->state);
+ smp_mb__after_clear_bit();
+
+ wake_up_bit(&b->state, B_WRITING);
+}
+
+/*
+ * This function is called when wait_on_bit is actually waiting.
+ */
+static int do_io_schedule(void *word)
+{
+ io_schedule();
+
+ return 0;
+}
+
+/*
+ * Initiate a write on a dirty buffer, but don't wait for it.
+ *
+ * - If the buffer is not dirty, exit.
+ * - If there some previous write going on, wait for it to finish (we can't
+ * have two writes on the same buffer simultaneously).
+ * - Submit our write and don't wait on it. We set B_WRITING indicating
+ * that there is a write in progress.
+ */
+static void __write_dirty_buffer(struct dm_buffer *b)
+{
+ if (!test_bit(B_DIRTY, &b->state))
+ return;
+
+ clear_bit(B_DIRTY, &b->state);
+ wait_on_bit_lock(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+
+ submit_io(b, WRITE, b->block, write_endio);
+}
+
+/*
+ * Wait until any activity on the buffer finishes. Possibly write the
+ * buffer if it is dirty. When this function finishes, there is no I/O
+ * running on the buffer and the buffer is not dirty.
+ */
+static void __make_buffer_clean(struct dm_buffer *b)
+{
+ BUG_ON(b->hold_count);
+
+ if (!b->state) /* fast case */
+ return;
+
+ wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+ __write_dirty_buffer(b);
+ wait_on_bit(&b->state, B_WRITING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+}
+
+/*
+ * Find some buffer that is not held by anybody, clean it, unlink it and
+ * return it.
+ */
+static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+
+ list_for_each_entry_reverse(b, &c->lru[LIST_CLEAN], lru_list) {
+ BUG_ON(test_bit(B_WRITING, &b->state));
+ BUG_ON(test_bit(B_DIRTY, &b->state));
+
+ if (!b->hold_count) {
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ return b;
+ }
+ dm_bufio_cond_resched();
+ }
+
+ list_for_each_entry_reverse(b, &c->lru[LIST_DIRTY], lru_list) {
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (!b->hold_count) {
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ return b;
+ }
+ dm_bufio_cond_resched();
+ }
+
+ return NULL;
+}
+
+/*
+ * Wait until some other threads free some buffer or release hold count on
+ * some buffer.
+ *
+ * This function is entered with c->lock held, drops it and regains it
+ * before exiting.
+ */
+static void __wait_for_free_buffer(struct dm_bufio_client *c)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&c->free_buffer_wait, &wait);
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+ dm_bufio_unlock(c);
+
+ io_schedule();
+
+ set_task_state(current, TASK_RUNNING);
+ remove_wait_queue(&c->free_buffer_wait, &wait);
+
+ dm_bufio_lock(c);
+}
+
+/*
+ * Allocate a new buffer. If the allocation is not possible, wait until
+ * some other thread frees a buffer.
+ *
+ * May drop the lock and regain it.
+ */
+static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+
+ /*
+ * dm-bufio is resistant to allocation failures (it just keeps
+ * one buffer reserved in cases all the allocations fail).
+ * So set flags to not try too hard:
+ * GFP_NOIO: don't recurse into the I/O layer
+ * __GFP_NORETRY: don't retry and rather return failure
+ * __GFP_NOMEMALLOC: don't use emergency reserves
+ * __GFP_NOWARN: don't print a warning in case of failure
+ *
+ * For debugging, if we set the cache size to 1, no new buffers will
+ * be allocated.
+ */
+ while (1) {
+ if (dm_bufio_cache_size_latch != 1) {
+ b = alloc_buffer(c, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
+ if (b)
+ return b;
+ }
+
+ if (!list_empty(&c->reserved_buffers)) {
+ b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ c->need_reserved_buffers++;
+
+ return b;
+ }
+
+ b = __get_unclaimed_buffer(c);
+ if (b)
+ return b;
+
+ __wait_for_free_buffer(c);
+ }
+}
+
+static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b = __alloc_buffer_wait_no_callback(c);
+
+ if (c->alloc_callback)
+ c->alloc_callback(b);
+
+ return b;
+}
+
+/*
+ * Free a buffer and wake other threads waiting for free buffers.
+ */
+static void __free_buffer_wake(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ if (!c->need_reserved_buffers)
+ free_buffer(b);
+ else {
+ list_add(&b->lru_list, &c->reserved_buffers);
+ c->need_reserved_buffers--;
+ }
+
+ wake_up(&c->free_buffer_wait);
+}
+
+static void __write_dirty_buffers_async(struct dm_bufio_client *c, int no_wait)
+{
+ struct dm_buffer *b, *tmp;
+
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_DIRTY], lru_list) {
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (!test_bit(B_DIRTY, &b->state) &&
+ !test_bit(B_WRITING, &b->state)) {
+ __relink_lru(b, LIST_CLEAN);
+ continue;
+ }
+
+ if (no_wait && test_bit(B_WRITING, &b->state))
+ return;
+
+ __write_dirty_buffer(b);
+ dm_bufio_cond_resched();
+ }
+}
+
+/*
+ * Get writeback threshold and buffer limit for a given client.
+ */
+static void __get_memory_limit(struct dm_bufio_client *c,
+ unsigned long *threshold_buffers,
+ unsigned long *limit_buffers)
+{
+ unsigned long buffers;
+
+ if (dm_bufio_cache_size != dm_bufio_cache_size_latch) {
+ mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+ }
+
+ buffers = dm_bufio_cache_size_per_client >>
+ (c->sectors_per_block_bits + SECTOR_SHIFT);
+
+ if (buffers < DM_BUFIO_MIN_BUFFERS)
+ buffers = DM_BUFIO_MIN_BUFFERS;
+
+ *limit_buffers = buffers;
+ *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100;
+}
+
+/*
+ * Check if we're over watermark.
+ * If we are over threshold_buffers, start freeing buffers.
+ * If we're over "limit_buffers", block until we get under the limit.
+ */
+static void __check_watermark(struct dm_bufio_client *c)
+{
+ unsigned long threshold_buffers, limit_buffers;
+
+ __get_memory_limit(c, &threshold_buffers, &limit_buffers);
+
+ while (c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY] >
+ limit_buffers) {
+
+ struct dm_buffer *b = __get_unclaimed_buffer(c);
+
+ if (!b)
+ return;
+
+ __free_buffer_wake(b);
+ dm_bufio_cond_resched();
+ }
+
+ if (c->n_buffers[LIST_DIRTY] > threshold_buffers)
+ __write_dirty_buffers_async(c, 1);
+}
+
+/*
+ * Find a buffer in the hash.
+ */
+static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
+{
+ struct dm_buffer *b;
+ struct hlist_node *hn;
+
+ hlist_for_each_entry(b, hn, &c->cache_hash[DM_BUFIO_HASH(block)],
+ hash_list) {
+ dm_bufio_cond_resched();
+ if (b->block == block)
+ return b;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------
+ * Getting a buffer
+ *--------------------------------------------------------------*/
+
+enum new_flag {
+ NF_FRESH = 0,
+ NF_READ = 1,
+ NF_GET = 2
+};
+
+static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
+ enum new_flag nf, struct dm_buffer **bp,
+ int *need_submit)
+{
+ struct dm_buffer *b, *new_b = NULL;
+
+ *need_submit = 0;
+
+ b = __find(c, block);
+ if (b) {
+ b->hold_count++;
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
+ test_bit(B_WRITING, &b->state));
+ return b;
+ }
+
+ if (nf == NF_GET)
+ return NULL;
+
+ new_b = __alloc_buffer_wait(c);
+
+ /*
+ * We've had a period where the mutex was unlocked, so need to
+ * recheck the hash table.
+ */
+ b = __find(c, block);
+ if (b) {
+ __free_buffer_wake(new_b);
+ b->hold_count++;
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
+ test_bit(B_WRITING, &b->state));
+ return b;
+ }
+
+ __check_watermark(c);
+
+ b = new_b;
+ b->hold_count = 1;
+ b->read_error = 0;
+ b->write_error = 0;
+ __link_buffer(b, block, LIST_CLEAN);
+
+ if (nf == NF_FRESH) {
+ b->state = 0;
+ return b;
+ }
+
+ b->state = 1 << B_READING;
+ *need_submit = 1;
+
+ return b;
+}
+
+/*
+ * The endio routine for reading: set the error, clear the bit and wake up
+ * anyone waiting on the buffer.
+ */
+static void read_endio(struct bio *bio, int error)
+{
+ struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
+
+ b->read_error = error;
+
+ BUG_ON(!test_bit(B_READING, &b->state));
+
+ smp_mb__before_clear_bit();
+ clear_bit(B_READING, &b->state);
+ smp_mb__after_clear_bit();
+
+ wake_up_bit(&b->state, B_READING);
+}
+
+/*
+ * A common routine for dm_bufio_new and dm_bufio_read. Operation of these
+ * functions is similar except that dm_bufio_new doesn't read the
+ * buffer from the disk (assuming that the caller overwrites all the data
+ * and uses dm_bufio_mark_buffer_dirty to write new data back).
+ */
+static void *new_read(struct dm_bufio_client *c, sector_t block,
+ enum new_flag nf, struct dm_buffer **bp)
+{
+ int need_submit;
+ struct dm_buffer *b;
+
+ dm_bufio_lock(c);
+ b = __bufio_new(c, block, nf, bp, &need_submit);
+ dm_bufio_unlock(c);
+
+ if (!b || IS_ERR(b))
+ return b;
+
+ if (need_submit)
+ submit_io(b, READ, b->block, read_endio);
+
+ wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE);
+
+ if (b->read_error) {
+ int error = b->read_error;
+
+ dm_bufio_release(b);
+
+ return ERR_PTR(error);
+ }
+
+ *bp = b;
+
+ return b->data;
+}
+
+void *dm_bufio_get(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ return new_read(c, block, NF_GET, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get);
+
+void *dm_bufio_read(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ return new_read(c, block, NF_READ, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_read);
+
+void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ return new_read(c, block, NF_FRESH, bp);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_new);
+
+void dm_bufio_release(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ dm_bufio_lock(c);
+
+ BUG_ON(test_bit(B_READING, &b->state));
+ BUG_ON(!b->hold_count);
+
+ b->hold_count--;
+ if (!b->hold_count) {
+ wake_up(&c->free_buffer_wait);
+
+ /*
+ * If there were errors on the buffer, and the buffer is not
+ * to be written, free the buffer. There is no point in caching
+ * invalid buffer.
+ */
+ if ((b->read_error || b->write_error) &&
+ !test_bit(B_WRITING, &b->state) &&
+ !test_bit(B_DIRTY, &b->state)) {
+ __unlink_buffer(b);
+ __free_buffer_wake(b);
+ }
+ }
+
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_release);
+
+void dm_bufio_mark_buffer_dirty(struct dm_buffer *b)
+{
+ struct dm_bufio_client *c = b->c;
+
+ dm_bufio_lock(c);
+
+ if (!test_and_set_bit(B_DIRTY, &b->state))
+ __relink_lru(b, LIST_DIRTY);
+
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_mark_buffer_dirty);
+
+void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c)
+{
+ BUG_ON(dm_bufio_in_request());
+
+ dm_bufio_lock(c);
+ __write_dirty_buffers_async(c, 0);
+ dm_bufio_unlock(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers_async);
+
+/*
+ * For performance, it is essential that the buffers are written asynchronously
+ * and simultaneously (so that the block layer can merge the writes) and then
+ * waited upon.
+ *
+ * Finally, we flush hardware disk cache.
+ */
+int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c)
+{
+ int a, f;
+ unsigned long buffers_processed = 0;
+ struct dm_buffer *b, *tmp;
+
+ dm_bufio_lock(c);
+ __write_dirty_buffers_async(c, 0);
+
+again:
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_DIRTY], lru_list) {
+ int dropped_lock = 0;
+
+ if (buffers_processed < c->n_buffers[LIST_DIRTY])
+ buffers_processed++;
+
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ if (test_bit(B_WRITING, &b->state)) {
+ if (buffers_processed < c->n_buffers[LIST_DIRTY]) {
+ dropped_lock = 1;
+ b->hold_count++;
+ dm_bufio_unlock(c);
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule,
+ TASK_UNINTERRUPTIBLE);
+ dm_bufio_lock(c);
+ b->hold_count--;
+ } else
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule,
+ TASK_UNINTERRUPTIBLE);
+ }
+
+ if (!test_bit(B_DIRTY, &b->state) &&
+ !test_bit(B_WRITING, &b->state))
+ __relink_lru(b, LIST_CLEAN);
+
+ dm_bufio_cond_resched();
+
+ /*
+ * If we dropped the lock, the list is no longer consistent,
+ * so we must restart the search.
+ *
+ * In the most common case, the buffer just processed is
+ * relinked to the clean list, so we won't loop scanning the
+ * same buffer again and again.
+ *
+ * This may livelock if there is another thread simultaneously
+ * dirtying buffers, so we count the number of buffers walked
+ * and if it exceeds the total number of buffers, it means that
+ * someone is doing some writes simultaneously with us. In
+ * this case, stop, dropping the lock.
+ */
+ if (dropped_lock)
+ goto again;
+ }
+ wake_up(&c->free_buffer_wait);
+ dm_bufio_unlock(c);
+
+ a = xchg(&c->async_write_error, 0);
+ f = dm_bufio_issue_flush(c);
+ if (a)
+ return a;
+
+ return f;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers);
+
+/*
+ * Use dm-io to send and empty barrier flush the device.
+ */
+int dm_bufio_issue_flush(struct dm_bufio_client *c)
+{
+ struct dm_io_request io_req = {
+ .bi_rw = REQ_FLUSH,
+ .mem.type = DM_IO_KMEM,
+ .mem.ptr.addr = NULL,
+ .client = c->dm_io,
+ };
+ struct dm_io_region io_reg = {
+ .bdev = c->bdev,
+ .sector = 0,
+ .count = 0,
+ };
+
+ BUG_ON(dm_bufio_in_request());
+
+ return dm_io(&io_req, 1, &io_reg, NULL);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_issue_flush);
+
+/*
+ * We first delete any other buffer that may be at that new location.
+ *
+ * Then, we write the buffer to the original location if it was dirty.
+ *
+ * Then, if we are the only one who is holding the buffer, relink the buffer
+ * in the hash queue for the new location.
+ *
+ * If there was someone else holding the buffer, we write it to the new
+ * location but not relink it, because that other user needs to have the buffer
+ * at the same place.
+ */
+void dm_bufio_release_move(struct dm_buffer *b, sector_t new_block)
+{
+ struct dm_bufio_client *c = b->c;
+ struct dm_buffer *new;
+
+ BUG_ON(dm_bufio_in_request());
+
+ dm_bufio_lock(c);
+
+retry:
+ new = __find(c, new_block);
+ if (new) {
+ if (new->hold_count) {
+ __wait_for_free_buffer(c);
+ goto retry;
+ }
+
+ /*
+ * FIXME: Is there any point waiting for a write that's going
+ * to be overwritten in a bit?
+ */
+ __make_buffer_clean(new);
+ __unlink_buffer(new);
+ __free_buffer_wake(new);
+ }
+
+ BUG_ON(!b->hold_count);
+ BUG_ON(test_bit(B_READING, &b->state));
+
+ __write_dirty_buffer(b);
+ if (b->hold_count == 1) {
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ set_bit(B_DIRTY, &b->state);
+ __unlink_buffer(b);
+ __link_buffer(b, new_block, LIST_DIRTY);
+ } else {
+ sector_t old_block;
+ wait_on_bit_lock(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ /*
+ * Relink buffer to "new_block" so that write_callback
+ * sees "new_block" as a block number.
+ * After the write, link the buffer back to old_block.
+ * All this must be done in bufio lock, so that block number
+ * change isn't visible to other threads.
+ */
+ old_block = b->block;
+ __unlink_buffer(b);
+ __link_buffer(b, new_block, b->list_mode);
+ submit_io(b, WRITE, new_block, write_endio);
+ wait_on_bit(&b->state, B_WRITING,
+ do_io_schedule, TASK_UNINTERRUPTIBLE);
+ __unlink_buffer(b);
+ __link_buffer(b, old_block, b->list_mode);
+ }
+
+ dm_bufio_unlock(c);
+ dm_bufio_release(b);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_release_move);
+
+unsigned dm_bufio_get_block_size(struct dm_bufio_client *c)
+{
+ return c->block_size;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_size);
+
+sector_t dm_bufio_get_device_size(struct dm_bufio_client *c)
+{
+ return i_size_read(c->bdev->bd_inode) >>
+ (SECTOR_SHIFT + c->sectors_per_block_bits);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_device_size);
+
+sector_t dm_bufio_get_block_number(struct dm_buffer *b)
+{
+ return b->block;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_number);
+
+void *dm_bufio_get_block_data(struct dm_buffer *b)
+{
+ return b->data;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_block_data);
+
+void *dm_bufio_get_aux_data(struct dm_buffer *b)
+{
+ return b + 1;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_aux_data);
+
+struct dm_bufio_client *dm_bufio_get_client(struct dm_buffer *b)
+{
+ return b->c;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_client);
+
+static void drop_buffers(struct dm_bufio_client *c)
+{
+ struct dm_buffer *b;
+ int i;
+
+ BUG_ON(dm_bufio_in_request());
+
+ /*
+ * An optimization so that the buffers are not written one-by-one.
+ */
+ dm_bufio_write_dirty_buffers_async(c);
+
+ dm_bufio_lock(c);
+
+ while ((b = __get_unclaimed_buffer(c)))
+ __free_buffer_wake(b);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ list_for_each_entry(b, &c->lru[i], lru_list)
+ DMERR("leaked buffer %llx, hold count %u, list %d",
+ (unsigned long long)b->block, b->hold_count, i);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ BUG_ON(!list_empty(&c->lru[i]));
+
+ dm_bufio_unlock(c);
+}
+
+/*
+ * Test if the buffer is unused and too old, and commit it.
+ * At if noio is set, we must not do any I/O because we hold
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
+ * different bufio client.
+ */
+static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
+ unsigned long max_jiffies)
+{
+ if (jiffies - b->last_accessed < max_jiffies)
+ return 1;
+
+ if (!(gfp & __GFP_IO)) {
+ if (test_bit(B_READING, &b->state) ||
+ test_bit(B_WRITING, &b->state) ||
+ test_bit(B_DIRTY, &b->state))
+ return 1;
+ }
+
+ if (b->hold_count)
+ return 1;
+
+ __make_buffer_clean(b);
+ __unlink_buffer(b);
+ __free_buffer_wake(b);
+
+ return 0;
+}
+
+static void __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
+ struct shrink_control *sc)
+{
+ int l;
+ struct dm_buffer *b, *tmp;
+
+ for (l = 0; l < LIST_SIZE; l++) {
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list)
+ if (!__cleanup_old_buffer(b, sc->gfp_mask, 0) &&
+ !--nr_to_scan)
+ return;
+ dm_bufio_cond_resched();
+ }
+}
+
+static int shrink(struct shrinker *shrinker, struct shrink_control *sc)
+{
+ struct dm_bufio_client *c =
+ container_of(shrinker, struct dm_bufio_client, shrinker);
+ unsigned long r;
+ unsigned long nr_to_scan = sc->nr_to_scan;
+
+ if (sc->gfp_mask & __GFP_IO)
+ dm_bufio_lock(c);
+ else if (!dm_bufio_trylock(c))
+ return !nr_to_scan ? 0 : -1;
+
+ if (nr_to_scan)
+ __scan(c, nr_to_scan, sc);
+
+ r = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
+ if (r > INT_MAX)
+ r = INT_MAX;
+
+ dm_bufio_unlock(c);
+
+ return r;
+}
+
+/*
+ * Create the buffering interface
+ */
+struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsigned block_size,
+ unsigned reserved_buffers, unsigned aux_size,
+ void (*alloc_callback)(struct dm_buffer *),
+ void (*write_callback)(struct dm_buffer *))
+{
+ int r;
+ struct dm_bufio_client *c;
+ unsigned i;
+
+ BUG_ON(block_size < 1 << SECTOR_SHIFT ||
+ (block_size & (block_size - 1)));
+
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ r = -ENOMEM;
+ goto bad_client;
+ }
+ c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS);
+ if (!c->cache_hash) {
+ r = -ENOMEM;
+ goto bad_hash;
+ }
+
+ c->bdev = bdev;
+ c->block_size = block_size;
+ c->sectors_per_block_bits = ffs(block_size) - 1 - SECTOR_SHIFT;
+ c->pages_per_block_bits = (ffs(block_size) - 1 >= PAGE_SHIFT) ?
+ ffs(block_size) - 1 - PAGE_SHIFT : 0;
+ c->blocks_per_page_bits = (ffs(block_size) - 1 < PAGE_SHIFT ?
+ PAGE_SHIFT - (ffs(block_size) - 1) : 0);
+
+ c->aux_size = aux_size;
+ c->alloc_callback = alloc_callback;
+ c->write_callback = write_callback;
+
+ for (i = 0; i < LIST_SIZE; i++) {
+ INIT_LIST_HEAD(&c->lru[i]);
+ c->n_buffers[i] = 0;
+ }
+
+ for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
+ INIT_HLIST_HEAD(&c->cache_hash[i]);
+
+ mutex_init(&c->lock);
+ INIT_LIST_HEAD(&c->reserved_buffers);
+ c->need_reserved_buffers = reserved_buffers;
+
+ init_waitqueue_head(&c->free_buffer_wait);
+ c->async_write_error = 0;
+
+ c->dm_io = dm_io_client_create();
+ if (IS_ERR(c->dm_io)) {
+ r = PTR_ERR(c->dm_io);
+ goto bad_dm_io;
+ }
+
+ mutex_lock(&dm_bufio_clients_lock);
+ if (c->blocks_per_page_bits) {
+ if (!DM_BUFIO_CACHE_NAME(c)) {
+ DM_BUFIO_CACHE_NAME(c) = kasprintf(GFP_KERNEL, "dm_bufio_cache-%u", c->block_size);
+ if (!DM_BUFIO_CACHE_NAME(c)) {
+ r = -ENOMEM;
+ mutex_unlock(&dm_bufio_clients_lock);
+ goto bad_cache;
+ }
+ }
+
+ if (!DM_BUFIO_CACHE(c)) {
+ DM_BUFIO_CACHE(c) = kmem_cache_create(DM_BUFIO_CACHE_NAME(c),
+ c->block_size,
+ c->block_size, 0, NULL);
+ if (!DM_BUFIO_CACHE(c)) {
+ r = -ENOMEM;
+ mutex_unlock(&dm_bufio_clients_lock);
+ goto bad_cache;
+ }
+ }
+ }
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ while (c->need_reserved_buffers) {
+ struct dm_buffer *b = alloc_buffer(c, GFP_KERNEL);
+
+ if (!b) {
+ r = -ENOMEM;
+ goto bad_buffer;
+ }
+ __free_buffer_wake(b);
+ }
+
+ mutex_lock(&dm_bufio_clients_lock);
+ dm_bufio_client_count++;
+ list_add(&c->client_list, &dm_bufio_all_clients);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ c->shrinker.shrink = shrink;
+ c->shrinker.seeks = 1;
+ c->shrinker.batch = 0;
+ register_shrinker(&c->shrinker);
+
+ return c;
+
+bad_buffer:
+bad_cache:
+ while (!list_empty(&c->reserved_buffers)) {
+ struct dm_buffer *b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ free_buffer(b);
+ }
+ dm_io_client_destroy(c->dm_io);
+bad_dm_io:
+ vfree(c->cache_hash);
+bad_hash:
+ kfree(c);
+bad_client:
+ return ERR_PTR(r);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_client_create);
+
+/*
+ * Free the buffering interface.
+ * It is required that there are no references on any buffers.
+ */
+void dm_bufio_client_destroy(struct dm_bufio_client *c)
+{
+ unsigned i;
+
+ drop_buffers(c);
+
+ unregister_shrinker(&c->shrinker);
+
+ mutex_lock(&dm_bufio_clients_lock);
+
+ list_del(&c->client_list);
+ dm_bufio_client_count--;
+ __cache_size_refresh();
+
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
+ BUG_ON(!hlist_empty(&c->cache_hash[i]));
+
+ BUG_ON(c->need_reserved_buffers);
+
+ while (!list_empty(&c->reserved_buffers)) {
+ struct dm_buffer *b = list_entry(c->reserved_buffers.next,
+ struct dm_buffer, lru_list);
+ list_del(&b->lru_list);
+ free_buffer(b);
+ }
+
+ for (i = 0; i < LIST_SIZE; i++)
+ if (c->n_buffers[i])
+ DMERR("leaked buffer count %d: %ld", i, c->n_buffers[i]);
+
+ for (i = 0; i < LIST_SIZE; i++)
+ BUG_ON(c->n_buffers[i]);
+
+ dm_io_client_destroy(c->dm_io);
+ vfree(c->cache_hash);
+ kfree(c);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);
+
+static void cleanup_old_buffers(void)
+{
+ unsigned long max_age = dm_bufio_max_age;
+ struct dm_bufio_client *c;
+
+ barrier();
+
+ if (max_age > ULONG_MAX / HZ)
+ max_age = ULONG_MAX / HZ;
+
+ mutex_lock(&dm_bufio_clients_lock);
+ list_for_each_entry(c, &dm_bufio_all_clients, client_list) {
+ if (!dm_bufio_trylock(c))
+ continue;
+
+ while (!list_empty(&c->lru[LIST_CLEAN])) {
+ struct dm_buffer *b;
+ b = list_entry(c->lru[LIST_CLEAN].prev,
+ struct dm_buffer, lru_list);
+ if (__cleanup_old_buffer(b, 0, max_age * HZ))
+ break;
+ dm_bufio_cond_resched();
+ }
+
+ dm_bufio_unlock(c);
+ dm_bufio_cond_resched();
+ }
+ mutex_unlock(&dm_bufio_clients_lock);
+}
+
+static struct workqueue_struct *dm_bufio_wq;
+static struct delayed_work dm_bufio_work;
+
+static void work_fn(struct work_struct *w)
+{
+ cleanup_old_buffers();
+
+ queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
+ DM_BUFIO_WORK_TIMER_SECS * HZ);
+}
+
+/*----------------------------------------------------------------
+ * Module setup
+ *--------------------------------------------------------------*/
+
+/*
+ * This is called only once for the whole dm_bufio module.
+ * It initializes memory limit.
+ */
+static int __init dm_bufio_init(void)
+{
+ __u64 mem;
+
+ memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches);
+ memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names);
+
+ mem = (__u64)((totalram_pages - totalhigh_pages) *
+ DM_BUFIO_MEMORY_PERCENT / 100) << PAGE_SHIFT;
+
+ if (mem > ULONG_MAX)
+ mem = ULONG_MAX;
+
+#ifdef CONFIG_MMU
+ /*
+ * Get the size of vmalloc space the same way as VMALLOC_TOTAL
+ * in fs/proc/internal.h
+ */
+ if (mem > (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100)
+ mem = (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100;
+#endif
+
+ dm_bufio_default_cache_size = mem;
+
+ mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+
+ dm_bufio_wq = create_singlethread_workqueue("dm_bufio_cache");
+ if (!dm_bufio_wq)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&dm_bufio_work, work_fn);
+ queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
+ DM_BUFIO_WORK_TIMER_SECS * HZ);
+
+ return 0;
+}
+
+/*
+ * This is called once when unloading the dm_bufio module.
+ */
+static void __exit dm_bufio_exit(void)
+{
+ int bug = 0;
+ int i;
+
+ cancel_delayed_work_sync(&dm_bufio_work);
+ destroy_workqueue(dm_bufio_wq);
+
+ for (i = 0; i < ARRAY_SIZE(dm_bufio_caches); i++) {
+ struct kmem_cache *kc = dm_bufio_caches[i];
+
+ if (kc)
+ kmem_cache_destroy(kc);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dm_bufio_cache_names); i++)
+ kfree(dm_bufio_cache_names[i]);
+
+ if (dm_bufio_client_count) {
+ DMCRIT("%s: dm_bufio_client_count leaked: %d",
+ __func__, dm_bufio_client_count);
+ bug = 1;
+ }
+
+ if (dm_bufio_current_allocated) {
+ DMCRIT("%s: dm_bufio_current_allocated leaked: %lu",
+ __func__, dm_bufio_current_allocated);
+ bug = 1;
+ }
+
+ if (dm_bufio_allocated_get_free_pages) {
+ DMCRIT("%s: dm_bufio_allocated_get_free_pages leaked: %lu",
+ __func__, dm_bufio_allocated_get_free_pages);
+ bug = 1;
+ }
+
+ if (dm_bufio_allocated_vmalloc) {
+ DMCRIT("%s: dm_bufio_vmalloc leaked: %lu",
+ __func__, dm_bufio_allocated_vmalloc);
+ bug = 1;
+ }
+
+ if (bug)
+ BUG();
+}
+
+module_init(dm_bufio_init)
+module_exit(dm_bufio_exit)
+
+module_param_named(max_cache_size_bytes, dm_bufio_cache_size, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");
+
+module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
+
+module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory");
+
+module_param_named(allocated_kmem_cache_bytes, dm_bufio_allocated_kmem_cache, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_kmem_cache_bytes, "Memory allocated with kmem_cache_alloc");
+
+module_param_named(allocated_get_free_pages_bytes, dm_bufio_allocated_get_free_pages, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_get_free_pages_bytes, "Memory allocated with get_free_pages");
+
+module_param_named(allocated_vmalloc_bytes, dm_bufio_allocated_vmalloc, ulong, S_IRUGO);
+MODULE_PARM_DESC(allocated_vmalloc_bytes, "Memory allocated with vmalloc");
+
+module_param_named(current_allocated_bytes, dm_bufio_current_allocated, ulong, S_IRUGO);
+MODULE_PARM_DESC(current_allocated_bytes, "Memory currently used by the cache");
+
+MODULE_AUTHOR("Mikulas Patocka <dm-devel@redhat.com>");
+MODULE_DESCRIPTION(DM_NAME " buffered I/O library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-bufio.h b/drivers/md/dm-bufio.h
new file mode 100644
index 000000000000..5c4c3a04e381
--- /dev/null
+++ b/drivers/md/dm-bufio.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009-2011 Red Hat, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_BUFIO_H
+#define DM_BUFIO_H
+
+#include <linux/blkdev.h>
+#include <linux/types.h>
+
+/*----------------------------------------------------------------*/
+
+struct dm_bufio_client;
+struct dm_buffer;
+
+/*
+ * Create a buffered IO cache on a given device
+ */
+struct dm_bufio_client *
+dm_bufio_client_create(struct block_device *bdev, unsigned block_size,
+ unsigned reserved_buffers, unsigned aux_size,
+ void (*alloc_callback)(struct dm_buffer *),
+ void (*write_callback)(struct dm_buffer *));
+
+/*
+ * Release a buffered IO cache.
+ */
+void dm_bufio_client_destroy(struct dm_bufio_client *c);
+
+/*
+ * WARNING: to avoid deadlocks, these conditions are observed:
+ *
+ * - At most one thread can hold at most "reserved_buffers" simultaneously.
+ * - Each other threads can hold at most one buffer.
+ * - Threads which call only dm_bufio_get can hold unlimited number of
+ * buffers.
+ */
+
+/*
+ * Read a given block from disk. Returns pointer to data. Returns a
+ * pointer to dm_buffer that can be used to release the buffer or to make
+ * it dirty.
+ */
+void *dm_bufio_read(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Like dm_bufio_read, but return buffer from cache, don't read
+ * it. If the buffer is not in the cache, return NULL.
+ */
+void *dm_bufio_get(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Like dm_bufio_read, but don't read anything from the disk. It is
+ * expected that the caller initializes the buffer and marks it dirty.
+ */
+void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
+ struct dm_buffer **bp);
+
+/*
+ * Release a reference obtained with dm_bufio_{read,get,new}. The data
+ * pointer and dm_buffer pointer is no longer valid after this call.
+ */
+void dm_bufio_release(struct dm_buffer *b);
+
+/*
+ * Mark a buffer dirty. It should be called after the buffer is modified.
+ *
+ * In case of memory pressure, the buffer may be written after
+ * dm_bufio_mark_buffer_dirty, but before dm_bufio_write_dirty_buffers. So
+ * dm_bufio_write_dirty_buffers guarantees that the buffer is on-disk but
+ * the actual writing may occur earlier.
+ */
+void dm_bufio_mark_buffer_dirty(struct dm_buffer *b);
+
+/*
+ * Initiate writing of dirty buffers, without waiting for completion.
+ */
+void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c);
+
+/*
+ * Write all dirty buffers. Guarantees that all dirty buffers created prior
+ * to this call are on disk when this call exits.
+ */
+int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c);
+
+/*
+ * Send an empty write barrier to the device to flush hardware disk cache.
+ */
+int dm_bufio_issue_flush(struct dm_bufio_client *c);
+
+/*
+ * Like dm_bufio_release but also move the buffer to the new
+ * block. dm_bufio_write_dirty_buffers is needed to commit the new block.
+ */
+void dm_bufio_release_move(struct dm_buffer *b, sector_t new_block);
+
+unsigned dm_bufio_get_block_size(struct dm_bufio_client *c);
+sector_t dm_bufio_get_device_size(struct dm_bufio_client *c);
+sector_t dm_bufio_get_block_number(struct dm_buffer *b);
+void *dm_bufio_get_block_data(struct dm_buffer *b);
+void *dm_bufio_get_aux_data(struct dm_buffer *b);
+struct dm_bufio_client *dm_bufio_get_client(struct dm_buffer *b);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 2e9a3ca37bdd..31c2dc25886d 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1215,6 +1215,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
struct hash_cell *hc;
struct dm_table *t;
struct mapped_device *md;
+ struct target_type *immutable_target_type;
md = find_device(param);
if (!md)
@@ -1230,6 +1231,16 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
goto out;
}
+ immutable_target_type = dm_get_immutable_target_type(md);
+ if (immutable_target_type &&
+ (immutable_target_type != dm_table_get_immutable_target_type(t))) {
+ DMWARN("can't replace immutable target type %s",
+ immutable_target_type->name);
+ dm_table_destroy(t);
+ r = -EINVAL;
+ goto out;
+ }
+
/* Protect md->type and md->queue against concurrent table loads. */
dm_lock_md_type(md);
if (dm_get_md_type(md) == DM_TYPE_NONE)
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 32ac70861d66..bed444c93d8d 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -66,6 +66,8 @@ struct dm_kcopyd_client {
struct list_head pages_jobs;
};
+static struct page_list zero_page_list;
+
static void wake(struct dm_kcopyd_client *kc)
{
queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
@@ -254,6 +256,9 @@ int __init dm_kcopyd_init(void)
if (!_job_cache)
return -ENOMEM;
+ zero_page_list.next = &zero_page_list;
+ zero_page_list.page = ZERO_PAGE(0);
+
return 0;
}
@@ -322,7 +327,7 @@ static int run_complete_job(struct kcopyd_job *job)
dm_kcopyd_notify_fn fn = job->fn;
struct dm_kcopyd_client *kc = job->kc;
- if (job->pages)
+ if (job->pages && job->pages != &zero_page_list)
kcopyd_put_pages(kc, job->pages);
/*
* If this is the master job, the sub jobs have already
@@ -484,6 +489,8 @@ static void dispatch_job(struct kcopyd_job *job)
atomic_inc(&kc->nr_jobs);
if (unlikely(!job->source.count))
push(&kc->complete_jobs, job);
+ else if (job->pages == &zero_page_list)
+ push(&kc->io_jobs, job);
else
push(&kc->pages_jobs, job);
wake(kc);
@@ -592,14 +599,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
job->flags = flags;
job->read_err = 0;
job->write_err = 0;
- job->rw = READ;
-
- job->source = *from;
job->num_dests = num_dests;
memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
- job->pages = NULL;
+ if (from) {
+ job->source = *from;
+ job->pages = NULL;
+ job->rw = READ;
+ } else {
+ memset(&job->source, 0, sizeof job->source);
+ job->source.count = job->dests[0].count;
+ job->pages = &zero_page_list;
+ job->rw = WRITE;
+ }
job->fn = fn;
job->context = context;
@@ -617,6 +630,14 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
}
EXPORT_SYMBOL(dm_kcopyd_copy);
+int dm_kcopyd_zero(struct dm_kcopyd_client *kc,
+ unsigned num_dests, struct dm_io_region *dests,
+ unsigned flags, dm_kcopyd_notify_fn fn, void *context)
+{
+ return dm_kcopyd_copy(kc, NULL, num_dests, dests, flags, fn, context);
+}
+EXPORT_SYMBOL(dm_kcopyd_zero);
+
void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
dm_kcopyd_notify_fn fn, void *context)
{
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index 1021c8986011..8db3862dade5 100644
--- a/drivers/md/dm-log-userspace-base.c
+++ b/drivers/md/dm-log-userspace-base.c
@@ -30,6 +30,7 @@ struct flush_entry {
struct log_c {
struct dm_target *ti;
+ struct dm_dev *log_dev;
uint32_t region_size;
region_t region_count;
uint64_t luid;
@@ -146,7 +147,7 @@ static int build_constructor_string(struct dm_target *ti,
* <UUID> <other args>
* Where 'other args' is the userspace implementation specific log
* arguments. An example might be:
- * <UUID> clustered_disk <arg count> <log dev> <region_size> [[no]sync]
+ * <UUID> clustered-disk <arg count> <log dev> <region_size> [[no]sync]
*
* So, this module will strip off the <UUID> for identification purposes
* when communicating with userspace about a log; but will pass on everything
@@ -161,13 +162,15 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
struct log_c *lc = NULL;
uint64_t rdata;
size_t rdata_size = sizeof(rdata);
+ char *devices_rdata = NULL;
+ size_t devices_rdata_size = DM_NAME_LEN;
if (argc < 3) {
DMWARN("Too few arguments to userspace dirty log");
return -EINVAL;
}
- lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+ lc = kzalloc(sizeof(*lc), GFP_KERNEL);
if (!lc) {
DMWARN("Unable to allocate userspace log context.");
return -ENOMEM;
@@ -195,9 +198,19 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
return str_size;
}
- /* Send table string */
+ devices_rdata = kzalloc(devices_rdata_size, GFP_KERNEL);
+ if (!devices_rdata) {
+ DMERR("Failed to allocate memory for device information");
+ r = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Send table string and get back any opened device.
+ */
r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR,
- ctr_str, str_size, NULL, NULL);
+ ctr_str, str_size,
+ devices_rdata, &devices_rdata_size);
if (r < 0) {
if (r == -ESRCH)
@@ -220,7 +233,20 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
lc->region_size = (uint32_t)rdata;
lc->region_count = dm_sector_div_up(ti->len, lc->region_size);
+ if (devices_rdata_size) {
+ if (devices_rdata[devices_rdata_size - 1] != '\0') {
+ DMERR("DM_ULOG_CTR device return string not properly terminated");
+ r = -EINVAL;
+ goto out;
+ }
+ r = dm_get_device(ti, devices_rdata,
+ dm_table_get_mode(ti->table), &lc->log_dev);
+ if (r)
+ DMERR("Failed to register %s with device-mapper",
+ devices_rdata);
+ }
out:
+ kfree(devices_rdata);
if (r) {
kfree(lc);
kfree(ctr_str);
@@ -241,6 +267,9 @@ static void userspace_dtr(struct dm_dirty_log *log)
NULL, 0,
NULL, NULL);
+ if (lc->log_dev)
+ dm_put_device(lc->ti, lc->log_dev);
+
kfree(lc->usr_argv_str);
kfree(lc);
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 37a37266a1e3..11fa96df4b06 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -1017,30 +1017,56 @@ static int raid_status(struct dm_target *ti, status_type_t type,
struct raid_set *rs = ti->private;
unsigned raid_param_cnt = 1; /* at least 1 for chunksize */
unsigned sz = 0;
- int i;
+ int i, array_in_sync = 0;
sector_t sync;
switch (type) {
case STATUSTYPE_INFO:
DMEMIT("%s %d ", rs->raid_type->name, rs->md.raid_disks);
- for (i = 0; i < rs->md.raid_disks; i++) {
- if (test_bit(Faulty, &rs->dev[i].rdev.flags))
- DMEMIT("D");
- else if (test_bit(In_sync, &rs->dev[i].rdev.flags))
- DMEMIT("A");
- else
- DMEMIT("a");
- }
-
if (test_bit(MD_RECOVERY_RUNNING, &rs->md.recovery))
sync = rs->md.curr_resync_completed;
else
sync = rs->md.recovery_cp;
- if (sync > rs->md.resync_max_sectors)
+ if (sync >= rs->md.resync_max_sectors) {
+ array_in_sync = 1;
sync = rs->md.resync_max_sectors;
+ } else {
+ /*
+ * The array may be doing an initial sync, or it may
+ * be rebuilding individual components. If all the
+ * devices are In_sync, then it is the array that is
+ * being initialized.
+ */
+ for (i = 0; i < rs->md.raid_disks; i++)
+ if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
+ array_in_sync = 1;
+ }
+ /*
+ * Status characters:
+ * 'D' = Dead/Failed device
+ * 'a' = Alive but not in-sync
+ * 'A' = Alive and in-sync
+ */
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if (test_bit(Faulty, &rs->dev[i].rdev.flags))
+ DMEMIT("D");
+ else if (!array_in_sync ||
+ !test_bit(In_sync, &rs->dev[i].rdev.flags))
+ DMEMIT("a");
+ else
+ DMEMIT("A");
+ }
+ /*
+ * In-sync ratio:
+ * The in-sync ratio shows the progress of:
+ * - Initializing the array
+ * - Rebuilding a subset of devices of the array
+ * The user can distinguish between the two by referring
+ * to the status characters.
+ */
DMEMIT(" %llu/%llu",
(unsigned long long) sync,
(unsigned long long) rs->md.resync_max_sectors);
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index bc04518e9d8b..8e9132130142 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -54,7 +54,9 @@ struct dm_table {
sector_t *highs;
struct dm_target *targets;
+ struct target_type *immutable_target_type;
unsigned integrity_supported:1;
+ unsigned singleton:1;
/*
* Indicates the rw permissions for the new logical
@@ -740,6 +742,12 @@ int dm_table_add_target(struct dm_table *t, const char *type,
char **argv;
struct dm_target *tgt;
+ if (t->singleton) {
+ DMERR("%s: target type %s must appear alone in table",
+ dm_device_name(t->md), t->targets->type->name);
+ return -EINVAL;
+ }
+
if ((r = check_space(t)))
return r;
@@ -758,6 +766,36 @@ int dm_table_add_target(struct dm_table *t, const char *type,
return -EINVAL;
}
+ if (dm_target_needs_singleton(tgt->type)) {
+ if (t->num_targets) {
+ DMERR("%s: target type %s must appear alone in table",
+ dm_device_name(t->md), type);
+ return -EINVAL;
+ }
+ t->singleton = 1;
+ }
+
+ if (dm_target_always_writeable(tgt->type) && !(t->mode & FMODE_WRITE)) {
+ DMERR("%s: target type %s may not be included in read-only tables",
+ dm_device_name(t->md), type);
+ return -EINVAL;
+ }
+
+ if (t->immutable_target_type) {
+ if (t->immutable_target_type != tgt->type) {
+ DMERR("%s: immutable target type %s cannot be mixed with other target types",
+ dm_device_name(t->md), t->immutable_target_type->name);
+ return -EINVAL;
+ }
+ } else if (dm_target_is_immutable(tgt->type)) {
+ if (t->num_targets) {
+ DMERR("%s: immutable target type %s cannot be mixed with other target types",
+ dm_device_name(t->md), tgt->type->name);
+ return -EINVAL;
+ }
+ t->immutable_target_type = tgt->type;
+ }
+
tgt->table = t;
tgt->begin = start;
tgt->len = len;
@@ -915,6 +953,11 @@ unsigned dm_table_get_type(struct dm_table *t)
return t->type;
}
+struct target_type *dm_table_get_immutable_target_type(struct dm_table *t)
+{
+ return t->immutable_target_type;
+}
+
bool dm_table_request_based(struct dm_table *t)
{
return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED;
@@ -1299,6 +1342,31 @@ static bool dm_table_discard_zeroes_data(struct dm_table *t)
return 1;
}
+static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && blk_queue_nonrot(q);
+}
+
+static bool dm_table_is_nonrot(struct dm_table *t)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ /* Ensure that all underlying device are non-rotational. */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (!ti->type->iterate_devices ||
+ !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+ return 0;
+ }
+
+ return 1;
+}
+
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits)
{
@@ -1324,6 +1392,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_discard_zeroes_data(t))
q->limits.discard_zeroes_data = 0;
+ if (dm_table_is_nonrot(t))
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ else
+ queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
+
dm_table_set_integrity(t);
/*
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
new file mode 100644
index 000000000000..59c4f0446ffa
--- /dev/null
+++ b/drivers/md/dm-thin-metadata.c
@@ -0,0 +1,1391 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-thin-metadata.h"
+#include "persistent-data/dm-btree.h"
+#include "persistent-data/dm-space-map.h"
+#include "persistent-data/dm-space-map-disk.h"
+#include "persistent-data/dm-transaction-manager.h"
+
+#include <linux/list.h>
+#include <linux/device-mapper.h>
+#include <linux/workqueue.h>
+
+/*--------------------------------------------------------------------------
+ * As far as the metadata goes, there is:
+ *
+ * - A superblock in block zero, taking up fewer than 512 bytes for
+ * atomic writes.
+ *
+ * - A space map managing the metadata blocks.
+ *
+ * - A space map managing the data blocks.
+ *
+ * - A btree mapping our internal thin dev ids onto struct disk_device_details.
+ *
+ * - A hierarchical btree, with 2 levels which effectively maps (thin
+ * dev id, virtual block) -> block_time. Block time is a 64-bit
+ * field holding the time in the low 24 bits, and block in the top 48
+ * bits.
+ *
+ * BTrees consist solely of btree_nodes, that fill a block. Some are
+ * internal nodes, as such their values are a __le64 pointing to other
+ * nodes. Leaf nodes can store data of any reasonable size (ie. much
+ * smaller than the block size). The nodes consist of the header,
+ * followed by an array of keys, followed by an array of values. We have
+ * to binary search on the keys so they're all held together to help the
+ * cpu cache.
+ *
+ * Space maps have 2 btrees:
+ *
+ * - One maps a uint64_t onto a struct index_entry. Which points to a
+ * bitmap block, and has some details about how many free entries there
+ * are etc.
+ *
+ * - The bitmap blocks have a header (for the checksum). Then the rest
+ * of the block is pairs of bits. With the meaning being:
+ *
+ * 0 - ref count is 0
+ * 1 - ref count is 1
+ * 2 - ref count is 2
+ * 3 - ref count is higher than 2
+ *
+ * - If the count is higher than 2 then the ref count is entered in a
+ * second btree that directly maps the block_address to a uint32_t ref
+ * count.
+ *
+ * The space map metadata variant doesn't have a bitmaps btree. Instead
+ * it has one single blocks worth of index_entries. This avoids
+ * recursive issues with the bitmap btree needing to allocate space in
+ * order to insert. With a small data block size such as 64k the
+ * metadata support data devices that are hundreds of terrabytes.
+ *
+ * The space maps allocate space linearly from front to back. Space that
+ * is freed in a transaction is never recycled within that transaction.
+ * To try and avoid fragmenting _free_ space the allocator always goes
+ * back and fills in gaps.
+ *
+ * All metadata io is in THIN_METADATA_BLOCK_SIZE sized/aligned chunks
+ * from the block manager.
+ *--------------------------------------------------------------------------*/
+
+#define DM_MSG_PREFIX "thin metadata"
+
+#define THIN_SUPERBLOCK_MAGIC 27022010
+#define THIN_SUPERBLOCK_LOCATION 0
+#define THIN_VERSION 1
+#define THIN_METADATA_CACHE_SIZE 64
+#define SECTOR_TO_BLOCK_SHIFT 3
+
+/* This should be plenty */
+#define SPACE_MAP_ROOT_SIZE 128
+
+/*
+ * Little endian on-disk superblock and device details.
+ */
+struct thin_disk_superblock {
+ __le32 csum; /* Checksum of superblock except for this field. */
+ __le32 flags;
+ __le64 blocknr; /* This block number, dm_block_t. */
+
+ __u8 uuid[16];
+ __le64 magic;
+ __le32 version;
+ __le32 time;
+
+ __le64 trans_id;
+
+ /*
+ * Root held by userspace transactions.
+ */
+ __le64 held_root;
+
+ __u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+
+ /*
+ * 2-level btree mapping (dev_id, (dev block, time)) -> data block
+ */
+ __le64 data_mapping_root;
+
+ /*
+ * Device detail root mapping dev_id -> device_details
+ */
+ __le64 device_details_root;
+
+ __le32 data_block_size; /* In 512-byte sectors. */
+
+ __le32 metadata_block_size; /* In 512-byte sectors. */
+ __le64 metadata_nr_blocks;
+
+ __le32 compat_flags;
+ __le32 compat_ro_flags;
+ __le32 incompat_flags;
+} __packed;
+
+struct disk_device_details {
+ __le64 mapped_blocks;
+ __le64 transaction_id; /* When created. */
+ __le32 creation_time;
+ __le32 snapshotted_time;
+} __packed;
+
+struct dm_pool_metadata {
+ struct hlist_node hash;
+
+ struct block_device *bdev;
+ struct dm_block_manager *bm;
+ struct dm_space_map *metadata_sm;
+ struct dm_space_map *data_sm;
+ struct dm_transaction_manager *tm;
+ struct dm_transaction_manager *nb_tm;
+
+ /*
+ * Two-level btree.
+ * First level holds thin_dev_t.
+ * Second level holds mappings.
+ */
+ struct dm_btree_info info;
+
+ /*
+ * Non-blocking version of the above.
+ */
+ struct dm_btree_info nb_info;
+
+ /*
+ * Just the top level for deleting whole devices.
+ */
+ struct dm_btree_info tl_info;
+
+ /*
+ * Just the bottom level for creating new devices.
+ */
+ struct dm_btree_info bl_info;
+
+ /*
+ * Describes the device details btree.
+ */
+ struct dm_btree_info details_info;
+
+ 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;
+};
+
+struct dm_thin_device {
+ struct list_head list;
+ struct dm_pool_metadata *pmd;
+ dm_thin_id id;
+
+ int open_count;
+ int changed;
+ uint64_t mapped_blocks;
+ uint64_t transaction_id;
+ uint32_t creation_time;
+ uint32_t snapshotted_time;
+};
+
+/*----------------------------------------------------------------
+ * superblock validator
+ *--------------------------------------------------------------*/
+
+#define SUPERBLOCK_CSUM_XOR 160774
+
+static void sb_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct thin_disk_superblock *disk_super = dm_block_data(b);
+
+ disk_super->blocknr = cpu_to_le64(dm_block_location(b));
+ disk_super->csum = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
+ block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+}
+
+static int sb_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct thin_disk_superblock *disk_super = dm_block_data(b);
+ __le32 csum_le;
+
+ if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) {
+ DMERR("sb_check failed: blocknr %llu: "
+ "wanted %llu", le64_to_cpu(disk_super->blocknr),
+ (unsigned long long)dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ if (le64_to_cpu(disk_super->magic) != THIN_SUPERBLOCK_MAGIC) {
+ DMERR("sb_check failed: magic %llu: "
+ "wanted %llu", le64_to_cpu(disk_super->magic),
+ (unsigned long long)THIN_SUPERBLOCK_MAGIC);
+ return -EILSEQ;
+ }
+
+ csum_le = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
+ block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+ if (csum_le != disk_super->csum) {
+ DMERR("sb_check failed: csum %u: wanted %u",
+ le32_to_cpu(csum_le), le32_to_cpu(disk_super->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator sb_validator = {
+ .name = "superblock",
+ .prepare_for_write = sb_prepare_for_write,
+ .check = sb_check
+};
+
+/*----------------------------------------------------------------
+ * Methods for the btree value types
+ *--------------------------------------------------------------*/
+
+static uint64_t pack_block_time(dm_block_t b, uint32_t t)
+{
+ return (b << 24) | t;
+}
+
+static void unpack_block_time(uint64_t v, dm_block_t *b, uint32_t *t)
+{
+ *b = v >> 24;
+ *t = v & ((1 << 24) - 1);
+}
+
+static void data_block_inc(void *context, void *value_le)
+{
+ struct dm_space_map *sm = context;
+ __le64 v_le;
+ uint64_t b;
+ uint32_t t;
+
+ memcpy(&v_le, value_le, sizeof(v_le));
+ unpack_block_time(le64_to_cpu(v_le), &b, &t);
+ dm_sm_inc_block(sm, b);
+}
+
+static void data_block_dec(void *context, void *value_le)
+{
+ struct dm_space_map *sm = context;
+ __le64 v_le;
+ uint64_t b;
+ uint32_t t;
+
+ memcpy(&v_le, value_le, sizeof(v_le));
+ unpack_block_time(le64_to_cpu(v_le), &b, &t);
+ dm_sm_dec_block(sm, b);
+}
+
+static int data_block_equal(void *context, void *value1_le, void *value2_le)
+{
+ __le64 v1_le, v2_le;
+ uint64_t b1, b2;
+ uint32_t t;
+
+ memcpy(&v1_le, value1_le, sizeof(v1_le));
+ memcpy(&v2_le, value2_le, sizeof(v2_le));
+ unpack_block_time(le64_to_cpu(v1_le), &b1, &t);
+ unpack_block_time(le64_to_cpu(v2_le), &b2, &t);
+
+ return b1 == b2;
+}
+
+static void subtree_inc(void *context, void *value)
+{
+ struct dm_btree_info *info = context;
+ __le64 root_le;
+ uint64_t root;
+
+ memcpy(&root_le, value, sizeof(root_le));
+ root = le64_to_cpu(root_le);
+ dm_tm_inc(info->tm, root);
+}
+
+static void subtree_dec(void *context, void *value)
+{
+ struct dm_btree_info *info = context;
+ __le64 root_le;
+ uint64_t root;
+
+ memcpy(&root_le, value, sizeof(root_le));
+ root = le64_to_cpu(root_le);
+ if (dm_btree_del(info, root))
+ DMERR("btree delete failed\n");
+}
+
+static int subtree_equal(void *context, void *value1_le, void *value2_le)
+{
+ __le64 v1_le, v2_le;
+ memcpy(&v1_le, value1_le, sizeof(v1_le));
+ memcpy(&v2_le, value2_le, sizeof(v2_le));
+
+ return v1_le == v2_le;
+}
+
+/*----------------------------------------------------------------*/
+
+static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
+{
+ int r;
+ unsigned i;
+ struct dm_block *b;
+ __le64 *data_le, zero = cpu_to_le64(0);
+ unsigned block_size = dm_bm_block_size(bm) / sizeof(__le64);
+
+ /*
+ * We can't use a validator here - it may be all zeroes.
+ */
+ r = dm_bm_read_lock(bm, THIN_SUPERBLOCK_LOCATION, NULL, &b);
+ if (r)
+ return r;
+
+ data_le = dm_block_data(b);
+ *result = 1;
+ for (i = 0; i < block_size; i++) {
+ if (data_le[i] != zero) {
+ *result = 0;
+ break;
+ }
+ }
+
+ 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)
+{
+ 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");
+ 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.levels = 2;
+ pmd->info.value_type.context = pmd->data_sm;
+ pmd->info.value_type.size = sizeof(__le64);
+ pmd->info.value_type.inc = data_block_inc;
+ pmd->info.value_type.dec = data_block_dec;
+ pmd->info.value_type.equal = data_block_equal;
+
+ 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.levels = 1;
+ pmd->tl_info.value_type.context = &pmd->info;
+ pmd->tl_info.value_type.size = sizeof(__le64);
+ pmd->tl_info.value_type.inc = subtree_inc;
+ pmd->tl_info.value_type.dec = subtree_dec;
+ pmd->tl_info.value_type.equal = subtree_equal;
+
+ pmd->bl_info.tm = tm;
+ pmd->bl_info.levels = 1;
+ pmd->bl_info.value_type.context = pmd->data_sm;
+ pmd->bl_info.value_type.size = sizeof(__le64);
+ pmd->bl_info.value_type.inc = data_block_inc;
+ 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.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;
+
+ 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);
+
+ return 0;
+
+bad_data_sm:
+ dm_sm_destroy(data_sm);
+bad:
+ dm_tm_destroy(tm);
+ dm_sm_destroy(sm);
+
+ return r;
+}
+
+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.
+ */
+ r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ return r;
+
+ disk_super = dm_block_data(sblock);
+ pmd->time = le32_to_cpu(disk_super->time);
+ pmd->root = le64_to_cpu(disk_super->data_mapping_root);
+ pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+ pmd->trans_id = le64_to_cpu(disk_super->trans_id);
+ 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;
+}
+
+static int __write_changed_details(struct dm_pool_metadata *pmd)
+{
+ int r;
+ struct dm_thin_device *td, *tmp;
+ struct disk_device_details details;
+ uint64_t key;
+
+ list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
+ if (!td->changed)
+ continue;
+
+ key = td->id;
+
+ details.mapped_blocks = cpu_to_le64(td->mapped_blocks);
+ details.transaction_id = cpu_to_le64(td->transaction_id);
+ details.creation_time = cpu_to_le32(td->creation_time);
+ details.snapshotted_time = cpu_to_le32(td->snapshotted_time);
+ __dm_bless_for_disk(&details);
+
+ r = dm_btree_insert(&pmd->details_info, pmd->details_root,
+ &key, &details, &pmd->details_root);
+ if (r)
+ return r;
+
+ if (td->open_count)
+ td->changed = 0;
+ else {
+ list_del(&td->list);
+ kfree(td);
+ }
+
+ pmd->need_commit = 1;
+ }
+
+ return 0;
+}
+
+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;
+ struct dm_block *sblock;
+
+ /*
+ * We need to know if the thin_disk_superblock exceeds a 512-byte sector.
+ */
+ BUILD_BUG_ON(sizeof(struct thin_disk_superblock) > 512);
+
+ r = __write_changed_details(pmd);
+ if (r < 0)
+ goto out;
+
+ if (!pmd->need_commit)
+ goto out;
+
+ r = dm_sm_commit(pmd->data_sm);
+ if (r < 0)
+ goto out;
+
+ r = dm_tm_pre_commit(pmd->tm);
+ if (r < 0)
+ goto out;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
+ if (r < 0)
+ goto out;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &data_len);
+ if (r < 0)
+ goto out;
+
+ r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ goto out;
+
+ disk_super = dm_block_data(sblock);
+ disk_super->time = cpu_to_le32(pmd->time);
+ disk_super->data_mapping_root = cpu_to_le64(pmd->root);
+ disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+ disk_super->trans_id = cpu_to_le64(pmd->trans_id);
+ disk_super->flags = cpu_to_le32(pmd->flags);
+
+ r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
+ metadata_len);
+ if (r < 0)
+ goto out_locked;
+
+ r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
+ data_len);
+ if (r < 0)
+ goto out_locked;
+
+ r = dm_tm_commit(pmd->tm, sblock);
+ if (!r)
+ pmd->need_commit = 0;
+
+out:
+ return r;
+
+out_locked:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
+ sector_t data_block_size)
+{
+ 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) {
+ DMERR("could not allocate metadata struct");
+ 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);
+ }
+
+
+ r = init_pmd(pmd, bm, 0, create);
+ 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;
+
+ 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);
+ if (r < 0) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ goto bad;
+ }
+
+ 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)
+{
+ int r;
+ unsigned open_devices = 0;
+ struct dm_thin_device *td, *tmp;
+
+ down_read(&pmd->root_lock);
+ list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
+ if (td->open_count)
+ open_devices++;
+ else {
+ list_del(&td->list);
+ kfree(td);
+ }
+ }
+ up_read(&pmd->root_lock);
+
+ if (open_devices) {
+ DMERR("attempt to close pmd when %u device(s) are still open",
+ open_devices);
+ return -EBUSY;
+ }
+
+ 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);
+
+ return 0;
+}
+
+static int __open_device(struct dm_pool_metadata *pmd,
+ dm_thin_id dev, int create,
+ struct dm_thin_device **td)
+{
+ int r, changed = 0;
+ struct dm_thin_device *td2;
+ uint64_t key = dev;
+ struct disk_device_details details_le;
+
+ /*
+ * Check the device isn't already open.
+ */
+ list_for_each_entry(td2, &pmd->thin_devices, list)
+ if (td2->id == dev) {
+ td2->open_count++;
+ *td = td2;
+ return 0;
+ }
+
+ /*
+ * Check the device exists.
+ */
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &key, &details_le);
+ if (r) {
+ if (r != -ENODATA || !create)
+ return r;
+
+ changed = 1;
+ details_le.mapped_blocks = 0;
+ details_le.transaction_id = cpu_to_le64(pmd->trans_id);
+ details_le.creation_time = cpu_to_le32(pmd->time);
+ details_le.snapshotted_time = cpu_to_le32(pmd->time);
+ }
+
+ *td = kmalloc(sizeof(**td), GFP_NOIO);
+ if (!*td)
+ return -ENOMEM;
+
+ (*td)->pmd = pmd;
+ (*td)->id = dev;
+ (*td)->open_count = 1;
+ (*td)->changed = changed;
+ (*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);
+ (*td)->snapshotted_time = le32_to_cpu(details_le.snapshotted_time);
+
+ list_add(&(*td)->list, &pmd->thin_devices);
+
+ return 0;
+}
+
+static void __close_device(struct dm_thin_device *td)
+{
+ --td->open_count;
+}
+
+static int __create_thin(struct dm_pool_metadata *pmd,
+ dm_thin_id dev)
+{
+ int r;
+ dm_block_t dev_root;
+ uint64_t key = dev;
+ struct disk_device_details details_le;
+ struct dm_thin_device *td;
+ __le64 value;
+
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &key, &details_le);
+ if (!r)
+ return -EEXIST;
+
+ /*
+ * Create an empty btree for the mappings.
+ */
+ r = dm_btree_empty(&pmd->bl_info, &dev_root);
+ if (r)
+ return r;
+
+ /*
+ * Insert it into the main mapping tree.
+ */
+ value = cpu_to_le64(dev_root);
+ __dm_bless_for_disk(&value);
+ r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
+ if (r) {
+ dm_btree_del(&pmd->bl_info, dev_root);
+ return r;
+ }
+
+ r = __open_device(pmd, dev, 1, &td);
+ if (r) {
+ __close_device(td);
+ dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ dm_btree_del(&pmd->bl_info, dev_root);
+ return r;
+ }
+ td->changed = 1;
+ __close_device(td);
+
+ return r;
+}
+
+int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __create_thin(pmd, dev);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+static int __set_snapshot_details(struct dm_pool_metadata *pmd,
+ struct dm_thin_device *snap,
+ dm_thin_id origin, uint32_t time)
+{
+ int r;
+ struct dm_thin_device *td;
+
+ r = __open_device(pmd, origin, 0, &td);
+ if (r)
+ return r;
+
+ td->changed = 1;
+ td->snapshotted_time = time;
+
+ snap->mapped_blocks = td->mapped_blocks;
+ snap->snapshotted_time = time;
+ __close_device(td);
+
+ return 0;
+}
+
+static int __create_snap(struct dm_pool_metadata *pmd,
+ dm_thin_id dev, dm_thin_id origin)
+{
+ int r;
+ dm_block_t origin_root;
+ uint64_t key = origin, dev_key = dev;
+ struct dm_thin_device *td;
+ struct disk_device_details details_le;
+ __le64 value;
+
+ /* check this device is unused */
+ r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
+ &dev_key, &details_le);
+ if (!r)
+ return -EEXIST;
+
+ /* find the mapping tree for the origin */
+ r = dm_btree_lookup(&pmd->tl_info, pmd->root, &key, &value);
+ if (r)
+ return r;
+ origin_root = le64_to_cpu(value);
+
+ /* clone the origin, an inc will do */
+ dm_tm_inc(pmd->tm, origin_root);
+
+ /* insert into the main mapping tree */
+ value = cpu_to_le64(origin_root);
+ __dm_bless_for_disk(&value);
+ key = dev;
+ r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
+ if (r) {
+ dm_tm_dec(pmd->tm, origin_root);
+ return r;
+ }
+
+ pmd->time++;
+
+ r = __open_device(pmd, dev, 1, &td);
+ if (r)
+ goto bad;
+
+ r = __set_snapshot_details(pmd, td, origin, pmd->time);
+ if (r)
+ goto bad;
+
+ __close_device(td);
+ return 0;
+
+bad:
+ __close_device(td);
+ dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ dm_btree_remove(&pmd->details_info, pmd->details_root,
+ &key, &pmd->details_root);
+ return r;
+}
+
+int dm_pool_create_snap(struct dm_pool_metadata *pmd,
+ dm_thin_id dev,
+ dm_thin_id origin)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __create_snap(pmd, dev, origin);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
+{
+ int r;
+ uint64_t key = dev;
+ struct dm_thin_device *td;
+
+ /* TODO: failure should mark the transaction invalid */
+ r = __open_device(pmd, dev, 0, &td);
+ if (r)
+ return r;
+
+ if (td->open_count > 1) {
+ __close_device(td);
+ return -EBUSY;
+ }
+
+ list_del(&td->list);
+ kfree(td);
+ r = dm_btree_remove(&pmd->details_info, pmd->details_root,
+ &key, &pmd->details_root);
+ if (r)
+ return r;
+
+ r = dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
+ 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;
+
+ down_write(&pmd->root_lock);
+ r = __delete_device(pmd, dev);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t current_id,
+ uint64_t new_id)
+{
+ down_write(&pmd->root_lock);
+ if (pmd->trans_id != current_id) {
+ up_write(&pmd->root_lock);
+ DMERR("mismatched transaction id");
+ return -EINVAL;
+ }
+
+ pmd->trans_id = new_id;
+ pmd->need_commit = 1;
+ up_write(&pmd->root_lock);
+
+ return 0;
+}
+
+int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t *result)
+{
+ down_read(&pmd->root_lock);
+ *result = pmd->trans_id;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+static int __get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+ struct thin_disk_superblock *disk_super;
+ struct dm_block *sblock;
+
+ r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r)
+ return r;
+
+ disk_super = dm_block_data(sblock);
+ *result = le64_to_cpu(disk_super->held_root);
+
+ return dm_bm_unlock(sblock);
+}
+
+int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = __get_held_metadata_root(pmd, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ struct dm_thin_device **td)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __open_device(pmd, dev, 0, td);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_close_thin_device(struct dm_thin_device *td)
+{
+ down_write(&td->pmd->root_lock);
+ __close_device(td);
+ up_write(&td->pmd->root_lock);
+
+ return 0;
+}
+
+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)
+{
+ return td->snapshotted_time > 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;
+ uint64_t block_time = 0;
+ __le64 value;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ 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
+ return -EWOULDBLOCK;
+
+ if (!r) {
+ dm_block_t exception_block;
+ uint32_t exception_time;
+ unpack_block_time(block_time, &exception_block,
+ &exception_time);
+ result->block = exception_block;
+ result->shared = __snapshotted_since(td, exception_time);
+ }
+
+ return r;
+}
+
+static int __insert(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block)
+{
+ int r, inserted;
+ __le64 value;
+ 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);
+
+ r = dm_btree_insert_notify(&pmd->info, pmd->root, keys, &value,
+ &pmd->root, &inserted);
+ if (r)
+ return r;
+
+ if (inserted) {
+ td->mapped_blocks++;
+ td->changed = 1;
+ }
+
+ return 0;
+}
+
+int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block)
+{
+ int r;
+
+ down_write(&td->pmd->root_lock);
+ r = __insert(td, block, data_block);
+ up_write(&td->pmd->root_lock);
+
+ return r;
+}
+
+static int __remove(struct dm_thin_device *td, dm_block_t block)
+{
+ int r;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ r = dm_btree_remove(&pmd->info, pmd->root, keys, &pmd->root);
+ if (r)
+ return r;
+
+ pmd->need_commit = 1;
+
+ return 0;
+}
+
+int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
+{
+ int r;
+
+ down_write(&td->pmd->root_lock);
+ 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)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+
+ r = dm_sm_new_block(pmd->data_sm, result);
+ pmd->need_commit = 1;
+
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+
+ r = __commit_transaction(pmd);
+ if (r <= 0)
+ goto out;
+
+ /*
+ * Open the next transaction.
+ */
+ r = __begin_transaction(pmd);
+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;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_free(pmd->data_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_free(pmd->metadata_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
+ dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result)
+{
+ down_read(&pmd->root_lock);
+ *result = pmd->data_block_size;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+ int r;
+
+ down_read(&pmd->root_lock);
+ r = dm_sm_get_nr_blocks(pmd->data_sm, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
+{
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ *result = td->mapped_blocks;
+ up_read(&pmd->root_lock);
+
+ return 0;
+}
+
+static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
+{
+ int r;
+ __le64 value_le;
+ dm_block_t thin_root;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ r = dm_btree_lookup(&pmd->tl_info, pmd->root, &td->id, &value_le);
+ if (r)
+ return r;
+
+ thin_root = le64_to_cpu(value_le);
+
+ return dm_btree_find_highest_key(&pmd->bl_info, thin_root, result);
+}
+
+int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
+ dm_block_t *result)
+{
+ int r;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ r = __highest_block(td, result);
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
+static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r;
+ dm_block_t old_count;
+
+ r = dm_sm_get_nr_blocks(pmd->data_sm, &old_count);
+ if (r)
+ return r;
+
+ if (new_count == old_count)
+ return 0;
+
+ if (new_count < old_count) {
+ DMERR("cannot reduce size of data device");
+ return -EINVAL;
+ }
+
+ r = dm_sm_extend(pmd->data_sm, new_count - old_count);
+ if (!r)
+ pmd->need_commit = 1;
+
+ return r;
+}
+
+int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = __resize_data_dev(pmd, new_count);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
new file mode 100644
index 000000000000..859c16896877
--- /dev/null
+++ b/drivers/md/dm-thin-metadata.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_THIN_METADATA_H
+#define DM_THIN_METADATA_H
+
+#include "persistent-data/dm-block-manager.h"
+
+#define THIN_METADATA_BLOCK_SIZE 4096
+
+/*----------------------------------------------------------------*/
+
+struct dm_pool_metadata;
+struct dm_thin_device;
+
+/*
+ * Device identifier
+ */
+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);
+
+int dm_pool_metadata_close(struct dm_pool_metadata *pmd);
+
+/*
+ * Compat feature flags. Any incompat flags beyond the ones
+ * specified below will prevent use of the thin metadata.
+ */
+#define THIN_FEATURE_COMPAT_SUPP 0UL
+#define THIN_FEATURE_COMPAT_RO_SUPP 0UL
+#define THIN_FEATURE_INCOMPAT_SUPP 0UL
+
+/*
+ * Device creation/deletion.
+ */
+int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev);
+
+/*
+ * An internal snapshot.
+ *
+ * You can only snapshot a quiesced origin i.e. one that is either
+ * suspended or not instanced at all.
+ */
+int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ dm_thin_id origin);
+
+/*
+ * Deletes a virtual device from the metadata. It _is_ safe to call this
+ * when that device is open. Operations on that device will just start
+ * failing. You still need to call close() on the device.
+ */
+int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
+ dm_thin_id dev);
+
+/*
+ * Commits _all_ metadata changes: device creation, deletion, mapping
+ * updates.
+ */
+int dm_pool_commit_metadata(struct dm_pool_metadata *pmd);
+
+/*
+ * Set/get userspace transaction id.
+ */
+int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t current_id,
+ uint64_t new_id);
+
+int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
+ uint64_t *result);
+
+/*
+ * Hold/get root for userspace transaction.
+ */
+int dm_pool_hold_metadata_root(struct dm_pool_metadata *pmd);
+
+int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+/*
+ * Actions on a single virtual device.
+ */
+
+/*
+ * Opening the same device more than once will fail with -EBUSY.
+ */
+int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
+ struct dm_thin_device **td);
+
+int dm_pool_close_thin_device(struct dm_thin_device *td);
+
+dm_thin_id dm_thin_dev_id(struct dm_thin_device *td);
+
+struct dm_thin_lookup_result {
+ dm_block_t block;
+ int shared;
+};
+
+/*
+ * Returns:
+ * -EWOULDBLOCK iff @can_block is set and would block.
+ * -ENODATA iff that mapping is not present.
+ * 0 success
+ */
+int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
+ int can_block, struct dm_thin_lookup_result *result);
+
+/*
+ * Obtain an unused block.
+ */
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result);
+
+/*
+ * Insert or remove block.
+ */
+int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t data_block);
+
+int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block);
+
+/*
+ * Queries.
+ */
+int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
+ dm_block_t *highest_mapped);
+
+int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result);
+
+int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
+ dm_block_t *result);
+
+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);
+
+/*
+ * Returns -ENOSPC if the new size is too small and already allocated
+ * blocks would be lost.
+ */
+int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
+
+/*----------------------------------------------------------------*/
+
+#endif
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
new file mode 100644
index 000000000000..c3087575fef0
--- /dev/null
+++ b/drivers/md/dm-thin.c
@@ -0,0 +1,2428 @@
+/*
+ * Copyright (C) 2011 Red Hat UK.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-thin-metadata.h"
+
+#include <linux/device-mapper.h>
+#include <linux/dm-io.h>
+#include <linux/dm-kcopyd.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define DM_MSG_PREFIX "thin"
+
+/*
+ * Tunable constants
+ */
+#define ENDIO_HOOK_POOL_SIZE 10240
+#define DEFERRED_SET_SIZE 64
+#define MAPPING_POOL_SIZE 1024
+#define PRISON_CELLS 1024
+
+/*
+ * The block size of the device holding pool data must be
+ * between 64KB and 1GB.
+ */
+#define DATA_DEV_BLOCK_SIZE_MIN_SECTORS (64 * 1024 >> SECTOR_SHIFT)
+#define DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
+
+/*
+ * The metadata device is currently limited in size. The limitation is
+ * checked lower down in dm-space-map-metadata, but we also check it here
+ * so we can fail early.
+ *
+ * We have one block of index, which can hold 255 index entries. Each
+ * index entry contains allocation info about 16k metadata blocks.
+ */
+#define METADATA_DEV_MAX_SECTORS (255 * (1 << 14) * (THIN_METADATA_BLOCK_SIZE / (1 << SECTOR_SHIFT)))
+
+/*
+ * Device id is restricted to 24 bits.
+ */
+#define MAX_DEV_ID ((1 << 24) - 1)
+
+/*
+ * How do we handle breaking sharing of data blocks?
+ * =================================================
+ *
+ * We use a standard copy-on-write btree to store the mappings for the
+ * devices (note I'm talking about copy-on-write of the metadata here, not
+ * the data). When you take an internal snapshot you clone the root node
+ * of the origin btree. After this there is no concept of an origin or a
+ * snapshot. They are just two device trees that happen to point to the
+ * same data blocks.
+ *
+ * When we get a write in we decide if it's to a shared data block using
+ * some timestamp magic. If it is, we have to break sharing.
+ *
+ * Let's say we write to a shared block in what was the origin. The
+ * steps are:
+ *
+ * i) plug io further to this physical block. (see bio_prison code).
+ *
+ * ii) quiesce any read io to that shared data block. Obviously
+ * including all devices that share this block. (see deferred_set code)
+ *
+ * iii) copy the data block to a newly allocate block. This step can be
+ * missed out if the io covers the block. (schedule_copy).
+ *
+ * iv) insert the new mapping into the origin's btree
+ * (process_prepared_mappings). This act of inserting breaks some
+ * sharing of btree nodes between the two devices. Breaking sharing only
+ * effects the btree of that specific device. Btrees for the other
+ * devices that share the block never change. The btree for the origin
+ * device as it was after the last commit is untouched, ie. we're using
+ * persistent data structures in the functional programming sense.
+ *
+ * v) unplug io to this physical block, including the io that triggered
+ * the breaking of sharing.
+ *
+ * Steps (ii) and (iii) occur in parallel.
+ *
+ * The metadata _doesn't_ need to be committed before the io continues. We
+ * get away with this because the io is always written to a _new_ block.
+ * If there's a crash, then:
+ *
+ * - The origin mapping will point to the old origin block (the shared
+ * one). This will contain the data as it was before the io that triggered
+ * the breaking of sharing came in.
+ *
+ * - The snap mapping still points to the old block. As it would after
+ * the commit.
+ *
+ * The downside of this scheme is the timestamp magic isn't perfect, and
+ * will continue to think that data block in the snapshot device is shared
+ * even after the write to the origin has broken sharing. I suspect data
+ * blocks will typically be shared by many different devices, so we're
+ * breaking sharing n + 1 times, rather than n, where n is the number of
+ * devices that reference this data block. At the moment I think the
+ * benefits far, far outweigh the disadvantages.
+ */
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Sometimes we can't deal with a bio straight away. We put them in prison
+ * where they can't cause any mischief. Bios are put in a cell identified
+ * by a key, multiple bios can be in the same cell. When the cell is
+ * subsequently unlocked the bios become available.
+ */
+struct bio_prison;
+
+struct cell_key {
+ int virtual;
+ dm_thin_id dev;
+ dm_block_t block;
+};
+
+struct cell {
+ struct hlist_node list;
+ struct bio_prison *prison;
+ struct cell_key key;
+ unsigned count;
+ struct bio_list bios;
+};
+
+struct bio_prison {
+ spinlock_t lock;
+ mempool_t *cell_pool;
+
+ unsigned nr_buckets;
+ unsigned hash_mask;
+ struct hlist_head *cells;
+};
+
+static uint32_t calc_nr_buckets(unsigned nr_cells)
+{
+ uint32_t n = 128;
+
+ nr_cells /= 4;
+ nr_cells = min(nr_cells, 8192u);
+
+ while (n < nr_cells)
+ n <<= 1;
+
+ return n;
+}
+
+/*
+ * @nr_cells should be the number of cells you want in use _concurrently_.
+ * Don't confuse it with the number of distinct keys.
+ */
+static struct bio_prison *prison_create(unsigned nr_cells)
+{
+ unsigned i;
+ uint32_t nr_buckets = calc_nr_buckets(nr_cells);
+ size_t len = sizeof(struct bio_prison) +
+ (sizeof(struct hlist_head) * nr_buckets);
+ struct bio_prison *prison = kmalloc(len, GFP_KERNEL);
+
+ if (!prison)
+ return NULL;
+
+ spin_lock_init(&prison->lock);
+ prison->cell_pool = mempool_create_kmalloc_pool(nr_cells,
+ sizeof(struct cell));
+ if (!prison->cell_pool) {
+ kfree(prison);
+ return NULL;
+ }
+
+ prison->nr_buckets = nr_buckets;
+ prison->hash_mask = nr_buckets - 1;
+ prison->cells = (struct hlist_head *) (prison + 1);
+ for (i = 0; i < nr_buckets; i++)
+ INIT_HLIST_HEAD(prison->cells + i);
+
+ return prison;
+}
+
+static void prison_destroy(struct bio_prison *prison)
+{
+ mempool_destroy(prison->cell_pool);
+ kfree(prison);
+}
+
+static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key)
+{
+ const unsigned long BIG_PRIME = 4294967291UL;
+ uint64_t hash = key->block * BIG_PRIME;
+
+ return (uint32_t) (hash & prison->hash_mask);
+}
+
+static int keys_equal(struct cell_key *lhs, struct cell_key *rhs)
+{
+ return (lhs->virtual == rhs->virtual) &&
+ (lhs->dev == rhs->dev) &&
+ (lhs->block == rhs->block);
+}
+
+static struct cell *__search_bucket(struct hlist_head *bucket,
+ struct cell_key *key)
+{
+ struct cell *cell;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry(cell, tmp, bucket, list)
+ if (keys_equal(&cell->key, key))
+ return cell;
+
+ return NULL;
+}
+
+/*
+ * This may block if a new cell needs allocating. You must ensure that
+ * cells will be unlocked even if the calling thread is blocked.
+ *
+ * Returns the number of entries in the cell prior to the new addition
+ * or < 0 on failure.
+ */
+static int bio_detain(struct bio_prison *prison, struct cell_key *key,
+ struct bio *inmate, struct cell **ref)
+{
+ int r;
+ unsigned long flags;
+ uint32_t hash = hash_key(prison, key);
+ struct cell *uninitialized_var(cell), *cell2 = NULL;
+
+ BUG_ON(hash > prison->nr_buckets);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ cell = __search_bucket(prison->cells + hash, key);
+
+ if (!cell) {
+ /*
+ * Allocate a new cell
+ */
+ spin_unlock_irqrestore(&prison->lock, flags);
+ cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
+ spin_lock_irqsave(&prison->lock, flags);
+
+ /*
+ * We've been unlocked, so we have to double check that
+ * nobody else has inserted this cell in the meantime.
+ */
+ cell = __search_bucket(prison->cells + hash, key);
+
+ if (!cell) {
+ cell = cell2;
+ cell2 = NULL;
+
+ cell->prison = prison;
+ memcpy(&cell->key, key, sizeof(cell->key));
+ cell->count = 0;
+ bio_list_init(&cell->bios);
+ hlist_add_head(&cell->list, prison->cells + hash);
+ }
+ }
+
+ r = cell->count++;
+ bio_list_add(&cell->bios, inmate);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ if (cell2)
+ mempool_free(cell2, prison->cell_pool);
+
+ *ref = cell;
+
+ return r;
+}
+
+/*
+ * @inmates must have been initialised prior to this call
+ */
+static void __cell_release(struct cell *cell, struct bio_list *inmates)
+{
+ struct bio_prison *prison = cell->prison;
+
+ hlist_del(&cell->list);
+
+ if (inmates)
+ bio_list_merge(inmates, &cell->bios);
+
+ mempool_free(cell, prison->cell_pool);
+}
+
+static void cell_release(struct cell *cell, struct bio_list *bios)
+{
+ unsigned long flags;
+ struct bio_prison *prison = cell->prison;
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+}
+
+/*
+ * There are a couple of places where we put a bio into a cell briefly
+ * before taking it out again. In these situations we know that no other
+ * bio may be in the cell. This function releases the cell, and also does
+ * a sanity check.
+ */
+static void cell_release_singleton(struct cell *cell, struct bio *bio)
+{
+ struct bio_prison *prison = cell->prison;
+ struct bio_list bios;
+ struct bio *b;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, &bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ b = bio_list_pop(&bios);
+ BUG_ON(b != bio);
+ BUG_ON(!bio_list_empty(&bios));
+}
+
+static void cell_error(struct cell *cell)
+{
+ struct bio_prison *prison = cell->prison;
+ struct bio_list bios;
+ struct bio *bio;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(cell, &bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ while ((bio = bio_list_pop(&bios)))
+ bio_io_error(bio);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We use the deferred set to keep track of pending reads to shared blocks.
+ * We do this to ensure the new mapping caused by a write isn't performed
+ * until these prior reads have completed. Otherwise the insertion of the
+ * new mapping could free the old block that the read bios are mapped to.
+ */
+
+struct deferred_set;
+struct deferred_entry {
+ struct deferred_set *ds;
+ unsigned count;
+ struct list_head work_items;
+};
+
+struct deferred_set {
+ spinlock_t lock;
+ unsigned current_entry;
+ unsigned sweeper;
+ struct deferred_entry entries[DEFERRED_SET_SIZE];
+};
+
+static void ds_init(struct deferred_set *ds)
+{
+ int i;
+
+ spin_lock_init(&ds->lock);
+ ds->current_entry = 0;
+ ds->sweeper = 0;
+ for (i = 0; i < DEFERRED_SET_SIZE; i++) {
+ ds->entries[i].ds = ds;
+ ds->entries[i].count = 0;
+ INIT_LIST_HEAD(&ds->entries[i].work_items);
+ }
+}
+
+static struct deferred_entry *ds_inc(struct deferred_set *ds)
+{
+ unsigned long flags;
+ struct deferred_entry *entry;
+
+ spin_lock_irqsave(&ds->lock, flags);
+ entry = ds->entries + ds->current_entry;
+ entry->count++;
+ spin_unlock_irqrestore(&ds->lock, flags);
+
+ return entry;
+}
+
+static unsigned ds_next(unsigned index)
+{
+ return (index + 1) % DEFERRED_SET_SIZE;
+}
+
+static void __sweep(struct deferred_set *ds, struct list_head *head)
+{
+ while ((ds->sweeper != ds->current_entry) &&
+ !ds->entries[ds->sweeper].count) {
+ list_splice_init(&ds->entries[ds->sweeper].work_items, head);
+ ds->sweeper = ds_next(ds->sweeper);
+ }
+
+ if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count)
+ list_splice_init(&ds->entries[ds->sweeper].work_items, head);
+}
+
+static void ds_dec(struct deferred_entry *entry, struct list_head *head)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&entry->ds->lock, flags);
+ BUG_ON(!entry->count);
+ --entry->count;
+ __sweep(entry->ds, head);
+ spin_unlock_irqrestore(&entry->ds->lock, flags);
+}
+
+/*
+ * Returns 1 if deferred or 0 if no pending items to delay job.
+ */
+static int ds_add_work(struct deferred_set *ds, struct list_head *work)
+{
+ int r = 1;
+ unsigned long flags;
+ unsigned next_entry;
+
+ spin_lock_irqsave(&ds->lock, flags);
+ if ((ds->sweeper == ds->current_entry) &&
+ !ds->entries[ds->current_entry].count)
+ r = 0;
+ else {
+ list_add(work, &ds->entries[ds->current_entry].work_items);
+ next_entry = ds_next(ds->current_entry);
+ if (!ds->entries[next_entry].count)
+ ds->current_entry = next_entry;
+ }
+ spin_unlock_irqrestore(&ds->lock, flags);
+
+ return r;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Key building.
+ */
+static void build_data_key(struct dm_thin_device *td,
+ dm_block_t b, struct cell_key *key)
+{
+ key->virtual = 0;
+ key->dev = dm_thin_dev_id(td);
+ key->block = b;
+}
+
+static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
+ struct cell_key *key)
+{
+ key->virtual = 1;
+ key->dev = dm_thin_dev_id(td);
+ key->block = b;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * A pool device ties together a metadata device and a data device. It
+ * also provides the interface for creating and destroying internal
+ * devices.
+ */
+struct new_mapping;
+struct pool {
+ struct list_head list;
+ struct dm_target *ti; /* Only set if a pool target is bound */
+
+ struct mapped_device *pool_md;
+ 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;
+
+ unsigned zero_new_blocks:1;
+ unsigned low_water_triggered:1; /* A dm event has been sent */
+ unsigned no_free_space:1; /* A -ENOSPC warning has been issued */
+
+ struct bio_prison *prison;
+ struct dm_kcopyd_client *copier;
+
+ struct workqueue_struct *wq;
+ struct work_struct worker;
+
+ unsigned ref_count;
+
+ spinlock_t lock;
+ struct bio_list deferred_bios;
+ struct bio_list deferred_flush_bios;
+ struct list_head prepared_mappings;
+
+ struct bio_list retry_on_resume_list;
+
+ struct deferred_set ds; /* FIXME: move to thin_c */
+
+ struct new_mapping *next_mapping;
+ mempool_t *mapping_pool;
+ mempool_t *endio_hook_pool;
+};
+
+/*
+ * Target context for a pool.
+ */
+struct pool_c {
+ struct dm_target *ti;
+ struct pool *pool;
+ struct dm_dev *data_dev;
+ struct dm_dev *metadata_dev;
+ struct dm_target_callbacks callbacks;
+
+ dm_block_t low_water_blocks;
+ unsigned zero_new_blocks:1;
+};
+
+/*
+ * Target context for a thin.
+ */
+struct thin_c {
+ struct dm_dev *pool_dev;
+ dm_thin_id dev_id;
+
+ struct pool *pool;
+ struct dm_thin_device *td;
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * A global list of pools that uses a struct mapped_device as a key.
+ */
+static struct dm_thin_pool_table {
+ struct mutex mutex;
+ struct list_head pools;
+} dm_thin_pool_table;
+
+static void pool_table_init(void)
+{
+ mutex_init(&dm_thin_pool_table.mutex);
+ INIT_LIST_HEAD(&dm_thin_pool_table.pools);
+}
+
+static void __pool_table_insert(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ list_add(&pool->list, &dm_thin_pool_table.pools);
+}
+
+static void __pool_table_remove(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ list_del(&pool->list);
+}
+
+static struct pool *__pool_table_lookup(struct mapped_device *md)
+{
+ struct pool *pool = NULL, *tmp;
+
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+
+ list_for_each_entry(tmp, &dm_thin_pool_table.pools, list) {
+ if (tmp->pool_md == md) {
+ pool = tmp;
+ break;
+ }
+ }
+
+ return pool;
+}
+
+static struct pool *__pool_table_lookup_metadata_dev(struct block_device *md_dev)
+{
+ struct pool *pool = NULL, *tmp;
+
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+
+ list_for_each_entry(tmp, &dm_thin_pool_table.pools, list) {
+ if (tmp->md_dev == md_dev) {
+ pool = tmp;
+ break;
+ }
+ }
+
+ return pool;
+}
+
+/*----------------------------------------------------------------*/
+
+static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ bio_list_merge(&bios, master);
+ bio_list_init(master);
+
+ while ((bio = bio_list_pop(&bios))) {
+ if (dm_get_mapinfo(bio)->ptr == tc)
+ bio_endio(bio, DM_ENDIO_REQUEUE);
+ else
+ bio_list_add(master, bio);
+ }
+}
+
+static void requeue_io(struct thin_c *tc)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ __requeue_bio_list(tc, &pool->deferred_bios);
+ __requeue_bio_list(tc, &pool->retry_on_resume_list);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+/*
+ * This section of code contains the logic for processing a thin device's IO.
+ * Much of the code depends on pool object resources (lists, workqueues, etc)
+ * but most is exclusively called from the thin target rather than the thin-pool
+ * target.
+ */
+
+static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
+{
+ return bio->bi_sector >> tc->pool->block_shift;
+}
+
+static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
+{
+ struct pool *pool = tc->pool;
+
+ bio->bi_bdev = tc->pool_dev->bdev;
+ bio->bi_sector = (block << pool->block_shift) +
+ (bio->bi_sector & pool->offset_mask);
+}
+
+static void remap_and_issue(struct thin_c *tc, struct bio *bio,
+ dm_block_t block)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ remap(tc, bio, block);
+
+ /*
+ * Batch together any FUA/FLUSH bios we find and then issue
+ * a single commit for them in process_deferred_bios().
+ */
+ 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);
+}
+
+/*
+ * wake_worker() is used when new work is queued and when pool_resume is
+ * ready to continue deferred IO processing.
+ */
+static void wake_worker(struct pool *pool)
+{
+ queue_work(pool->wq, &pool->worker);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Bio endio functions.
+ */
+struct endio_hook {
+ struct thin_c *tc;
+ bio_end_io_t *saved_bi_end_io;
+ struct deferred_entry *entry;
+};
+
+struct new_mapping {
+ struct list_head list;
+
+ int prepared;
+
+ struct thin_c *tc;
+ dm_block_t virt_block;
+ dm_block_t data_block;
+ struct cell *cell;
+ int err;
+
+ /*
+ * If the bio covers the whole area of a block then we can avoid
+ * zeroing or copying. Instead this bio is hooked. The bio will
+ * still be in the cell, so care has to be taken to avoid issuing
+ * the bio twice.
+ */
+ struct bio *bio;
+ bio_end_io_t *saved_bi_end_io;
+};
+
+static void __maybe_add_mapping(struct new_mapping *m)
+{
+ struct pool *pool = m->tc->pool;
+
+ if (list_empty(&m->list) && m->prepared) {
+ list_add(&m->list, &pool->prepared_mappings);
+ wake_worker(pool);
+ }
+}
+
+static void copy_complete(int read_err, unsigned long write_err, void *context)
+{
+ unsigned long flags;
+ struct new_mapping *m = context;
+ struct pool *pool = m->tc->pool;
+
+ m->err = read_err || write_err ? -EIO : 0;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ m->prepared = 1;
+ __maybe_add_mapping(m);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void overwrite_endio(struct bio *bio, int err)
+{
+ unsigned long flags;
+ struct new_mapping *m = dm_get_mapinfo(bio)->ptr;
+ struct pool *pool = m->tc->pool;
+
+ m->err = err;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ m->prepared = 1;
+ __maybe_add_mapping(m);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void shared_read_endio(struct bio *bio, int err)
+{
+ struct list_head mappings;
+ struct new_mapping *m, *tmp;
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ unsigned long flags;
+ struct pool *pool = h->tc->pool;
+
+ bio->bi_end_io = h->saved_bi_end_io;
+ bio_endio(bio, err);
+
+ INIT_LIST_HEAD(&mappings);
+ ds_dec(h->entry, &mappings);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ list_for_each_entry_safe(m, tmp, &mappings, list) {
+ list_del(&m->list);
+ INIT_LIST_HEAD(&m->list);
+ __maybe_add_mapping(m);
+ }
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ mempool_free(h, pool->endio_hook_pool);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Workqueue.
+ */
+
+/*
+ * Prepared mapping jobs.
+ */
+
+/*
+ * This sends the bios in the cell back to the deferred_bios list.
+ */
+static void cell_defer(struct thin_c *tc, struct cell *cell,
+ dm_block_t data_block)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ cell_release(cell, &pool->deferred_bios);
+ spin_unlock_irqrestore(&tc->pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+/*
+ * Same as cell_defer above, except it omits one particular detainee,
+ * a write bio that covers the block and has already been processed.
+ */
+static void cell_defer_except(struct thin_c *tc, struct cell *cell,
+ struct bio *exception)
+{
+ struct bio_list bios;
+ struct bio *bio;
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ bio_list_init(&bios);
+ cell_release(cell, &bios);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ while ((bio = bio_list_pop(&bios)))
+ if (bio != exception)
+ bio_list_add(&pool->deferred_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+static void process_prepared_mapping(struct new_mapping *m)
+{
+ struct thin_c *tc = m->tc;
+ struct bio *bio;
+ int r;
+
+ bio = m->bio;
+ if (bio)
+ bio->bi_end_io = m->saved_bi_end_io;
+
+ if (m->err) {
+ cell_error(m->cell);
+ return;
+ }
+
+ /*
+ * Commit the prepared block into the mapping btree.
+ * Any I/O for this block arriving after this point will get
+ * remapped to it directly.
+ */
+ r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block);
+ if (r) {
+ DMERR("dm_thin_insert_block() failed");
+ cell_error(m->cell);
+ return;
+ }
+
+ /*
+ * Release any bios held while the block was being provisioned.
+ * If we are processing a write bio that completely covers the block,
+ * we already processed it so can ignore it now when processing
+ * the bios in the cell.
+ */
+ if (bio) {
+ cell_defer_except(tc, m->cell, bio);
+ bio_endio(bio, 0);
+ } else
+ cell_defer(tc, m->cell, m->data_block);
+
+ list_del(&m->list);
+ mempool_free(m, tc->pool->mapping_pool);
+}
+
+static void process_prepared_mappings(struct pool *pool)
+{
+ unsigned long flags;
+ struct list_head maps;
+ struct new_mapping *m, *tmp;
+
+ INIT_LIST_HEAD(&maps);
+ spin_lock_irqsave(&pool->lock, flags);
+ list_splice_init(&pool->prepared_mappings, &maps);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ list_for_each_entry_safe(m, tmp, &maps, list)
+ process_prepared_mapping(m);
+}
+
+/*
+ * Deferred bio jobs.
+ */
+static int io_overwrites_block(struct pool *pool, struct bio *bio)
+{
+ return ((bio_data_dir(bio) == WRITE) &&
+ !(bio->bi_sector & pool->offset_mask)) &&
+ (bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT));
+}
+
+static void save_and_set_endio(struct bio *bio, bio_end_io_t **save,
+ bio_end_io_t *fn)
+{
+ *save = bio->bi_end_io;
+ bio->bi_end_io = fn;
+}
+
+static int ensure_next_mapping(struct pool *pool)
+{
+ if (pool->next_mapping)
+ return 0;
+
+ pool->next_mapping = mempool_alloc(pool->mapping_pool, GFP_ATOMIC);
+
+ return pool->next_mapping ? 0 : -ENOMEM;
+}
+
+static struct new_mapping *get_next_mapping(struct pool *pool)
+{
+ struct new_mapping *r = pool->next_mapping;
+
+ BUG_ON(!pool->next_mapping);
+
+ pool->next_mapping = NULL;
+
+ return r;
+}
+
+static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_origin, dm_block_t data_dest,
+ struct cell *cell, struct bio *bio)
+{
+ int r;
+ struct pool *pool = tc->pool;
+ struct new_mapping *m = get_next_mapping(pool);
+
+ INIT_LIST_HEAD(&m->list);
+ m->prepared = 0;
+ m->tc = tc;
+ m->virt_block = virt_block;
+ m->data_block = data_dest;
+ m->cell = cell;
+ m->err = 0;
+ m->bio = NULL;
+
+ ds_add_work(&pool->ds, &m->list);
+
+ /*
+ * IO to pool_dev remaps to the pool target's data_dev.
+ *
+ * If the whole block of data is being overwritten, we can issue the
+ * bio immediately. Otherwise we use kcopyd to clone the data first.
+ */
+ if (io_overwrites_block(pool, bio)) {
+ m->bio = bio;
+ save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
+ dm_get_mapinfo(bio)->ptr = m;
+ remap_and_issue(tc, bio, data_dest);
+ } else {
+ struct dm_io_region from, to;
+
+ from.bdev = tc->pool_dev->bdev;
+ from.sector = data_origin * pool->sectors_per_block;
+ from.count = pool->sectors_per_block;
+
+ to.bdev = tc->pool_dev->bdev;
+ to.sector = data_dest * pool->sectors_per_block;
+ to.count = pool->sectors_per_block;
+
+ r = dm_kcopyd_copy(pool->copier, &from, 1, &to,
+ 0, copy_complete, m);
+ if (r < 0) {
+ mempool_free(m, pool->mapping_pool);
+ DMERR("dm_kcopyd_copy() failed");
+ cell_error(cell);
+ }
+ }
+}
+
+static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_block, struct cell *cell,
+ struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+ struct new_mapping *m = get_next_mapping(pool);
+
+ INIT_LIST_HEAD(&m->list);
+ m->prepared = 0;
+ m->tc = tc;
+ m->virt_block = virt_block;
+ m->data_block = data_block;
+ m->cell = cell;
+ m->err = 0;
+ m->bio = NULL;
+
+ /*
+ * If the whole block of data is being overwritten or we are not
+ * zeroing pre-existing data, we can issue the bio immediately.
+ * Otherwise we use kcopyd to zero the data first.
+ */
+ if (!pool->zero_new_blocks)
+ process_prepared_mapping(m);
+
+ else if (io_overwrites_block(pool, bio)) {
+ m->bio = bio;
+ save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
+ dm_get_mapinfo(bio)->ptr = m;
+ remap_and_issue(tc, bio, data_block);
+
+ } else {
+ int r;
+ struct dm_io_region to;
+
+ to.bdev = tc->pool_dev->bdev;
+ to.sector = data_block * pool->sectors_per_block;
+ to.count = pool->sectors_per_block;
+
+ r = dm_kcopyd_zero(pool->copier, 1, &to, 0, copy_complete, m);
+ if (r < 0) {
+ mempool_free(m, pool->mapping_pool);
+ DMERR("dm_kcopyd_zero() failed");
+ cell_error(cell);
+ }
+ }
+}
+
+static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
+{
+ int r;
+ dm_block_t free_blocks;
+ unsigned long flags;
+ struct pool *pool = tc->pool;
+
+ r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
+ if (r)
+ return r;
+
+ if (free_blocks <= pool->low_water_blocks && !pool->low_water_triggered) {
+ DMWARN("%s: reached low water mark, sending event.",
+ dm_device_name(pool->pool_md));
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->low_water_triggered = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+ dm_table_event(pool->ti->table);
+ }
+
+ if (!free_blocks) {
+ if (pool->no_free_space)
+ return -ENOSPC;
+ else {
+ /*
+ * 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;
+ }
+
+ r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
+ if (r)
+ return r;
+
+ /*
+ * If we still have no space we set a flag to avoid
+ * doing all this checking and return -ENOSPC.
+ */
+ if (!free_blocks) {
+ DMWARN("%s: no free space available.",
+ dm_device_name(pool->pool_md));
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->no_free_space = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return -ENOSPC;
+ }
+ }
+ }
+
+ r = dm_pool_alloc_data_block(pool->pmd, result);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+/*
+ * If we have run out of space, queue bios until the device is
+ * resumed, presumably after having been reloaded with more space.
+ */
+static void retry_on_resume(struct bio *bio)
+{
+ struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->retry_on_resume_list, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static void no_space(struct cell *cell)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ cell_release(cell, &bios);
+
+ while ((bio = bio_list_pop(&bios)))
+ retry_on_resume(bio);
+}
+
+static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
+ struct cell_key *key,
+ struct dm_thin_lookup_result *lookup_result,
+ struct cell *cell)
+{
+ int r;
+ dm_block_t data_block;
+
+ r = alloc_data_block(tc, &data_block);
+ switch (r) {
+ case 0:
+ schedule_copy(tc, block, lookup_result->block,
+ data_block, cell, bio);
+ break;
+
+ case -ENOSPC:
+ no_space(cell);
+ break;
+
+ default:
+ DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+ cell_error(cell);
+ break;
+ }
+}
+
+static void process_shared_bio(struct thin_c *tc, struct bio *bio,
+ dm_block_t block,
+ struct dm_thin_lookup_result *lookup_result)
+{
+ struct cell *cell;
+ struct pool *pool = tc->pool;
+ struct cell_key key;
+
+ /*
+ * If cell is already occupied, then sharing is already in the process
+ * of being broken so we have nothing further to do here.
+ */
+ build_data_key(tc->td, lookup_result->block, &key);
+ if (bio_detain(pool->prison, &key, bio, &cell))
+ return;
+
+ if (bio_data_dir(bio) == WRITE)
+ break_sharing(tc, bio, block, &key, lookup_result, cell);
+ else {
+ struct endio_hook *h;
+ h = mempool_alloc(pool->endio_hook_pool, GFP_NOIO);
+
+ h->tc = tc;
+ h->entry = ds_inc(&pool->ds);
+ save_and_set_endio(bio, &h->saved_bi_end_io, shared_read_endio);
+ dm_get_mapinfo(bio)->ptr = h;
+
+ cell_release_singleton(cell, bio);
+ remap_and_issue(tc, bio, lookup_result->block);
+ }
+}
+
+static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block,
+ struct cell *cell)
+{
+ int r;
+ dm_block_t data_block;
+
+ /*
+ * Remap empty bios (flushes) immediately, without provisioning.
+ */
+ if (!bio->bi_size) {
+ cell_release_singleton(cell, bio);
+ remap_and_issue(tc, bio, 0);
+ return;
+ }
+
+ /*
+ * Fill read bios with zeroes and complete them immediately.
+ */
+ if (bio_data_dir(bio) == READ) {
+ zero_fill_bio(bio);
+ cell_release_singleton(cell, bio);
+ bio_endio(bio, 0);
+ return;
+ }
+
+ r = alloc_data_block(tc, &data_block);
+ switch (r) {
+ case 0:
+ schedule_zero(tc, block, data_block, cell, bio);
+ break;
+
+ case -ENOSPC:
+ no_space(cell);
+ break;
+
+ default:
+ DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+ cell_error(cell);
+ break;
+ }
+}
+
+static void process_bio(struct thin_c *tc, struct bio *bio)
+{
+ int r;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct cell *cell;
+ struct cell_key key;
+ struct dm_thin_lookup_result lookup_result;
+
+ /*
+ * If cell is already occupied, then the block is already
+ * being provisioned so we have nothing further to do here.
+ */
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(tc->pool->prison, &key, bio, &cell))
+ return;
+
+ r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
+ switch (r) {
+ case 0:
+ /*
+ * We can release this cell now. This thread is the only
+ * one that puts bios into a cell, and we know there were
+ * no preceding bios.
+ */
+ /*
+ * TODO: this will probably have to change when discard goes
+ * back in.
+ */
+ cell_release_singleton(cell, bio);
+
+ if (lookup_result.shared)
+ process_shared_bio(tc, bio, block, &lookup_result);
+ else
+ remap_and_issue(tc, bio, lookup_result.block);
+ break;
+
+ case -ENODATA:
+ provision_block(tc, bio, block, cell);
+ break;
+
+ default:
+ DMERR("dm_thin_find_block() failed, error = %d", r);
+ bio_io_error(bio);
+ break;
+ }
+}
+
+static void process_deferred_bios(struct pool *pool)
+{
+ unsigned long flags;
+ struct bio *bio;
+ struct bio_list bios;
+ int r;
+
+ bio_list_init(&bios);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&bios, &pool->deferred_bios);
+ bio_list_init(&pool->deferred_bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ while ((bio = bio_list_pop(&bios))) {
+ struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ /*
+ * If we've got no free new_mapping structs, and processing
+ * this bio might require one, we pause until there are some
+ * prepared mappings to process.
+ */
+ if (ensure_next_mapping(pool)) {
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&pool->deferred_bios, &bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ break;
+ }
+ process_bio(tc, bio);
+ }
+
+ /*
+ * If there are any deferred flush bios, we must commit
+ * the metadata before issuing them.
+ */
+ bio_list_init(&bios);
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_merge(&bios, &pool->deferred_flush_bios);
+ bio_list_init(&pool->deferred_flush_bios);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ if (bio_list_empty(&bios))
+ return;
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ while ((bio = bio_list_pop(&bios)))
+ bio_io_error(bio);
+ return;
+ }
+
+ while ((bio = bio_list_pop(&bios)))
+ generic_make_request(bio);
+}
+
+static void do_worker(struct work_struct *ws)
+{
+ struct pool *pool = container_of(ws, struct pool, worker);
+
+ process_prepared_mappings(pool);
+ process_deferred_bios(pool);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Mapping functions.
+ */
+
+/*
+ * Called only while mapping a thin bio to hand it over to the workqueue.
+ */
+static void thin_defer_bio(struct thin_c *tc, struct bio *bio)
+{
+ unsigned long flags;
+ struct pool *pool = tc->pool;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->deferred_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+/*
+ * Non-blocking function called from the thin target's map function.
+ */
+static int thin_bio_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ int r;
+ struct thin_c *tc = ti->private;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct dm_thin_device *td = tc->td;
+ struct dm_thin_lookup_result result;
+
+ /*
+ * Save the thin context for easy access from the deferred bio later.
+ */
+ map_context->ptr = tc;
+
+ if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
+ thin_defer_bio(tc, bio);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ r = dm_thin_find_block(td, block, 0, &result);
+
+ /*
+ * Note that we defer readahead too.
+ */
+ switch (r) {
+ case 0:
+ if (unlikely(result.shared)) {
+ /*
+ * We have a race condition here between the
+ * result.shared value returned by the lookup and
+ * snapshot creation, which may cause new
+ * sharing.
+ *
+ * To avoid this always quiesce the origin before
+ * taking the snap. You want to do this anyway to
+ * ensure a consistent application view
+ * (i.e. lockfs).
+ *
+ * More distant ancestors are irrelevant. The
+ * shared flag will be set in their case.
+ */
+ thin_defer_bio(tc, bio);
+ r = DM_MAPIO_SUBMITTED;
+ } else {
+ remap(tc, bio, result.block);
+ r = DM_MAPIO_REMAPPED;
+ }
+ break;
+
+ case -ENODATA:
+ /*
+ * 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;
+ }
+
+ return r;
+}
+
+static int pool_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
+{
+ int r;
+ unsigned long flags;
+ struct pool_c *pt = container_of(cb, struct pool_c, callbacks);
+
+ spin_lock_irqsave(&pt->pool->lock, flags);
+ r = !bio_list_empty(&pt->pool->retry_on_resume_list);
+ spin_unlock_irqrestore(&pt->pool->lock, flags);
+
+ if (!r) {
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+ r = bdi_congested(&q->backing_dev_info, bdi_bits);
+ }
+
+ return r;
+}
+
+static void __requeue_bios(struct pool *pool)
+{
+ bio_list_merge(&pool->deferred_bios, &pool->retry_on_resume_list);
+ bio_list_init(&pool->retry_on_resume_list);
+}
+
+/*----------------------------------------------------------------
+ * Binding of control targets to a pool object
+ *--------------------------------------------------------------*/
+static int bind_control_target(struct pool *pool, struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+
+ pool->ti = ti;
+ pool->low_water_blocks = pt->low_water_blocks;
+ pool->zero_new_blocks = pt->zero_new_blocks;
+
+ return 0;
+}
+
+static void unbind_control_target(struct pool *pool, struct dm_target *ti)
+{
+ if (pool->ti == ti)
+ pool->ti = NULL;
+}
+
+/*----------------------------------------------------------------
+ * Pool creation
+ *--------------------------------------------------------------*/
+static void __pool_destroy(struct pool *pool)
+{
+ __pool_table_remove(pool);
+
+ if (dm_pool_metadata_close(pool->pmd) < 0)
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+
+ prison_destroy(pool->prison);
+ dm_kcopyd_client_destroy(pool->copier);
+
+ if (pool->wq)
+ destroy_workqueue(pool->wq);
+
+ if (pool->next_mapping)
+ mempool_free(pool->next_mapping, pool->mapping_pool);
+ mempool_destroy(pool->mapping_pool);
+ mempool_destroy(pool->endio_hook_pool);
+ kfree(pool);
+}
+
+static struct pool *pool_create(struct mapped_device *pool_md,
+ struct block_device *metadata_dev,
+ unsigned long block_size, char **error)
+{
+ int r;
+ void *err_p;
+ struct pool *pool;
+ struct dm_pool_metadata *pmd;
+
+ pmd = dm_pool_metadata_open(metadata_dev, block_size);
+ if (IS_ERR(pmd)) {
+ *error = "Error creating metadata object";
+ return (struct pool *)pmd;
+ }
+
+ pool = kmalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool) {
+ *error = "Error allocating memory for pool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_pool;
+ }
+
+ pool->pmd = pmd;
+ pool->sectors_per_block = block_size;
+ pool->block_shift = ffs(block_size) - 1;
+ pool->offset_mask = block_size - 1;
+ pool->low_water_blocks = 0;
+ pool->zero_new_blocks = 1;
+ pool->prison = prison_create(PRISON_CELLS);
+ if (!pool->prison) {
+ *error = "Error creating pool's bio prison";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_prison;
+ }
+
+ pool->copier = dm_kcopyd_client_create();
+ if (IS_ERR(pool->copier)) {
+ r = PTR_ERR(pool->copier);
+ *error = "Error creating pool's kcopyd client";
+ err_p = ERR_PTR(r);
+ goto bad_kcopyd_client;
+ }
+
+ /*
+ * Create singlethreaded workqueue that will service all devices
+ * that use this metadata.
+ */
+ pool->wq = alloc_ordered_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM);
+ if (!pool->wq) {
+ *error = "Error creating pool's workqueue";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_wq;
+ }
+
+ INIT_WORK(&pool->worker, do_worker);
+ spin_lock_init(&pool->lock);
+ bio_list_init(&pool->deferred_bios);
+ bio_list_init(&pool->deferred_flush_bios);
+ INIT_LIST_HEAD(&pool->prepared_mappings);
+ pool->low_water_triggered = 0;
+ pool->no_free_space = 0;
+ bio_list_init(&pool->retry_on_resume_list);
+ ds_init(&pool->ds);
+
+ pool->next_mapping = NULL;
+ pool->mapping_pool =
+ mempool_create_kmalloc_pool(MAPPING_POOL_SIZE, sizeof(struct new_mapping));
+ if (!pool->mapping_pool) {
+ *error = "Error creating pool's mapping mempool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_mapping_pool;
+ }
+
+ pool->endio_hook_pool =
+ mempool_create_kmalloc_pool(ENDIO_HOOK_POOL_SIZE, sizeof(struct endio_hook));
+ if (!pool->endio_hook_pool) {
+ *error = "Error creating pool's endio_hook mempool";
+ err_p = ERR_PTR(-ENOMEM);
+ goto bad_endio_hook_pool;
+ }
+ pool->ref_count = 1;
+ pool->pool_md = pool_md;
+ pool->md_dev = metadata_dev;
+ __pool_table_insert(pool);
+
+ return pool;
+
+bad_endio_hook_pool:
+ mempool_destroy(pool->mapping_pool);
+bad_mapping_pool:
+ destroy_workqueue(pool->wq);
+bad_wq:
+ dm_kcopyd_client_destroy(pool->copier);
+bad_kcopyd_client:
+ prison_destroy(pool->prison);
+bad_prison:
+ kfree(pool);
+bad_pool:
+ if (dm_pool_metadata_close(pmd))
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+
+ return err_p;
+}
+
+static void __pool_inc(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ pool->ref_count++;
+}
+
+static void __pool_dec(struct pool *pool)
+{
+ BUG_ON(!mutex_is_locked(&dm_thin_pool_table.mutex));
+ BUG_ON(!pool->ref_count);
+ if (!--pool->ref_count)
+ __pool_destroy(pool);
+}
+
+static struct pool *__pool_find(struct mapped_device *pool_md,
+ struct block_device *metadata_dev,
+ unsigned long block_size, char **error)
+{
+ struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
+
+ if (pool) {
+ if (pool->pool_md != pool_md)
+ return ERR_PTR(-EBUSY);
+ __pool_inc(pool);
+
+ } else {
+ pool = __pool_table_lookup(pool_md);
+ if (pool) {
+ if (pool->md_dev != metadata_dev)
+ return ERR_PTR(-EINVAL);
+ __pool_inc(pool);
+
+ } else
+ pool = pool_create(pool_md, metadata_dev, block_size, error);
+ }
+
+ return pool;
+}
+
+/*----------------------------------------------------------------
+ * Pool target methods
+ *--------------------------------------------------------------*/
+static void pool_dtr(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ unbind_control_target(pt->pool, ti);
+ __pool_dec(pt->pool);
+ dm_put_device(ti, pt->metadata_dev);
+ dm_put_device(ti, pt->data_dev);
+ kfree(pt);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+}
+
+struct pool_features {
+ unsigned zero_new_blocks:1;
+};
+
+static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
+ struct dm_target *ti)
+{
+ int r;
+ unsigned argc;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, 1, "Invalid number of pool feature arguments"},
+ };
+
+ /*
+ * No feature arguments supplied.
+ */
+ if (!as->argc)
+ return 0;
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return -EINVAL;
+
+ while (argc && !r) {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ if (!strcasecmp(arg_name, "skip_block_zeroing")) {
+ pf->zero_new_blocks = 0;
+ continue;
+ }
+
+ ti->error = "Unrecognised pool feature requested";
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+/*
+ * thin-pool <metadata dev> <data dev>
+ * <data block size (sectors)>
+ * <low water mark (blocks)>
+ * [<#feature args> [<arg>]*]
+ *
+ * Optional feature arguments are:
+ * skip_block_zeroing: skips the zeroing of newly-provisioned blocks.
+ */
+static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ struct pool_c *pt;
+ struct pool *pool;
+ struct pool_features pf;
+ struct dm_arg_set as;
+ struct dm_dev *data_dev;
+ unsigned long block_size;
+ dm_block_t low_water_blocks;
+ struct dm_dev *metadata_dev;
+ sector_t metadata_dev_size;
+
+ /*
+ * FIXME Remove validation from scope of lock.
+ */
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ if (argc < 4) {
+ ti->error = "Invalid argument count";
+ r = -EINVAL;
+ goto out_unlock;
+ }
+ as.argc = argc;
+ as.argv = argv;
+
+ r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &metadata_dev);
+ if (r) {
+ ti->error = "Error opening metadata block device";
+ goto out_unlock;
+ }
+
+ metadata_dev_size = i_size_read(metadata_dev->bdev->bd_inode) >> SECTOR_SHIFT;
+ if (metadata_dev_size > METADATA_DEV_MAX_SECTORS) {
+ ti->error = "Metadata device is too large";
+ r = -EINVAL;
+ goto out_metadata;
+ }
+
+ r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &data_dev);
+ if (r) {
+ ti->error = "Error getting data device";
+ goto out_metadata;
+ }
+
+ 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)) {
+ ti->error = "Invalid block size";
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (kstrtoull(argv[3], 10, (unsigned long long *)&low_water_blocks)) {
+ ti->error = "Invalid low water mark";
+ r = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Set default pool features.
+ */
+ memset(&pf, 0, sizeof(pf));
+ pf.zero_new_blocks = 1;
+
+ dm_consume_args(&as, 4);
+ r = parse_pool_features(&as, &pf, ti);
+ if (r)
+ goto out;
+
+ pt = kzalloc(sizeof(*pt), GFP_KERNEL);
+ if (!pt) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
+ block_size, &ti->error);
+ if (IS_ERR(pool)) {
+ r = PTR_ERR(pool);
+ goto out_free_pt;
+ }
+
+ pt->pool = pool;
+ pt->ti = ti;
+ pt->metadata_dev = metadata_dev;
+ pt->data_dev = data_dev;
+ pt->low_water_blocks = low_water_blocks;
+ pt->zero_new_blocks = pf.zero_new_blocks;
+ ti->num_flush_requests = 1;
+ ti->num_discard_requests = 0;
+ ti->private = pt;
+
+ pt->callbacks.congested_fn = pool_is_congested;
+ dm_table_add_target_callbacks(ti->table, &pt->callbacks);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return 0;
+
+out_free_pt:
+ kfree(pt);
+out:
+ dm_put_device(ti, data_dev);
+out_metadata:
+ dm_put_device(ti, metadata_dev);
+out_unlock:
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return r;
+}
+
+static int pool_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ /*
+ * As this is a singleton target, ti->begin is always zero.
+ */
+ spin_lock_irqsave(&pool->lock, flags);
+ bio->bi_bdev = pt->data_dev->bdev;
+ r = DM_MAPIO_REMAPPED;
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ return r;
+}
+
+/*
+ * Retrieves the number of blocks of the data device from
+ * the superblock and compares it to the actual device size,
+ * thus resizing the data device in case it has grown.
+ *
+ * This both copes with opening preallocated data devices in the ctr
+ * being followed by a resume
+ * -and-
+ * calling the resume method individually after userspace has
+ * grown the data device in reaction to a table event.
+ */
+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;
+
+ /*
+ * Take control of the pool object.
+ */
+ r = bind_control_target(pool, ti);
+ if (r)
+ return r;
+
+ data_size = ti->len >> pool->block_shift;
+ r = dm_pool_get_data_dev_size(pool->pmd, &sb_data_size);
+ if (r) {
+ DMERR("failed to retrieve data device size");
+ return r;
+ }
+
+ if (data_size < sb_data_size) {
+ DMERR("pool target too small, is %llu blocks (expected %llu)",
+ 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");
+ return r;
+ }
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r) {
+ DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
+ __func__, r);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static void pool_resume(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->low_water_triggered = 0;
+ pool->no_free_space = 0;
+ __requeue_bios(pool);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ wake_worker(pool);
+}
+
+static void pool_postsuspend(struct dm_target *ti)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ 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 ?*/
+ }
+}
+
+static int check_arg_count(unsigned argc, unsigned args_required)
+{
+ if (argc != args_required) {
+ DMWARN("Message received with %u arguments instead of %u.",
+ argc, args_required);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int read_dev_id(char *arg, dm_thin_id *dev_id, int warning)
+{
+ if (!kstrtoull(arg, 10, (unsigned long long *)dev_id) &&
+ *dev_id <= MAX_DEV_ID)
+ return 0;
+
+ if (warning)
+ DMWARN("Message received with invalid device id: %s", arg);
+
+ return -EINVAL;
+}
+
+static int process_create_thin_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ int r;
+
+ r = check_arg_count(argc, 2);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_create_thin(pool->pmd, dev_id);
+ if (r) {
+ DMWARN("Creation of new thinly-provisioned device with id %s failed.",
+ argv[1]);
+ return r;
+ }
+
+ return 0;
+}
+
+static int process_create_snap_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ dm_thin_id origin_dev_id;
+ int r;
+
+ r = check_arg_count(argc, 3);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[2], &origin_dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_create_snap(pool->pmd, dev_id, origin_dev_id);
+ if (r) {
+ DMWARN("Creation of new snapshot %s of device %s failed.",
+ argv[1], argv[2]);
+ return r;
+ }
+
+ return 0;
+}
+
+static int process_delete_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id dev_id;
+ int r;
+
+ r = check_arg_count(argc, 2);
+ if (r)
+ return r;
+
+ r = read_dev_id(argv[1], &dev_id, 1);
+ if (r)
+ return r;
+
+ r = dm_pool_delete_thin_device(pool->pmd, dev_id);
+ if (r)
+ DMWARN("Deletion of thin device %s failed.", argv[1]);
+
+ return r;
+}
+
+static int process_set_transaction_id_mesg(unsigned argc, char **argv, struct pool *pool)
+{
+ dm_thin_id old_id, new_id;
+ int r;
+
+ r = check_arg_count(argc, 3);
+ if (r)
+ return r;
+
+ if (kstrtoull(argv[1], 10, (unsigned long long *)&old_id)) {
+ DMWARN("set_transaction_id message: Unrecognised id %s.", argv[1]);
+ return -EINVAL;
+ }
+
+ if (kstrtoull(argv[2], 10, (unsigned long long *)&new_id)) {
+ DMWARN("set_transaction_id message: Unrecognised new id %s.", argv[2]);
+ return -EINVAL;
+ }
+
+ r = dm_pool_set_metadata_transaction_id(pool->pmd, old_id, new_id);
+ if (r) {
+ DMWARN("Failed to change transaction id from %s to %s.",
+ argv[1], argv[2]);
+ return r;
+ }
+
+ return 0;
+}
+
+/*
+ * Messages supported:
+ * create_thin <dev_id>
+ * create_snap <dev_id> <origin_id>
+ * delete <dev_id>
+ * trim <dev_id> <new_size_in_sectors>
+ * set_transaction_id <current_trans_id> <new_trans_id>
+ */
+static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r = -EINVAL;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ if (!strcasecmp(argv[0], "create_thin"))
+ r = process_create_thin_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "create_snap"))
+ r = process_create_snap_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "delete"))
+ r = process_delete_mesg(argc, argv, pool);
+
+ else if (!strcasecmp(argv[0], "set_transaction_id"))
+ r = process_set_transaction_id_mesg(argc, argv, pool);
+
+ 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);
+ }
+
+ return r;
+}
+
+/*
+ * 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)
+{
+ int r;
+ unsigned sz = 0;
+ uint64_t transaction_id;
+ dm_block_t nr_free_blocks_data;
+ dm_block_t nr_free_blocks_metadata;
+ dm_block_t nr_blocks_data;
+ dm_block_t nr_blocks_metadata;
+ dm_block_t held_root;
+ char buf[BDEVNAME_SIZE];
+ char buf2[BDEVNAME_SIZE];
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = dm_pool_get_metadata_transaction_id(pool->pmd,
+ &transaction_id);
+ if (r)
+ return r;
+
+ r = dm_pool_get_free_metadata_block_count(pool->pmd,
+ &nr_free_blocks_metadata);
+ if (r)
+ return r;
+
+ r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata);
+ if (r)
+ return r;
+
+ r = dm_pool_get_free_block_count(pool->pmd,
+ &nr_free_blocks_data);
+ if (r)
+ return r;
+
+ r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data);
+ if (r)
+ return r;
+
+ r = dm_pool_get_held_metadata_root(pool->pmd, &held_root);
+ if (r)
+ return r;
+
+ DMEMIT("%llu %llu/%llu %llu/%llu ",
+ (unsigned long long)transaction_id,
+ (unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata),
+ (unsigned long long)nr_blocks_metadata,
+ (unsigned long long)(nr_blocks_data - nr_free_blocks_data),
+ (unsigned long long)nr_blocks_data);
+
+ if (held_root)
+ DMEMIT("%llu", held_root);
+ else
+ DMEMIT("-");
+
+ break;
+
+ case STATUSTYPE_TABLE:
+ DMEMIT("%s %s %lu %llu ",
+ format_dev_t(buf, pt->metadata_dev->bdev->bd_dev),
+ format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
+ (unsigned long)pool->sectors_per_block,
+ (unsigned long long)pt->low_water_blocks);
+
+ DMEMIT("%u ", !pool->zero_new_blocks);
+
+ if (!pool->zero_new_blocks)
+ DMEMIT("skip_block_zeroing ");
+ break;
+ }
+
+ return 0;
+}
+
+static int pool_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct pool_c *pt = ti->private;
+
+ return fn(ti, pt->data_dev, 0, ti->len, data);
+}
+
+static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct pool_c *pt = ti->private;
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = pt->data_dev->bdev;
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
+}
+
+static struct target_type pool_target = {
+ .name = "thin-pool",
+ .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
+ DM_TARGET_IMMUTABLE,
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = pool_ctr,
+ .dtr = pool_dtr,
+ .map = pool_map,
+ .postsuspend = pool_postsuspend,
+ .preresume = pool_preresume,
+ .resume = pool_resume,
+ .message = pool_message,
+ .status = pool_status,
+ .merge = pool_merge,
+ .iterate_devices = pool_iterate_devices,
+ .io_hints = pool_io_hints,
+};
+
+/*----------------------------------------------------------------
+ * Thin target methods
+ *--------------------------------------------------------------*/
+static void thin_dtr(struct dm_target *ti)
+{
+ struct thin_c *tc = ti->private;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ __pool_dec(tc->pool);
+ dm_pool_close_thin_device(tc->td);
+ dm_put_device(ti, tc->pool_dev);
+ kfree(tc);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+}
+
+/*
+ * Thin target parameters:
+ *
+ * <pool_dev> <dev_id>
+ *
+ * pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
+ * dev_id: the internal device identifier
+ */
+static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ struct thin_c *tc;
+ struct dm_dev *pool_dev;
+ struct mapped_device *pool_md;
+
+ mutex_lock(&dm_thin_pool_table.mutex);
+
+ if (argc != 2) {
+ ti->error = "Invalid argument count";
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
+ tc = ti->private = kzalloc(sizeof(*tc), GFP_KERNEL);
+ if (!tc) {
+ ti->error = "Out of memory";
+ r = -ENOMEM;
+ goto out_unlock;
+ }
+
+ r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &pool_dev);
+ if (r) {
+ ti->error = "Error opening pool device";
+ goto bad_pool_dev;
+ }
+ tc->pool_dev = pool_dev;
+
+ if (read_dev_id(argv[1], (unsigned long long *)&tc->dev_id, 0)) {
+ ti->error = "Invalid device id";
+ r = -EINVAL;
+ goto bad_common;
+ }
+
+ pool_md = dm_get_md(tc->pool_dev->bdev->bd_dev);
+ if (!pool_md) {
+ ti->error = "Couldn't get pool mapped device";
+ r = -EINVAL;
+ goto bad_common;
+ }
+
+ tc->pool = __pool_table_lookup(pool_md);
+ if (!tc->pool) {
+ ti->error = "Couldn't find pool object";
+ r = -EINVAL;
+ goto bad_pool_lookup;
+ }
+ __pool_inc(tc->pool);
+
+ 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;
+ ti->num_flush_requests = 1;
+ ti->num_discard_requests = 0;
+ ti->discards_supported = 0;
+
+ dm_put(pool_md);
+
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return 0;
+
+bad_thin_open:
+ __pool_dec(tc->pool);
+bad_pool_lookup:
+ dm_put(pool_md);
+bad_common:
+ dm_put_device(ti, tc->pool_dev);
+bad_pool_dev:
+ kfree(tc);
+out_unlock:
+ mutex_unlock(&dm_thin_pool_table.mutex);
+
+ return r;
+}
+
+static int thin_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ bio->bi_sector -= ti->begin;
+
+ return thin_bio_map(ti, bio, map_context);
+}
+
+static void thin_postsuspend(struct dm_target *ti)
+{
+ if (dm_noflush_suspending(ti))
+ requeue_io((struct thin_c *)ti->private);
+}
+
+/*
+ * <nr mapped sectors> <highest mapped sector>
+ */
+static int thin_status(struct dm_target *ti, status_type_t type,
+ char *result, unsigned maxlen)
+{
+ int r;
+ ssize_t sz = 0;
+ dm_block_t mapped, highest;
+ char buf[BDEVNAME_SIZE];
+ struct thin_c *tc = ti->private;
+
+ if (!tc->td)
+ DMEMIT("-");
+ else {
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = dm_thin_get_mapped_count(tc->td, &mapped);
+ if (r)
+ return r;
+
+ r = dm_thin_get_highest_mapped_block(tc->td, &highest);
+ if (r < 0)
+ return r;
+
+ DMEMIT("%llu ", mapped * tc->pool->sectors_per_block);
+ if (r)
+ DMEMIT("%llu", ((highest + 1) *
+ tc->pool->sectors_per_block) - 1);
+ else
+ DMEMIT("-");
+ break;
+
+ case STATUSTYPE_TABLE:
+ DMEMIT("%s %lu",
+ format_dev_t(buf, tc->pool_dev->bdev->bd_dev),
+ (unsigned long) tc->dev_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int thin_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ dm_block_t blocks;
+ struct thin_c *tc = ti->private;
+
+ /*
+ * 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)
+ return 0; /* nothing is bound */
+
+ blocks = tc->pool->ti->len >> tc->pool->block_shift;
+ if (blocks)
+ return fn(ti, tc->pool_dev, 0, tc->pool->sectors_per_block * blocks, data);
+
+ return 0;
+}
+
+static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct thin_c *tc = ti->private;
+
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, tc->pool->sectors_per_block << SECTOR_SHIFT);
+}
+
+static struct target_type thin_target = {
+ .name = "thin",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = thin_ctr,
+ .dtr = thin_dtr,
+ .map = thin_map,
+ .postsuspend = thin_postsuspend,
+ .status = thin_status,
+ .iterate_devices = thin_iterate_devices,
+ .io_hints = thin_io_hints,
+};
+
+/*----------------------------------------------------------------*/
+
+static int __init dm_thin_init(void)
+{
+ int r;
+
+ pool_table_init();
+
+ r = dm_register_target(&thin_target);
+ if (r)
+ return r;
+
+ r = dm_register_target(&pool_target);
+ if (r)
+ dm_unregister_target(&thin_target);
+
+ return r;
+}
+
+static void dm_thin_exit(void)
+{
+ dm_unregister_target(&thin_target);
+ dm_unregister_target(&pool_target);
+}
+
+module_init(dm_thin_init);
+module_exit(dm_thin_exit);
+
+MODULE_DESCRIPTION(DM_NAME "device-mapper thin provisioning target");
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 52b39f335bb3..4720f68f817e 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -25,6 +25,16 @@
#define DM_MSG_PREFIX "core"
+#ifdef CONFIG_PRINTK
+/*
+ * ratelimit state to be used in DMXXX_LIMIT().
+ */
+DEFINE_RATELIMIT_STATE(dm_ratelimit_state,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+EXPORT_SYMBOL(dm_ratelimit_state);
+#endif
+
/*
* Cookies are numeric values sent with CHANGE and REMOVE
* uevents while resuming, removing or renaming the device.
@@ -130,6 +140,8 @@ struct mapped_device {
/* Protect queue and type against concurrent access. */
struct mutex type_lock;
+ struct target_type *immutable_target_type;
+
struct gendisk *disk;
char name[16];
@@ -180,9 +192,6 @@ struct mapped_device {
/* forced geometry settings */
struct hd_geometry geometry;
- /* For saving the address of __make_request for request based dm */
- make_request_fn *saved_make_request_fn;
-
/* sysfs handle */
struct kobject kobj;
@@ -1391,7 +1400,7 @@ out:
* The request function that just remaps the bio built up by
* dm_merge_bvec.
*/
-static int _dm_request(struct request_queue *q, struct bio *bio)
+static void _dm_request(struct request_queue *q, struct bio *bio)
{
int rw = bio_data_dir(bio);
struct mapped_device *md = q->queuedata;
@@ -1412,19 +1421,12 @@ static int _dm_request(struct request_queue *q, struct bio *bio)
queue_io(md, bio);
else
bio_io_error(bio);
- return 0;
+ return;
}
__split_and_process_bio(md, bio);
up_read(&md->io_lock);
- return 0;
-}
-
-static int dm_make_request(struct request_queue *q, struct bio *bio)
-{
- struct mapped_device *md = q->queuedata;
-
- return md->saved_make_request_fn(q, bio); /* call __make_request() */
+ return;
}
static int dm_request_based(struct mapped_device *md)
@@ -1432,14 +1434,14 @@ static int dm_request_based(struct mapped_device *md)
return blk_queue_stackable(md->queue);
}
-static int dm_request(struct request_queue *q, struct bio *bio)
+static void dm_request(struct request_queue *q, struct bio *bio)
{
struct mapped_device *md = q->queuedata;
if (dm_request_based(md))
- return dm_make_request(q, bio);
-
- return _dm_request(q, bio);
+ blk_queue_bio(q, bio);
+ else
+ _dm_request(q, bio);
}
void dm_dispatch_request(struct request *rq)
@@ -2086,6 +2088,8 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
write_lock_irqsave(&md->map_lock, flags);
old_map = md->map;
md->map = t;
+ md->immutable_target_type = dm_table_get_immutable_target_type(t);
+
dm_table_set_restrictions(t, q, limits);
if (merge_is_optional)
set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
@@ -2156,6 +2160,11 @@ unsigned dm_get_md_type(struct mapped_device *md)
return md->type;
}
+struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
+{
+ return md->immutable_target_type;
+}
+
/*
* Fully initialize a request-based queue (->elevator, ->request_fn, etc).
*/
@@ -2172,7 +2181,6 @@ static int dm_init_request_based_queue(struct mapped_device *md)
return 0;
md->queue = q;
- md->saved_make_request_fn = md->queue->make_request_fn;
dm_init_md_queue(md);
blk_queue_softirq_done(md->queue, dm_softirq_done);
blk_queue_prep_rq(md->queue, dm_prep_fn);
@@ -2231,6 +2239,7 @@ struct mapped_device *dm_get_md(dev_t dev)
return md;
}
+EXPORT_SYMBOL_GPL(dm_get_md);
void *dm_get_mdptr(struct mapped_device *md)
{
@@ -2316,7 +2325,6 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible)
while (1) {
set_current_state(interruptible);
- smp_mb();
if (!md_in_flight(md))
break;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 6745dbd278a4..b7dacd59d8d7 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -60,6 +60,7 @@ int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
int dm_table_any_busy_target(struct dm_table *t);
unsigned dm_table_get_type(struct dm_table *t);
+struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
bool dm_table_supports_discards(struct dm_table *t);
int dm_table_alloc_md_mempools(struct dm_table *t);
@@ -72,6 +73,7 @@ void dm_lock_md_type(struct mapped_device *md);
void dm_unlock_md_type(struct mapped_device *md);
void dm_set_md_type(struct mapped_device *md, unsigned type);
unsigned dm_get_md_type(struct mapped_device *md);
+struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
int dm_setup_md_queue(struct mapped_device *md);
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
index 60816b132c2e..918fb8ac6607 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -169,7 +169,7 @@ static void add_sector(struct faulty_conf *conf, sector_t start, int mode)
conf->nfaults = n+1;
}
-static int make_request(struct mddev *mddev, struct bio *bio)
+static void make_request(struct mddev *mddev, struct bio *bio)
{
struct faulty_conf *conf = mddev->private;
int failit = 0;
@@ -181,7 +181,7 @@ static int make_request(struct mddev *mddev, struct bio *bio)
* just fail immediately
*/
bio_endio(bio, -EIO);
- return 0;
+ return;
}
if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9),
@@ -211,15 +211,15 @@ static int make_request(struct mddev *mddev, struct bio *bio)
}
if (failit) {
struct bio *b = bio_clone_mddev(bio, GFP_NOIO, mddev);
+
b->bi_bdev = conf->rdev->bdev;
b->bi_private = bio;
b->bi_end_io = faulty_fail;
- generic_make_request(b);
- return 0;
- } else {
+ bio = b;
+ } else
bio->bi_bdev = conf->rdev->bdev;
- return 1;
- }
+
+ generic_make_request(bio);
}
static void status(struct seq_file *seq, struct mddev *mddev)
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 10c5844460cb..a82035867519 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -264,14 +264,14 @@ static int linear_stop (struct mddev *mddev)
return 0;
}
-static int linear_make_request (struct mddev *mddev, struct bio *bio)
+static void linear_make_request(struct mddev *mddev, struct bio *bio)
{
struct dev_info *tmp_dev;
sector_t start_sector;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
rcu_read_lock();
@@ -293,7 +293,7 @@ static int linear_make_request (struct mddev *mddev, struct bio *bio)
(unsigned long long)start_sector);
rcu_read_unlock();
bio_io_error(bio);
- return 0;
+ return;
}
if (unlikely(bio->bi_sector + (bio->bi_size >> 9) >
tmp_dev->end_sector)) {
@@ -307,20 +307,17 @@ static int linear_make_request (struct mddev *mddev, struct bio *bio)
bp = bio_split(bio, end_sector - bio->bi_sector);
- if (linear_make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (linear_make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
+ linear_make_request(mddev, &bp->bio1);
+ linear_make_request(mddev, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
bio->bi_bdev = tmp_dev->rdev->bdev;
bio->bi_sector = bio->bi_sector - start_sector
+ tmp_dev->rdev->data_offset;
rcu_read_unlock();
-
- return 1;
+ generic_make_request(bio);
}
static void linear_status (struct seq_file *seq, struct mddev *mddev)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 266e82ebaf11..2acb32827fde 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -332,18 +332,17 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
* call has finished, the bio has been linked into some internal structure
* and so is visible to ->quiesce(), so we don't need the refcount any more.
*/
-static int md_make_request(struct request_queue *q, struct bio *bio)
+static void md_make_request(struct request_queue *q, struct bio *bio)
{
const int rw = bio_data_dir(bio);
struct mddev *mddev = q->queuedata;
- int rv;
int cpu;
unsigned int sectors;
if (mddev == NULL || mddev->pers == NULL
|| !mddev->ready) {
bio_io_error(bio);
- return 0;
+ return;
}
smp_rmb(); /* Ensure implications of 'active' are visible */
rcu_read_lock();
@@ -368,7 +367,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
* go away inside make_request
*/
sectors = bio_sectors(bio);
- rv = mddev->pers->make_request(mddev, bio);
+ mddev->pers->make_request(mddev, bio);
cpu = part_stat_lock();
part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);
@@ -377,8 +376,6 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
wake_up(&mddev->sb_wait);
-
- return rv;
}
/* mddev_suspend makes sure no new requests are submitted
@@ -477,8 +474,7 @@ static void md_submit_flush_data(struct work_struct *ws)
bio_endio(bio, 0);
else {
bio->bi_rw &= ~REQ_FLUSH;
- if (mddev->pers->make_request(mddev, bio))
- generic_make_request(bio);
+ mddev->pers->make_request(mddev, bio);
}
mddev->flush_bio = NULL;
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 51c1d91557e0..cf742d9306ec 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -419,7 +419,7 @@ struct md_personality
int level;
struct list_head list;
struct module *owner;
- int (*make_request)(struct mddev *mddev, struct bio *bio);
+ void (*make_request)(struct mddev *mddev, struct bio *bio);
int (*run)(struct mddev *mddev);
int (*stop)(struct mddev *mddev);
void (*status)(struct seq_file *seq, struct mddev *mddev);
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index d32c785e17d4..ad20a28fbf2a 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -106,7 +106,7 @@ static void multipath_end_request(struct bio *bio, int error)
rdev_dec_pending(rdev, conf->mddev);
}
-static int multipath_make_request(struct mddev *mddev, struct bio * bio)
+static void multipath_make_request(struct mddev *mddev, struct bio * bio)
{
struct mpconf *conf = mddev->private;
struct multipath_bh * mp_bh;
@@ -114,7 +114,7 @@ static int multipath_make_request(struct mddev *mddev, struct bio * bio)
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
mp_bh = mempool_alloc(conf->pool, GFP_NOIO);
@@ -126,7 +126,7 @@ static int multipath_make_request(struct mddev *mddev, struct bio * bio)
if (mp_bh->path < 0) {
bio_endio(bio, -EIO);
mempool_free(mp_bh, conf->pool);
- return 0;
+ return;
}
multipath = conf->multipaths + mp_bh->path;
@@ -137,7 +137,7 @@ static int multipath_make_request(struct mddev *mddev, struct bio * bio)
mp_bh->bio.bi_end_io = multipath_end_request;
mp_bh->bio.bi_private = mp_bh;
generic_make_request(&mp_bh->bio);
- return 0;
+ return;
}
static void multipath_status (struct seq_file *seq, struct mddev *mddev)
diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig
new file mode 100644
index 000000000000..ceb359050a59
--- /dev/null
+++ b/drivers/md/persistent-data/Kconfig
@@ -0,0 +1,8 @@
+config DM_PERSISTENT_DATA
+ tristate
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ select LIBCRC32C
+ select DM_BUFIO
+ ---help---
+ Library providing immutable on-disk data structure support for
+ device-mapper targets such as the thin provisioning target.
diff --git a/drivers/md/persistent-data/Makefile b/drivers/md/persistent-data/Makefile
new file mode 100644
index 000000000000..cfa95f662230
--- /dev/null
+++ b/drivers/md/persistent-data/Makefile
@@ -0,0 +1,11 @@
+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 \
+ dm-transaction-manager.o \
+ dm-btree.o \
+ dm-btree-remove.o \
+ dm-btree-spine.o
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
new file mode 100644
index 000000000000..0317ecdc6e53
--- /dev/null
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#include "dm-block-manager.h"
+#include "dm-persistent-data-internal.h"
+#include "../dm-bufio.h"
+
+#include <linux/crc32c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rwsem.h>
+#include <linux/device-mapper.h>
+#include <linux/stacktrace.h>
+
+#define DM_MSG_PREFIX "block manager"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * This is a read/write semaphore with a couple of differences.
+ *
+ * i) There is a restriction on the number of concurrent read locks that
+ * may be held at once. This is just an implementation detail.
+ *
+ * ii) Recursive locking attempts are detected and return EINVAL. A stack
+ * trace is also emitted for the previous lock aquisition.
+ *
+ * iii) Priority is given to write locks.
+ */
+#define MAX_HOLDERS 4
+#define MAX_STACK 10
+
+typedef unsigned long stack_entries[MAX_STACK];
+
+struct block_lock {
+ spinlock_t lock;
+ __s32 count;
+ struct list_head waiters;
+ struct task_struct *holders[MAX_HOLDERS];
+
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ struct stack_trace traces[MAX_HOLDERS];
+ stack_entries entries[MAX_HOLDERS];
+#endif
+};
+
+struct waiter {
+ struct list_head list;
+ struct task_struct *task;
+ int wants_write;
+};
+
+static unsigned __find_holder(struct block_lock *lock,
+ struct task_struct *task)
+{
+ unsigned i;
+
+ for (i = 0; i < MAX_HOLDERS; i++)
+ if (lock->holders[i] == task)
+ break;
+
+ BUG_ON(i == MAX_HOLDERS);
+ return i;
+}
+
+/* call this *after* you increment lock->count */
+static void __add_holder(struct block_lock *lock, struct task_struct *task)
+{
+ unsigned h = __find_holder(lock, NULL);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ struct stack_trace *t;
+#endif
+
+ get_task_struct(task);
+ lock->holders[h] = task;
+
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ t = lock->traces + h;
+ t->nr_entries = 0;
+ t->max_entries = MAX_STACK;
+ t->entries = lock->entries[h];
+ t->skip = 2;
+ save_stack_trace(t);
+#endif
+}
+
+/* call this *before* you decrement lock->count */
+static void __del_holder(struct block_lock *lock, struct task_struct *task)
+{
+ unsigned h = __find_holder(lock, task);
+ lock->holders[h] = NULL;
+ put_task_struct(task);
+}
+
+static int __check_holder(struct block_lock *lock)
+{
+ unsigned i;
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ static struct stack_trace t;
+ static stack_entries entries;
+#endif
+
+ for (i = 0; i < MAX_HOLDERS; i++) {
+ if (lock->holders[i] == current) {
+ DMERR("recursive lock detected in pool metadata");
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ DMERR("previously held here:");
+ print_stack_trace(lock->traces + i, 4);
+
+ DMERR("subsequent aquisition attempted here:");
+ t.nr_entries = 0;
+ t.max_entries = MAX_STACK;
+ t.entries = entries;
+ t.skip = 3;
+ save_stack_trace(&t);
+ print_stack_trace(&t, 4);
+#endif
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void __wait(struct waiter *w)
+{
+ for (;;) {
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+
+ if (!w->task)
+ break;
+
+ schedule();
+ }
+
+ set_task_state(current, TASK_RUNNING);
+}
+
+static void __wake_waiter(struct waiter *w)
+{
+ struct task_struct *task;
+
+ list_del(&w->list);
+ task = w->task;
+ smp_mb();
+ w->task = NULL;
+ wake_up_process(task);
+}
+
+/*
+ * We either wake a few readers or a single writer.
+ */
+static void __wake_many(struct block_lock *lock)
+{
+ struct waiter *w, *tmp;
+
+ BUG_ON(lock->count < 0);
+ list_for_each_entry_safe(w, tmp, &lock->waiters, list) {
+ if (lock->count >= MAX_HOLDERS)
+ return;
+
+ if (w->wants_write) {
+ if (lock->count > 0)
+ return; /* still read locked */
+
+ lock->count = -1;
+ __add_holder(lock, w->task);
+ __wake_waiter(w);
+ return;
+ }
+
+ lock->count++;
+ __add_holder(lock, w->task);
+ __wake_waiter(w);
+ }
+}
+
+static void bl_init(struct block_lock *lock)
+{
+ int i;
+
+ spin_lock_init(&lock->lock);
+ lock->count = 0;
+ INIT_LIST_HEAD(&lock->waiters);
+ for (i = 0; i < MAX_HOLDERS; i++)
+ lock->holders[i] = NULL;
+}
+
+static int __available_for_read(struct block_lock *lock)
+{
+ return lock->count >= 0 &&
+ lock->count < MAX_HOLDERS &&
+ list_empty(&lock->waiters);
+}
+
+static int bl_down_read(struct block_lock *lock)
+{
+ int r;
+ struct waiter w;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r) {
+ spin_unlock(&lock->lock);
+ return r;
+ }
+
+ if (__available_for_read(lock)) {
+ lock->count++;
+ __add_holder(lock, current);
+ spin_unlock(&lock->lock);
+ return 0;
+ }
+
+ get_task_struct(current);
+
+ w.task = current;
+ w.wants_write = 0;
+ list_add_tail(&w.list, &lock->waiters);
+ spin_unlock(&lock->lock);
+
+ __wait(&w);
+ put_task_struct(current);
+ return 0;
+}
+
+static int bl_down_read_nonblock(struct block_lock *lock)
+{
+ int r;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r)
+ goto out;
+
+ if (__available_for_read(lock)) {
+ lock->count++;
+ __add_holder(lock, current);
+ r = 0;
+ } else
+ r = -EWOULDBLOCK;
+
+out:
+ spin_unlock(&lock->lock);
+ return r;
+}
+
+static void bl_up_read(struct block_lock *lock)
+{
+ spin_lock(&lock->lock);
+ BUG_ON(lock->count <= 0);
+ __del_holder(lock, current);
+ --lock->count;
+ if (!list_empty(&lock->waiters))
+ __wake_many(lock);
+ spin_unlock(&lock->lock);
+}
+
+static int bl_down_write(struct block_lock *lock)
+{
+ int r;
+ struct waiter w;
+
+ spin_lock(&lock->lock);
+ r = __check_holder(lock);
+ if (r) {
+ spin_unlock(&lock->lock);
+ return r;
+ }
+
+ if (lock->count == 0 && list_empty(&lock->waiters)) {
+ lock->count = -1;
+ __add_holder(lock, current);
+ spin_unlock(&lock->lock);
+ return 0;
+ }
+
+ get_task_struct(current);
+ w.task = current;
+ w.wants_write = 1;
+
+ /*
+ * Writers given priority. We know there's only one mutator in the
+ * system, so ignoring the ordering reversal.
+ */
+ list_add(&w.list, &lock->waiters);
+ spin_unlock(&lock->lock);
+
+ __wait(&w);
+ put_task_struct(current);
+
+ return 0;
+}
+
+static void bl_up_write(struct block_lock *lock)
+{
+ spin_lock(&lock->lock);
+ __del_holder(lock, current);
+ lock->count = 0;
+ if (!list_empty(&lock->waiters))
+ __wake_many(lock);
+ spin_unlock(&lock->lock);
+}
+
+static void report_recursive_bug(dm_block_t b, int r)
+{
+ if (r == -EINVAL)
+ DMERR("recursive acquisition of block %llu requested.",
+ (unsigned long long) b);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Block manager is currently implemented using dm-bufio. struct
+ * dm_block_manager and struct dm_block map directly onto a couple of
+ * structs in the bufio interface. I want to retain the freedom to move
+ * away from bufio in the future. So these structs are just cast within
+ * this .c file, rather than making it through to the public interface.
+ */
+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));
+}
+EXPORT_SYMBOL_GPL(dm_block_location);
+
+void *dm_block_data(struct dm_block *b)
+{
+ return dm_bufio_get_block_data(to_buffer(b));
+}
+EXPORT_SYMBOL_GPL(dm_block_data);
+
+struct buffer_aux {
+ struct dm_block_validator *validator;
+ struct block_lock lock;
+ int write_locked;
+};
+
+static void dm_block_manager_alloc_callback(struct dm_buffer *buf)
+{
+ struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
+ aux->validator = NULL;
+ bl_init(&aux->lock);
+}
+
+static void dm_block_manager_write_callback(struct dm_buffer *buf)
+{
+ struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
+ if (aux->validator) {
+ aux->validator->prepare_for_write(aux->validator, (struct dm_block *) buf,
+ dm_bufio_get_block_size(dm_bufio_get_client(buf)));
+ }
+}
+
+/*----------------------------------------------------------------
+ * Public interface
+ *--------------------------------------------------------------*/
+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);
+}
+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));
+}
+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));
+}
+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));
+}
+
+static int dm_bm_validate_buffer(struct dm_block_manager *bm,
+ struct dm_buffer *buf,
+ struct buffer_aux *aux,
+ struct dm_block_validator *v)
+{
+ if (unlikely(!aux->validator)) {
+ int r;
+ if (!v)
+ return 0;
+ r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(to_bufio(bm)));
+ if (unlikely(r))
+ return r;
+ aux->validator = v;
+ } else {
+ if (unlikely(aux->validator != v)) {
+ DMERR("validator mismatch (old=%s vs new=%s) for block %llu",
+ aux->validator->name, v ? v->name : "NULL",
+ (unsigned long long)
+ dm_bufio_get_block_number(buf));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_read(&aux->lock);
+ if (unlikely(r)) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+
+ aux->write_locked = 0;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_read(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_bm_read_lock);
+
+int dm_bm_write_lock(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_write(&aux->lock);
+ if (r) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+
+ aux->write_locked = 1;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_write(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_bm_write_lock);
+
+int dm_bm_read_try_lock(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ struct buffer_aux *aux;
+ void *p;
+ int r;
+
+ p = dm_bufio_get(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+ if (unlikely(!p))
+ return -EWOULDBLOCK;
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_read_nonblock(&aux->lock);
+ if (r < 0) {
+ dm_bufio_release(to_buffer(*result));
+ report_recursive_bug(b, r);
+ return r;
+ }
+ aux->write_locked = 0;
+
+ r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+ if (unlikely(r)) {
+ bl_up_read(&aux->lock);
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ return 0;
+}
+
+int dm_bm_write_lock_zero(struct dm_block_manager *bm,
+ dm_block_t b, struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ struct buffer_aux *aux;
+ void *p;
+
+ p = dm_bufio_new(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (unlikely(IS_ERR(p)))
+ return PTR_ERR(p);
+
+ memset(p, 0, dm_bm_block_size(bm));
+
+ aux = dm_bufio_get_aux_data(to_buffer(*result));
+ r = bl_down_write(&aux->lock);
+ if (r) {
+ dm_bufio_release(to_buffer(*result));
+ return r;
+ }
+
+ aux->write_locked = 1;
+ aux->validator = v;
+
+ return 0;
+}
+
+int dm_bm_unlock(struct dm_block *b)
+{
+ 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(to_buffer(b));
+
+ return 0;
+}
+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))
+ 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 0;
+}
+
+u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
+{
+ return crc32c(~(u32) 0, data, len) ^ init_xor;
+}
+EXPORT_SYMBOL_GPL(dm_bm_checksum);
+
+/*----------------------------------------------------------------*/
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
+MODULE_DESCRIPTION("Immutable metadata library for dm");
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
new file mode 100644
index 000000000000..924833d2dfa6
--- /dev/null
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_BLOCK_MANAGER_H
+#define _LINUX_DM_BLOCK_MANAGER_H
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Block number.
+ */
+typedef uint64_t dm_block_t;
+struct dm_block;
+
+dm_block_t dm_block_location(struct dm_block *b);
+void *dm_block_data(struct dm_block *b);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * @name should be a unique identifier for the block manager, no longer
+ * than 32 chars.
+ *
+ * @max_held_per_thread should be the maximum number of locks, read or
+ * write, that an individual thread holds at any one time.
+ */
+struct dm_block_manager;
+struct dm_block_manager *dm_block_manager_create(
+ struct block_device *bdev, unsigned block_size,
+ unsigned cache_size, unsigned max_held_per_thread);
+void dm_block_manager_destroy(struct dm_block_manager *bm);
+
+unsigned dm_bm_block_size(struct dm_block_manager *bm);
+dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * The validator allows the caller to verify newly-read data and modify
+ * the data just before writing, e.g. to calculate checksums. It's
+ * important to be consistent with your use of validators. The only time
+ * you can change validators is if you call dm_bm_write_lock_zero.
+ */
+struct dm_block_validator {
+ const char *name;
+ void (*prepare_for_write)(struct dm_block_validator *v, struct dm_block *b, size_t block_size);
+
+ /*
+ * Return 0 if the checksum is valid or < 0 on error.
+ */
+ int (*check)(struct dm_block_validator *v, struct dm_block *b, size_t block_size);
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * You can have multiple concurrent readers or a single writer holding a
+ * block lock.
+ */
+
+/*
+ * dm_bm_lock() locks a block and returns through @result a pointer to
+ * memory that holds a copy of that block. If you have write-locked the
+ * block then any changes you make to memory pointed to by @result will be
+ * written back to the disk sometime after dm_bm_unlock is called.
+ */
+int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+int dm_bm_write_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * The *_try_lock variants return -EWOULDBLOCK if the block isn't
+ * available immediately.
+ */
+int dm_bm_read_try_lock(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * Use dm_bm_write_lock_zero() when you know you're going to
+ * overwrite the block completely. It saves a disk read.
+ */
+int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+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
+ * this function. All dirty blocks are guaranteed to be written and flushed
+ * before the superblock.
+ *
+ * This method always blocks.
+ */
+int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
+ struct dm_block *superblock);
+
+u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
+
+/*----------------------------------------------------------------*/
+
+#endif /* _LINUX_DM_BLOCK_MANAGER_H */
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
new file mode 100644
index 000000000000..d279c768f8f1
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-internal.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_BTREE_INTERNAL_H
+#define DM_BTREE_INTERNAL_H
+
+#include "dm-btree.h"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We'll need 2 accessor functions for n->csum and n->blocknr
+ * to support dm-btree-spine.c in that case.
+ */
+
+enum node_flags {
+ INTERNAL_NODE = 1,
+ LEAF_NODE = 1 << 1
+};
+
+/*
+ * Every btree node begins with this structure. Make sure it's a multiple
+ * of 8-bytes in size, otherwise the 64bit keys will be mis-aligned.
+ */
+struct node_header {
+ __le32 csum;
+ __le32 flags;
+ __le64 blocknr; /* Block this node is supposed to live in. */
+
+ __le32 nr_entries;
+ __le32 max_entries;
+ __le32 value_size;
+ __le32 padding;
+} __packed;
+
+struct node {
+ struct node_header header;
+ __le64 keys[0];
+} __packed;
+
+
+void inc_children(struct dm_transaction_manager *tm, struct node *n,
+ struct dm_btree_value_type *vt);
+
+int new_block(struct dm_btree_info *info, struct dm_block **result);
+int unlock_block(struct dm_btree_info *info, struct dm_block *b);
+
+/*
+ * Spines keep track of the rolling locks. There are 2 variants, read-only
+ * and one that uses shadowing. These are separate structs to allow the
+ * type checker to spot misuse, for example accidentally calling read_lock
+ * on a shadow spine.
+ */
+struct ro_spine {
+ struct dm_btree_info *info;
+
+ int count;
+ struct dm_block *nodes[2];
+};
+
+void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info);
+int exit_ro_spine(struct ro_spine *s);
+int ro_step(struct ro_spine *s, dm_block_t new_child);
+struct node *ro_node(struct ro_spine *s);
+
+struct shadow_spine {
+ struct dm_btree_info *info;
+
+ int count;
+ struct dm_block *nodes[2];
+
+ dm_block_t root;
+};
+
+void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info);
+int exit_shadow_spine(struct shadow_spine *s);
+
+int shadow_step(struct shadow_spine *s, dm_block_t b,
+ struct dm_btree_value_type *vt);
+
+/*
+ * The spine must have at least one entry before calling this.
+ */
+struct dm_block *shadow_current(struct shadow_spine *s);
+
+/*
+ * The spine must have at least two entries before calling this.
+ */
+struct dm_block *shadow_parent(struct shadow_spine *s);
+
+int shadow_has_parent(struct shadow_spine *s);
+
+int shadow_root(struct shadow_spine *s);
+
+/*
+ * Some inlines.
+ */
+static inline __le64 *key_ptr(struct node *n, uint32_t index)
+{
+ return n->keys + index;
+}
+
+static inline void *value_base(struct node *n)
+{
+ return &n->keys[le32_to_cpu(n->header.max_entries)];
+}
+
+/*
+ * FIXME: Now that value size is stored in node we don't need the third parm.
+ */
+static inline void *value_ptr(struct node *n, uint32_t index, size_t value_size)
+{
+ BUG_ON(value_size != le32_to_cpu(n->header.value_size));
+ return value_base(n) + (value_size * index);
+}
+
+/*
+ * Assumes the values are suitably-aligned and converts to core format.
+ */
+static inline uint64_t value64(struct node *n, uint32_t index)
+{
+ __le64 *values_le = value_base(n);
+
+ return le64_to_cpu(values_le[index]);
+}
+
+/*
+ * Searching for a key within a single node.
+ */
+int lower_bound(struct node *n, uint64_t key);
+
+extern struct dm_block_validator btree_node_validator;
+
+#endif /* DM_BTREE_INTERNAL_H */
diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c
new file mode 100644
index 000000000000..65fd85ec6514
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-remove.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree.h"
+#include "dm-btree-internal.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/module.h>
+
+/*
+ * Removing an entry from a btree
+ * ==============================
+ *
+ * A very important constraint for our btree is that no node, except the
+ * root, may have fewer than a certain number of entries.
+ * (MIN_ENTRIES <= nr_entries <= MAX_ENTRIES).
+ *
+ * Ensuring this is complicated by the way we want to only ever hold the
+ * locks on 2 nodes concurrently, and only change nodes in a top to bottom
+ * fashion.
+ *
+ * Each node may have a left or right sibling. When decending the spine,
+ * if a node contains only MIN_ENTRIES then we try and increase this to at
+ * least MIN_ENTRIES + 1. We do this in the following ways:
+ *
+ * [A] No siblings => this can only happen if the node is the root, in which
+ * case we copy the childs contents over the root.
+ *
+ * [B] No left sibling
+ * ==> rebalance(node, right sibling)
+ *
+ * [C] No right sibling
+ * ==> rebalance(left sibling, node)
+ *
+ * [D] Both siblings, total_entries(left, node, right) <= DEL_THRESHOLD
+ * ==> delete node adding it's contents to left and right
+ *
+ * [E] Both siblings, total_entries(left, node, right) > DEL_THRESHOLD
+ * ==> rebalance(left, node, right)
+ *
+ * After these operations it's possible that the our original node no
+ * longer contains the desired sub tree. For this reason this rebalancing
+ * is performed on the children of the current node. This also avoids
+ * having a special case for the root.
+ *
+ * Once this rebalancing has occurred we can then step into the child node
+ * for internal nodes. Or delete the entry for leaf nodes.
+ */
+
+/*
+ * Some little utilities for moving node data around.
+ */
+static void node_shift(struct node *n, int shift)
+{
+ uint32_t nr_entries = le32_to_cpu(n->header.nr_entries);
+ uint32_t value_size = le32_to_cpu(n->header.value_size);
+
+ if (shift < 0) {
+ shift = -shift;
+ BUG_ON(shift > nr_entries);
+ BUG_ON((void *) key_ptr(n, shift) >= value_ptr(n, shift, value_size));
+ memmove(key_ptr(n, 0),
+ key_ptr(n, shift),
+ (nr_entries - shift) * sizeof(__le64));
+ memmove(value_ptr(n, 0, value_size),
+ value_ptr(n, shift, value_size),
+ (nr_entries - shift) * value_size);
+ } else {
+ BUG_ON(nr_entries + shift > le32_to_cpu(n->header.max_entries));
+ memmove(key_ptr(n, shift),
+ key_ptr(n, 0),
+ nr_entries * sizeof(__le64));
+ memmove(value_ptr(n, shift, value_size),
+ value_ptr(n, 0, value_size),
+ nr_entries * value_size);
+ }
+}
+
+static void node_copy(struct node *left, struct node *right, int shift)
+{
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t value_size = le32_to_cpu(left->header.value_size);
+ BUG_ON(value_size != le32_to_cpu(right->header.value_size));
+
+ if (shift < 0) {
+ shift = -shift;
+ BUG_ON(nr_left + shift > le32_to_cpu(left->header.max_entries));
+ memcpy(key_ptr(left, nr_left),
+ key_ptr(right, 0),
+ shift * sizeof(__le64));
+ memcpy(value_ptr(left, nr_left, value_size),
+ value_ptr(right, 0, value_size),
+ shift * value_size);
+ } else {
+ BUG_ON(shift > le32_to_cpu(right->header.max_entries));
+ memcpy(key_ptr(right, 0),
+ key_ptr(left, nr_left - shift),
+ shift * sizeof(__le64));
+ memcpy(value_ptr(right, 0, value_size),
+ value_ptr(left, nr_left - shift, value_size),
+ shift * value_size);
+ }
+}
+
+/*
+ * Delete a specific entry from a leaf node.
+ */
+static void delete_at(struct node *n, unsigned index)
+{
+ unsigned nr_entries = le32_to_cpu(n->header.nr_entries);
+ unsigned nr_to_copy = nr_entries - (index + 1);
+ uint32_t value_size = le32_to_cpu(n->header.value_size);
+ BUG_ON(index >= nr_entries);
+
+ if (nr_to_copy) {
+ memmove(key_ptr(n, index),
+ key_ptr(n, index + 1),
+ nr_to_copy * sizeof(__le64));
+
+ memmove(value_ptr(n, index, value_size),
+ value_ptr(n, index + 1, value_size),
+ nr_to_copy * value_size);
+ }
+
+ n->header.nr_entries = cpu_to_le32(nr_entries - 1);
+}
+
+static unsigned del_threshold(struct node *n)
+{
+ return le32_to_cpu(n->header.max_entries) / 3;
+}
+
+static unsigned merge_threshold(struct node *n)
+{
+ /*
+ * The extra one is because we know we're potentially going to
+ * delete an entry.
+ */
+ return 2 * (le32_to_cpu(n->header.max_entries) / 3) + 1;
+}
+
+struct child {
+ unsigned index;
+ struct dm_block *block;
+ struct node *n;
+};
+
+static struct dm_btree_value_type le64_type = {
+ .context = NULL,
+ .size = sizeof(__le64),
+ .inc = NULL,
+ .dec = NULL,
+ .equal = NULL
+};
+
+static int init_child(struct dm_btree_info *info, struct node *parent,
+ unsigned index, struct child *result)
+{
+ int r, inc;
+ dm_block_t root;
+
+ result->index = index;
+ root = value64(parent, index);
+
+ r = dm_tm_shadow_block(info->tm, root, &btree_node_validator,
+ &result->block, &inc);
+ if (r)
+ return r;
+
+ result->n = dm_block_data(result->block);
+
+ if (inc)
+ inc_children(info->tm, result->n, &le64_type);
+
+ *((__le64 *) value_ptr(parent, index, sizeof(__le64))) =
+ cpu_to_le64(dm_block_location(result->block));
+
+ return 0;
+}
+
+static int exit_child(struct dm_btree_info *info, struct child *c)
+{
+ return dm_tm_unlock(info->tm, c->block);
+}
+
+static void shift(struct node *left, struct node *right, int count)
+{
+ if (!count)
+ return;
+
+ if (count > 0) {
+ node_shift(right, count);
+ node_copy(left, right, count);
+ } else {
+ node_copy(left, right, count);
+ node_shift(right, count);
+ }
+
+ left->header.nr_entries =
+ cpu_to_le32(le32_to_cpu(left->header.nr_entries) - count);
+ BUG_ON(le32_to_cpu(left->header.nr_entries) > le32_to_cpu(left->header.max_entries));
+
+ right->header.nr_entries =
+ cpu_to_le32(le32_to_cpu(right->header.nr_entries) + count);
+ BUG_ON(le32_to_cpu(right->header.nr_entries) > le32_to_cpu(right->header.max_entries));
+}
+
+static void __rebalance2(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *r)
+{
+ struct node *left = l->n;
+ struct node *right = r->n;
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+
+ if (nr_left + nr_right <= merge_threshold(left)) {
+ /*
+ * Merge
+ */
+ node_copy(left, right, -nr_right);
+ left->header.nr_entries = cpu_to_le32(nr_left + nr_right);
+ delete_at(parent, r->index);
+
+ /*
+ * We need to decrement the right block, but not it's
+ * children, since they're still referenced by left.
+ */
+ dm_tm_dec(info->tm, dm_block_location(r->block));
+ } else {
+ /*
+ * Rebalance.
+ */
+ unsigned target_left = (nr_left + nr_right) / 2;
+ unsigned shift_ = nr_left - target_left;
+ BUG_ON(le32_to_cpu(left->header.max_entries) <= nr_left - shift_);
+ BUG_ON(le32_to_cpu(right->header.max_entries) <= nr_right + shift_);
+ shift(left, right, nr_left - target_left);
+ *key_ptr(parent, r->index) = right->keys[0];
+ }
+}
+
+static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info,
+ unsigned left_index)
+{
+ int r;
+ struct node *parent;
+ struct child left, right;
+
+ parent = dm_block_data(shadow_current(s));
+
+ r = init_child(info, parent, left_index, &left);
+ if (r)
+ return r;
+
+ r = init_child(info, parent, left_index + 1, &right);
+ if (r) {
+ exit_child(info, &left);
+ return r;
+ }
+
+ __rebalance2(info, parent, &left, &right);
+
+ r = exit_child(info, &left);
+ if (r) {
+ exit_child(info, &right);
+ return r;
+ }
+
+ return exit_child(info, &right);
+}
+
+static void __rebalance3(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *c, struct child *r)
+{
+ struct node *left = l->n;
+ struct node *center = c->n;
+ struct node *right = r->n;
+
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t nr_center = le32_to_cpu(center->header.nr_entries);
+ uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+ uint32_t max_entries = le32_to_cpu(left->header.max_entries);
+
+ unsigned target;
+
+ BUG_ON(left->header.max_entries != center->header.max_entries);
+ BUG_ON(center->header.max_entries != right->header.max_entries);
+
+ if (((nr_left + nr_center + nr_right) / 2) < merge_threshold(center)) {
+ /*
+ * Delete center node:
+ *
+ * We dump as many entries from center as possible into
+ * left, then the rest in right, then rebalance2. This
+ * wastes some cpu, but I want something simple atm.
+ */
+ unsigned shift = min(max_entries - nr_left, nr_center);
+
+ BUG_ON(nr_left + shift > max_entries);
+ node_copy(left, center, -shift);
+ left->header.nr_entries = cpu_to_le32(nr_left + shift);
+
+ if (shift != nr_center) {
+ shift = nr_center - shift;
+ BUG_ON((nr_right + shift) >= max_entries);
+ node_shift(right, shift);
+ node_copy(center, right, shift);
+ right->header.nr_entries = cpu_to_le32(nr_right + shift);
+ }
+ *key_ptr(parent, r->index) = right->keys[0];
+
+ delete_at(parent, c->index);
+ r->index--;
+
+ dm_tm_dec(info->tm, dm_block_location(c->block));
+ __rebalance2(info, parent, l, r);
+
+ return;
+ }
+
+ /*
+ * Rebalance
+ */
+ target = (nr_left + nr_center + nr_right) / 3;
+ BUG_ON(target > max_entries);
+
+ /*
+ * Adjust the left node
+ */
+ shift(left, center, nr_left - target);
+
+ /*
+ * Adjust the right node
+ */
+ shift(center, right, target - nr_right);
+ *key_ptr(parent, c->index) = center->keys[0];
+ *key_ptr(parent, r->index) = right->keys[0];
+}
+
+static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info,
+ unsigned left_index)
+{
+ int r;
+ struct node *parent = dm_block_data(shadow_current(s));
+ struct child left, center, right;
+
+ /*
+ * FIXME: fill out an array?
+ */
+ r = init_child(info, parent, left_index, &left);
+ if (r)
+ return r;
+
+ r = init_child(info, parent, left_index + 1, &center);
+ if (r) {
+ exit_child(info, &left);
+ return r;
+ }
+
+ r = init_child(info, parent, left_index + 2, &right);
+ if (r) {
+ exit_child(info, &left);
+ exit_child(info, &center);
+ return r;
+ }
+
+ __rebalance3(info, parent, &left, &center, &right);
+
+ r = exit_child(info, &left);
+ if (r) {
+ exit_child(info, &center);
+ exit_child(info, &right);
+ return r;
+ }
+
+ r = exit_child(info, &center);
+ if (r) {
+ exit_child(info, &right);
+ return r;
+ }
+
+ r = exit_child(info, &right);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int get_nr_entries(struct dm_transaction_manager *tm,
+ dm_block_t b, uint32_t *result)
+{
+ int r;
+ struct dm_block *block;
+ struct node *n;
+
+ r = dm_tm_read_lock(tm, b, &btree_node_validator, &block);
+ if (r)
+ return r;
+
+ n = dm_block_data(block);
+ *result = le32_to_cpu(n->header.nr_entries);
+
+ return dm_tm_unlock(tm, block);
+}
+
+static int rebalance_children(struct shadow_spine *s,
+ struct dm_btree_info *info, uint64_t key)
+{
+ int i, r, has_left_sibling, has_right_sibling;
+ uint32_t child_entries;
+ struct node *n;
+
+ n = dm_block_data(shadow_current(s));
+
+ if (le32_to_cpu(n->header.nr_entries) == 1) {
+ struct dm_block *child;
+ dm_block_t b = value64(n, 0);
+
+ r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child);
+ if (r)
+ return r;
+
+ memcpy(n, dm_block_data(child),
+ dm_bm_block_size(dm_tm_get_bm(info->tm)));
+ r = dm_tm_unlock(info->tm, child);
+ if (r)
+ return r;
+
+ dm_tm_dec(info->tm, dm_block_location(child));
+ return 0;
+ }
+
+ i = lower_bound(n, key);
+ if (i < 0)
+ return -ENODATA;
+
+ r = get_nr_entries(info->tm, value64(n, i), &child_entries);
+ if (r)
+ return r;
+
+ if (child_entries > del_threshold(n))
+ return 0;
+
+ has_left_sibling = i > 0;
+ has_right_sibling = i < (le32_to_cpu(n->header.nr_entries) - 1);
+
+ if (!has_left_sibling)
+ r = rebalance2(s, info, i);
+
+ else if (!has_right_sibling)
+ r = rebalance2(s, info, i - 1);
+
+ else
+ r = rebalance3(s, info, i - 1);
+
+ return r;
+}
+
+static int do_leaf(struct node *n, uint64_t key, unsigned *index)
+{
+ int i = lower_bound(n, key);
+
+ if ((i < 0) ||
+ (i >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[i]) != key))
+ return -ENODATA;
+
+ *index = i;
+
+ return 0;
+}
+
+/*
+ * Prepares for removal from one level of the hierarchy. The caller must
+ * call delete_at() to remove the entry at index.
+ */
+static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,
+ struct dm_btree_value_type *vt, dm_block_t root,
+ uint64_t key, unsigned *index)
+{
+ int i = *index, r;
+ struct node *n;
+
+ for (;;) {
+ r = shadow_step(s, root, vt);
+ if (r < 0)
+ break;
+
+ /*
+ * We have to patch up the parent node, ugly, but I don't
+ * see a way to do this automatically as part of the spine
+ * op.
+ */
+ if (shadow_has_parent(s)) {
+ __le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
+ memcpy(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(__le64)),
+ &location, sizeof(__le64));
+ }
+
+ n = dm_block_data(shadow_current(s));
+
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE)
+ return do_leaf(n, key, index);
+
+ r = rebalance_children(s, info, key);
+ if (r)
+ break;
+
+ n = dm_block_data(shadow_current(s));
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE)
+ return do_leaf(n, key, index);
+
+ i = lower_bound(n, key);
+
+ /*
+ * We know the key is present, or else
+ * rebalance_children would have returned
+ * -ENODATA
+ */
+ root = value64(n, i);
+ }
+
+ return r;
+}
+
+int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, dm_block_t *new_root)
+{
+ unsigned level, last_level = info->levels - 1;
+ int index = 0, r = 0;
+ struct shadow_spine spine;
+ struct node *n;
+
+ init_shadow_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ r = remove_raw(&spine, info,
+ (level == last_level ?
+ &info->value_type : &le64_type),
+ root, keys[level], (unsigned *)&index);
+ if (r < 0)
+ break;
+
+ n = dm_block_data(shadow_current(&spine));
+ if (level != last_level) {
+ root = value64(n, index);
+ continue;
+ }
+
+ BUG_ON(index < 0 || index >= le32_to_cpu(n->header.nr_entries));
+
+ if (info->value_type.dec)
+ info->value_type.dec(info->value_type.context,
+ value_ptr(n, index, info->value_type.size));
+
+ delete_at(n, index);
+ }
+
+ *new_root = shadow_root(&spine);
+ exit_shadow_spine(&spine);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_remove);
diff --git a/drivers/md/persistent-data/dm-btree-spine.c b/drivers/md/persistent-data/dm-btree-spine.c
new file mode 100644
index 000000000000..d9a7912ee8ee
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree-spine.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree-internal.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "btree spine"
+
+/*----------------------------------------------------------------*/
+
+#define BTREE_CSUM_XOR 121107
+
+static int node_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size);
+
+static void node_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct node *n = dm_block_data(b);
+ struct node_header *h = &n->header;
+
+ h->blocknr = cpu_to_le64(dm_block_location(b));
+ h->csum = cpu_to_le32(dm_bm_checksum(&h->flags,
+ block_size - sizeof(__le32),
+ BTREE_CSUM_XOR));
+
+ BUG_ON(node_check(v, b, 4096));
+}
+
+static int node_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct node *n = dm_block_data(b);
+ struct node_header *h = &n->header;
+ size_t value_size;
+ __le32 csum_disk;
+ uint32_t flags;
+
+ if (dm_block_location(b) != le64_to_cpu(h->blocknr)) {
+ DMERR("node_check failed blocknr %llu wanted %llu",
+ le64_to_cpu(h->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&h->flags,
+ block_size - sizeof(__le32),
+ BTREE_CSUM_XOR));
+ if (csum_disk != h->csum) {
+ DMERR("node_check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(h->csum));
+ return -EILSEQ;
+ }
+
+ value_size = le32_to_cpu(h->value_size);
+
+ if (sizeof(struct node_header) +
+ (sizeof(__le64) + value_size) * le32_to_cpu(h->max_entries) > block_size) {
+ DMERR("node_check failed: max_entries too large");
+ return -EILSEQ;
+ }
+
+ if (le32_to_cpu(h->nr_entries) > le32_to_cpu(h->max_entries)) {
+ DMERR("node_check failed, too many entries");
+ return -EILSEQ;
+ }
+
+ /*
+ * The node must be either INTERNAL or LEAF.
+ */
+ flags = le32_to_cpu(h->flags);
+ if (!(flags & INTERNAL_NODE) && !(flags & LEAF_NODE)) {
+ DMERR("node_check failed, node is neither INTERNAL or LEAF");
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+struct dm_block_validator btree_node_validator = {
+ .name = "btree_node",
+ .prepare_for_write = node_prepare_for_write,
+ .check = node_check
+};
+
+/*----------------------------------------------------------------*/
+
+static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+ struct dm_block **result)
+{
+ return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
+}
+
+static int bn_shadow(struct dm_btree_info *info, dm_block_t orig,
+ struct dm_btree_value_type *vt,
+ struct dm_block **result)
+{
+ int r, inc;
+
+ r = dm_tm_shadow_block(info->tm, orig, &btree_node_validator,
+ result, &inc);
+ if (!r && inc)
+ inc_children(info->tm, dm_block_data(*result), vt);
+
+ return r;
+}
+
+int new_block(struct dm_btree_info *info, struct dm_block **result)
+{
+ return dm_tm_new_block(info->tm, &btree_node_validator, result);
+}
+
+int unlock_block(struct dm_btree_info *info, struct dm_block *b)
+{
+ return dm_tm_unlock(info->tm, b);
+}
+
+/*----------------------------------------------------------------*/
+
+void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info)
+{
+ s->info = info;
+ s->count = 0;
+ s->nodes[0] = NULL;
+ s->nodes[1] = NULL;
+}
+
+int exit_ro_spine(struct ro_spine *s)
+{
+ int r = 0, i;
+
+ for (i = 0; i < s->count; i++) {
+ int r2 = unlock_block(s->info, s->nodes[i]);
+ if (r2 < 0)
+ r = r2;
+ }
+
+ return r;
+}
+
+int ro_step(struct ro_spine *s, dm_block_t new_child)
+{
+ int r;
+
+ if (s->count == 2) {
+ r = unlock_block(s->info, s->nodes[0]);
+ if (r < 0)
+ return r;
+ s->nodes[0] = s->nodes[1];
+ s->count--;
+ }
+
+ r = bn_read_lock(s->info, new_child, s->nodes + s->count);
+ if (!r)
+ s->count++;
+
+ return r;
+}
+
+struct node *ro_node(struct ro_spine *s)
+{
+ struct dm_block *block;
+
+ BUG_ON(!s->count);
+ block = s->nodes[s->count - 1];
+
+ return dm_block_data(block);
+}
+
+/*----------------------------------------------------------------*/
+
+void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info)
+{
+ s->info = info;
+ s->count = 0;
+}
+
+int exit_shadow_spine(struct shadow_spine *s)
+{
+ int r = 0, i;
+
+ for (i = 0; i < s->count; i++) {
+ int r2 = unlock_block(s->info, s->nodes[i]);
+ if (r2 < 0)
+ r = r2;
+ }
+
+ return r;
+}
+
+int shadow_step(struct shadow_spine *s, dm_block_t b,
+ struct dm_btree_value_type *vt)
+{
+ int r;
+
+ if (s->count == 2) {
+ r = unlock_block(s->info, s->nodes[0]);
+ if (r < 0)
+ return r;
+ s->nodes[0] = s->nodes[1];
+ s->count--;
+ }
+
+ r = bn_shadow(s->info, b, vt, s->nodes + s->count);
+ if (!r) {
+ if (!s->count)
+ s->root = dm_block_location(s->nodes[0]);
+
+ s->count++;
+ }
+
+ return r;
+}
+
+struct dm_block *shadow_current(struct shadow_spine *s)
+{
+ BUG_ON(!s->count);
+
+ return s->nodes[s->count - 1];
+}
+
+struct dm_block *shadow_parent(struct shadow_spine *s)
+{
+ BUG_ON(s->count != 2);
+
+ return s->count == 2 ? s->nodes[0] : NULL;
+}
+
+int shadow_has_parent(struct shadow_spine *s)
+{
+ return s->count >= 2;
+}
+
+int shadow_root(struct shadow_spine *s)
+{
+ return s->root;
+}
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
new file mode 100644
index 000000000000..e0638be53ea4
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-btree-internal.h"
+#include "dm-space-map.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/module.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "btree"
+
+/*----------------------------------------------------------------
+ * Array manipulation
+ *--------------------------------------------------------------*/
+static void memcpy_disk(void *dest, const void *src, size_t len)
+ __dm_written_to_disk(src)
+{
+ memcpy(dest, src, len);
+ __dm_unbless_for_disk(src);
+}
+
+static void array_insert(void *base, size_t elt_size, unsigned nr_elts,
+ unsigned index, void *elt)
+ __dm_written_to_disk(elt)
+{
+ if (index < nr_elts)
+ memmove(base + (elt_size * (index + 1)),
+ base + (elt_size * index),
+ (nr_elts - index) * elt_size);
+
+ memcpy_disk(base + (elt_size * index), elt, elt_size);
+}
+
+/*----------------------------------------------------------------*/
+
+/* makes the assumption that no two keys are the same. */
+static int bsearch(struct node *n, uint64_t key, int want_hi)
+{
+ int lo = -1, hi = le32_to_cpu(n->header.nr_entries);
+
+ while (hi - lo > 1) {
+ int mid = lo + ((hi - lo) / 2);
+ uint64_t mid_key = le64_to_cpu(n->keys[mid]);
+
+ if (mid_key == key)
+ return mid;
+
+ if (mid_key < key)
+ lo = mid;
+ else
+ hi = mid;
+ }
+
+ return want_hi ? hi : lo;
+}
+
+int lower_bound(struct node *n, uint64_t key)
+{
+ return bsearch(n, key, 0);
+}
+
+void inc_children(struct dm_transaction_manager *tm, struct node *n,
+ struct dm_btree_value_type *vt)
+{
+ unsigned i;
+ uint32_t nr_entries = le32_to_cpu(n->header.nr_entries);
+
+ if (le32_to_cpu(n->header.flags) & INTERNAL_NODE)
+ for (i = 0; i < nr_entries; i++)
+ dm_tm_inc(tm, value64(n, i));
+ else if (vt->inc)
+ for (i = 0; i < nr_entries; i++)
+ vt->inc(vt->context,
+ value_ptr(n, i, vt->size));
+}
+
+static int insert_at(size_t value_size, struct node *node, unsigned index,
+ uint64_t key, void *value)
+ __dm_written_to_disk(value)
+{
+ uint32_t nr_entries = le32_to_cpu(node->header.nr_entries);
+ __le64 key_le = cpu_to_le64(key);
+
+ if (index > nr_entries ||
+ index >= le32_to_cpu(node->header.max_entries)) {
+ DMERR("too many entries in btree node for insert");
+ __dm_unbless_for_disk(value);
+ return -ENOMEM;
+ }
+
+ __dm_bless_for_disk(&key_le);
+
+ array_insert(node->keys, sizeof(*node->keys), nr_entries, index, &key_le);
+ array_insert(value_base(node), value_size, nr_entries, index, value);
+ node->header.nr_entries = cpu_to_le32(nr_entries + 1);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * We want 3n entries (for some n). This works more nicely for repeated
+ * insert remove loops than (2n + 1).
+ */
+static uint32_t calc_max_entries(size_t value_size, size_t block_size)
+{
+ uint32_t total, n;
+ size_t elt_size = sizeof(uint64_t) + value_size; /* key + value */
+
+ block_size -= sizeof(struct node_header);
+ total = block_size / elt_size;
+ n = total / 3; /* rounds down */
+
+ return 3 * n;
+}
+
+int dm_btree_empty(struct dm_btree_info *info, dm_block_t *root)
+{
+ int r;
+ struct dm_block *b;
+ struct node *n;
+ size_t block_size;
+ uint32_t max_entries;
+
+ r = new_block(info, &b);
+ if (r < 0)
+ return r;
+
+ block_size = dm_bm_block_size(dm_tm_get_bm(info->tm));
+ max_entries = calc_max_entries(info->value_type.size, block_size);
+
+ n = dm_block_data(b);
+ memset(n, 0, block_size);
+ n->header.flags = cpu_to_le32(LEAF_NODE);
+ n->header.nr_entries = cpu_to_le32(0);
+ n->header.max_entries = cpu_to_le32(max_entries);
+ n->header.value_size = cpu_to_le32(info->value_type.size);
+
+ *root = dm_block_location(b);
+ return unlock_block(info, b);
+}
+EXPORT_SYMBOL_GPL(dm_btree_empty);
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Deletion uses a recursive algorithm, since we have limited stack space
+ * we explicitly manage our own stack on the heap.
+ */
+#define MAX_SPINE_DEPTH 64
+struct frame {
+ struct dm_block *b;
+ struct node *n;
+ unsigned level;
+ unsigned nr_children;
+ unsigned current_child;
+};
+
+struct del_stack {
+ struct dm_transaction_manager *tm;
+ int top;
+ struct frame spine[MAX_SPINE_DEPTH];
+};
+
+static int top_frame(struct del_stack *s, struct frame **f)
+{
+ if (s->top < 0) {
+ DMERR("btree deletion stack empty");
+ return -EINVAL;
+ }
+
+ *f = s->spine + s->top;
+
+ return 0;
+}
+
+static int unprocessed_frames(struct del_stack *s)
+{
+ return s->top >= 0;
+}
+
+static int push_frame(struct del_stack *s, dm_block_t b, unsigned level)
+{
+ int r;
+ uint32_t ref_count;
+
+ if (s->top >= MAX_SPINE_DEPTH - 1) {
+ DMERR("btree deletion stack out of memory");
+ return -ENOMEM;
+ }
+
+ r = dm_tm_ref(s->tm, b, &ref_count);
+ if (r)
+ return r;
+
+ if (ref_count > 1)
+ /*
+ * This is a shared node, so we can just decrement it's
+ * reference counter and leave the children.
+ */
+ dm_tm_dec(s->tm, b);
+
+ else {
+ struct frame *f = s->spine + ++s->top;
+
+ r = dm_tm_read_lock(s->tm, b, &btree_node_validator, &f->b);
+ if (r) {
+ s->top--;
+ return r;
+ }
+
+ f->n = dm_block_data(f->b);
+ f->level = level;
+ f->nr_children = le32_to_cpu(f->n->header.nr_entries);
+ f->current_child = 0;
+ }
+
+ return 0;
+}
+
+static void pop_frame(struct del_stack *s)
+{
+ struct frame *f = s->spine + s->top--;
+
+ dm_tm_dec(s->tm, dm_block_location(f->b));
+ dm_tm_unlock(s->tm, f->b);
+}
+
+int dm_btree_del(struct dm_btree_info *info, dm_block_t root)
+{
+ int r;
+ struct del_stack *s;
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+ s->tm = info->tm;
+ s->top = -1;
+
+ r = push_frame(s, root, 1);
+ if (r)
+ goto out;
+
+ while (unprocessed_frames(s)) {
+ uint32_t flags;
+ struct frame *f;
+ dm_block_t b;
+
+ r = top_frame(s, &f);
+ if (r)
+ goto out;
+
+ if (f->current_child >= f->nr_children) {
+ pop_frame(s);
+ continue;
+ }
+
+ flags = le32_to_cpu(f->n->header.flags);
+ if (flags & INTERNAL_NODE) {
+ b = value64(f->n, f->current_child);
+ f->current_child++;
+ r = push_frame(s, b, f->level);
+ if (r)
+ goto out;
+
+ } else if (f->level != (info->levels - 1)) {
+ b = value64(f->n, f->current_child);
+ f->current_child++;
+ r = push_frame(s, b, f->level + 1);
+ if (r)
+ goto out;
+
+ } else {
+ if (info->value_type.dec) {
+ unsigned i;
+
+ for (i = 0; i < f->nr_children; i++)
+ info->value_type.dec(info->value_type.context,
+ value_ptr(f->n, i, info->value_type.size));
+ }
+ f->current_child = f->nr_children;
+ }
+ }
+
+out:
+ kfree(s);
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_del);
+
+/*----------------------------------------------------------------*/
+
+static int btree_lookup_raw(struct ro_spine *s, dm_block_t block, uint64_t key,
+ int (*search_fn)(struct node *, uint64_t),
+ uint64_t *result_key, void *v, size_t value_size)
+{
+ int i, r;
+ uint32_t flags, nr_entries;
+
+ do {
+ r = ro_step(s, block);
+ if (r < 0)
+ return r;
+
+ i = search_fn(ro_node(s), key);
+
+ flags = le32_to_cpu(ro_node(s)->header.flags);
+ nr_entries = le32_to_cpu(ro_node(s)->header.nr_entries);
+ if (i < 0 || i >= nr_entries)
+ return -ENODATA;
+
+ if (flags & INTERNAL_NODE)
+ block = value64(ro_node(s), i);
+
+ } while (!(flags & LEAF_NODE));
+
+ *result_key = le64_to_cpu(ro_node(s)->keys[i]);
+ memcpy(v, value_ptr(ro_node(s), i, value_size), value_size);
+
+ return 0;
+}
+
+int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value_le)
+{
+ unsigned level, last_level = info->levels - 1;
+ int r = -ENODATA;
+ uint64_t rkey;
+ __le64 internal_value_le;
+ struct ro_spine spine;
+
+ init_ro_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ size_t size;
+ void *value_p;
+
+ if (level == last_level) {
+ value_p = value_le;
+ size = info->value_type.size;
+
+ } else {
+ value_p = &internal_value_le;
+ size = sizeof(uint64_t);
+ }
+
+ r = btree_lookup_raw(&spine, root, keys[level],
+ lower_bound, &rkey,
+ value_p, size);
+
+ if (!r) {
+ if (rkey != keys[level]) {
+ exit_ro_spine(&spine);
+ return -ENODATA;
+ }
+ } else {
+ exit_ro_spine(&spine);
+ return r;
+ }
+
+ root = le64_to_cpu(internal_value_le);
+ }
+ exit_ro_spine(&spine);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_lookup);
+
+/*
+ * Splits a node by creating a sibling node and shifting half the nodes
+ * contents across. Assumes there is a parent node, and it has room for
+ * another child.
+ *
+ * Before:
+ * +--------+
+ * | Parent |
+ * +--------+
+ * |
+ * v
+ * +----------+
+ * | A ++++++ |
+ * +----------+
+ *
+ *
+ * After:
+ * +--------+
+ * | Parent |
+ * +--------+
+ * | |
+ * v +------+
+ * +---------+ |
+ * | A* +++ | v
+ * +---------+ +-------+
+ * | B +++ |
+ * +-------+
+ *
+ * Where A* is a shadow of A.
+ */
+static int btree_split_sibling(struct shadow_spine *s, dm_block_t root,
+ unsigned parent_index, uint64_t key)
+{
+ int r;
+ size_t size;
+ unsigned nr_left, nr_right;
+ struct dm_block *left, *right, *parent;
+ struct node *ln, *rn, *pn;
+ __le64 location;
+
+ left = shadow_current(s);
+
+ r = new_block(s->info, &right);
+ if (r < 0)
+ return r;
+
+ ln = dm_block_data(left);
+ rn = dm_block_data(right);
+
+ nr_left = le32_to_cpu(ln->header.nr_entries) / 2;
+ nr_right = le32_to_cpu(ln->header.nr_entries) - nr_left;
+
+ ln->header.nr_entries = cpu_to_le32(nr_left);
+
+ rn->header.flags = ln->header.flags;
+ rn->header.nr_entries = cpu_to_le32(nr_right);
+ rn->header.max_entries = ln->header.max_entries;
+ rn->header.value_size = ln->header.value_size;
+ memcpy(rn->keys, ln->keys + nr_left, nr_right * sizeof(rn->keys[0]));
+
+ size = le32_to_cpu(ln->header.flags) & INTERNAL_NODE ?
+ sizeof(uint64_t) : s->info->value_type.size;
+ memcpy(value_ptr(rn, 0, size), value_ptr(ln, nr_left, size),
+ size * nr_right);
+
+ /*
+ * Patch up the parent
+ */
+ parent = shadow_parent(s);
+
+ pn = dm_block_data(parent);
+ location = cpu_to_le64(dm_block_location(left));
+ __dm_bless_for_disk(&location);
+ memcpy_disk(value_ptr(pn, parent_index, sizeof(__le64)),
+ &location, sizeof(__le64));
+
+ location = cpu_to_le64(dm_block_location(right));
+ __dm_bless_for_disk(&location);
+
+ r = insert_at(sizeof(__le64), pn, parent_index + 1,
+ le64_to_cpu(rn->keys[0]), &location);
+ if (r)
+ return r;
+
+ if (key < le64_to_cpu(rn->keys[0])) {
+ unlock_block(s->info, right);
+ s->nodes[1] = left;
+ } else {
+ unlock_block(s->info, left);
+ s->nodes[1] = right;
+ }
+
+ return 0;
+}
+
+/*
+ * Splits a node by creating two new children beneath the given node.
+ *
+ * Before:
+ * +----------+
+ * | A ++++++ |
+ * +----------+
+ *
+ *
+ * After:
+ * +------------+
+ * | A (shadow) |
+ * +------------+
+ * | |
+ * +------+ +----+
+ * | |
+ * v v
+ * +-------+ +-------+
+ * | B +++ | | C +++ |
+ * +-------+ +-------+
+ */
+static int btree_split_beneath(struct shadow_spine *s, uint64_t key)
+{
+ int r;
+ size_t size;
+ unsigned nr_left, nr_right;
+ struct dm_block *left, *right, *new_parent;
+ struct node *pn, *ln, *rn;
+ __le64 val;
+
+ new_parent = shadow_current(s);
+
+ r = new_block(s->info, &left);
+ if (r < 0)
+ return r;
+
+ r = new_block(s->info, &right);
+ if (r < 0) {
+ /* FIXME: put left */
+ return r;
+ }
+
+ pn = dm_block_data(new_parent);
+ ln = dm_block_data(left);
+ rn = dm_block_data(right);
+
+ nr_left = le32_to_cpu(pn->header.nr_entries) / 2;
+ nr_right = le32_to_cpu(pn->header.nr_entries) - nr_left;
+
+ ln->header.flags = pn->header.flags;
+ ln->header.nr_entries = cpu_to_le32(nr_left);
+ ln->header.max_entries = pn->header.max_entries;
+ ln->header.value_size = pn->header.value_size;
+
+ rn->header.flags = pn->header.flags;
+ rn->header.nr_entries = cpu_to_le32(nr_right);
+ rn->header.max_entries = pn->header.max_entries;
+ rn->header.value_size = pn->header.value_size;
+
+ memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0]));
+ memcpy(rn->keys, pn->keys + nr_left, nr_right * sizeof(pn->keys[0]));
+
+ size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ?
+ sizeof(__le64) : s->info->value_type.size;
+ memcpy(value_ptr(ln, 0, size), value_ptr(pn, 0, size), nr_left * size);
+ memcpy(value_ptr(rn, 0, size), value_ptr(pn, nr_left, size),
+ nr_right * size);
+
+ /* new_parent should just point to l and r now */
+ pn->header.flags = cpu_to_le32(INTERNAL_NODE);
+ pn->header.nr_entries = cpu_to_le32(2);
+ pn->header.max_entries = cpu_to_le32(
+ calc_max_entries(sizeof(__le64),
+ dm_bm_block_size(
+ dm_tm_get_bm(s->info->tm))));
+ pn->header.value_size = cpu_to_le32(sizeof(__le64));
+
+ val = cpu_to_le64(dm_block_location(left));
+ __dm_bless_for_disk(&val);
+ pn->keys[0] = ln->keys[0];
+ memcpy_disk(value_ptr(pn, 0, sizeof(__le64)), &val, sizeof(__le64));
+
+ val = cpu_to_le64(dm_block_location(right));
+ __dm_bless_for_disk(&val);
+ pn->keys[1] = rn->keys[0];
+ memcpy_disk(value_ptr(pn, 1, sizeof(__le64)), &val, sizeof(__le64));
+
+ /*
+ * rejig the spine. This is ugly, since it knows too
+ * much about the spine
+ */
+ if (s->nodes[0] != new_parent) {
+ unlock_block(s->info, s->nodes[0]);
+ s->nodes[0] = new_parent;
+ }
+ if (key < le64_to_cpu(rn->keys[0])) {
+ unlock_block(s->info, right);
+ s->nodes[1] = left;
+ } else {
+ unlock_block(s->info, left);
+ s->nodes[1] = right;
+ }
+ s->count = 2;
+
+ return 0;
+}
+
+static int btree_insert_raw(struct shadow_spine *s, dm_block_t root,
+ struct dm_btree_value_type *vt,
+ uint64_t key, unsigned *index)
+{
+ int r, i = *index, top = 1;
+ struct node *node;
+
+ for (;;) {
+ r = shadow_step(s, root, vt);
+ if (r < 0)
+ return r;
+
+ node = dm_block_data(shadow_current(s));
+
+ /*
+ * We have to patch up the parent node, ugly, but I don't
+ * see a way to do this automatically as part of the spine
+ * op.
+ */
+ if (shadow_has_parent(s) && i >= 0) { /* FIXME: second clause unness. */
+ __le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
+
+ __dm_bless_for_disk(&location);
+ memcpy_disk(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(uint64_t)),
+ &location, sizeof(__le64));
+ }
+
+ node = dm_block_data(shadow_current(s));
+
+ if (node->header.nr_entries == node->header.max_entries) {
+ if (top)
+ r = btree_split_beneath(s, key);
+ else
+ r = btree_split_sibling(s, root, i, key);
+
+ if (r < 0)
+ return r;
+ }
+
+ node = dm_block_data(shadow_current(s));
+
+ i = lower_bound(node, key);
+
+ if (le32_to_cpu(node->header.flags) & LEAF_NODE)
+ break;
+
+ if (i < 0) {
+ /* change the bounds on the lowest key */
+ node->keys[0] = cpu_to_le64(key);
+ i = 0;
+ }
+
+ root = value64(node, i);
+ top = 0;
+ }
+
+ if (i < 0 || le64_to_cpu(node->keys[i]) != key)
+ i++;
+
+ *index = i;
+ return 0;
+}
+
+static int insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value)
+{
+ int r, need_insert;
+ unsigned level, index = -1, last_level = info->levels - 1;
+ dm_block_t block = root;
+ struct shadow_spine spine;
+ struct node *n;
+ struct dm_btree_value_type le64_type;
+
+ le64_type.context = NULL;
+ le64_type.size = sizeof(__le64);
+ le64_type.inc = NULL;
+ le64_type.dec = NULL;
+ le64_type.equal = NULL;
+
+ init_shadow_spine(&spine, info);
+
+ for (level = 0; level < (info->levels - 1); level++) {
+ r = btree_insert_raw(&spine, block, &le64_type, keys[level], &index);
+ if (r < 0)
+ goto bad;
+
+ n = dm_block_data(shadow_current(&spine));
+ need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[index]) != keys[level]));
+
+ if (need_insert) {
+ dm_block_t new_tree;
+ __le64 new_le;
+
+ r = dm_btree_empty(info, &new_tree);
+ if (r < 0)
+ goto bad;
+
+ new_le = cpu_to_le64(new_tree);
+ __dm_bless_for_disk(&new_le);
+
+ r = insert_at(sizeof(uint64_t), n, index,
+ keys[level], &new_le);
+ if (r)
+ goto bad;
+ }
+
+ if (level < last_level)
+ block = value64(n, index);
+ }
+
+ r = btree_insert_raw(&spine, block, &info->value_type,
+ keys[level], &index);
+ if (r < 0)
+ goto bad;
+
+ n = dm_block_data(shadow_current(&spine));
+ need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
+ (le64_to_cpu(n->keys[index]) != keys[level]));
+
+ if (need_insert) {
+ if (inserted)
+ *inserted = 1;
+
+ r = insert_at(info->value_type.size, n, index,
+ keys[level], value);
+ if (r)
+ goto bad_unblessed;
+ } else {
+ if (inserted)
+ *inserted = 0;
+
+ if (info->value_type.dec &&
+ (!info->value_type.equal ||
+ !info->value_type.equal(
+ info->value_type.context,
+ value_ptr(n, index, info->value_type.size),
+ value))) {
+ info->value_type.dec(info->value_type.context,
+ value_ptr(n, index, info->value_type.size));
+ }
+ memcpy_disk(value_ptr(n, index, info->value_type.size),
+ value, info->value_type.size);
+ }
+
+ *new_root = shadow_root(&spine);
+ exit_shadow_spine(&spine);
+
+ return 0;
+
+bad:
+ __dm_unbless_for_disk(value);
+bad_unblessed:
+ exit_shadow_spine(&spine);
+ return r;
+}
+
+int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root)
+ __dm_written_to_disk(value)
+{
+ return insert(info, root, keys, value, new_root, NULL);
+}
+EXPORT_SYMBOL_GPL(dm_btree_insert);
+
+int dm_btree_insert_notify(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value)
+{
+ return insert(info, root, keys, value, new_root, inserted);
+}
+EXPORT_SYMBOL_GPL(dm_btree_insert_notify);
+
+/*----------------------------------------------------------------*/
+
+static int find_highest_key(struct ro_spine *s, dm_block_t block,
+ uint64_t *result_key, dm_block_t *next_block)
+{
+ int i, r;
+ uint32_t flags;
+
+ do {
+ r = ro_step(s, block);
+ if (r < 0)
+ return r;
+
+ flags = le32_to_cpu(ro_node(s)->header.flags);
+ i = le32_to_cpu(ro_node(s)->header.nr_entries);
+ if (!i)
+ return -ENODATA;
+ else
+ i--;
+
+ *result_key = le64_to_cpu(ro_node(s)->keys[i]);
+ if (next_block || flags & INTERNAL_NODE)
+ block = value64(ro_node(s), i);
+
+ } while (flags & INTERNAL_NODE);
+
+ if (next_block)
+ *next_block = block;
+ return 0;
+}
+
+int dm_btree_find_highest_key(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *result_keys)
+{
+ int r = 0, count = 0, level;
+ struct ro_spine spine;
+
+ init_ro_spine(&spine, info);
+ for (level = 0; level < info->levels; level++) {
+ r = find_highest_key(&spine, root, result_keys + level,
+ level == info->levels - 1 ? NULL : &root);
+ if (r == -ENODATA) {
+ r = 0;
+ break;
+
+ } else if (r)
+ break;
+
+ count++;
+ }
+ exit_ro_spine(&spine);
+
+ return r ? r : count;
+}
+EXPORT_SYMBOL_GPL(dm_btree_find_highest_key);
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h
new file mode 100644
index 000000000000..ae02c84410ff
--- /dev/null
+++ b/drivers/md/persistent-data/dm-btree.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#ifndef _LINUX_DM_BTREE_H
+#define _LINUX_DM_BTREE_H
+
+#include "dm-block-manager.h"
+
+struct dm_transaction_manager;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Annotations used to check on-disk metadata is handled as little-endian.
+ */
+#ifdef __CHECKER__
+# define __dm_written_to_disk(x) __releases(x)
+# define __dm_reads_from_disk(x) __acquires(x)
+# define __dm_bless_for_disk(x) __acquire(x)
+# define __dm_unbless_for_disk(x) __release(x)
+#else
+# define __dm_written_to_disk(x)
+# define __dm_reads_from_disk(x)
+# define __dm_bless_for_disk(x)
+# define __dm_unbless_for_disk(x)
+#endif
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Manipulates hierarchical B+ trees with 64-bit keys and arbitrary-sized
+ * values.
+ */
+
+/*
+ * Infomation about the values stored within the btree.
+ */
+struct dm_btree_value_type {
+ void *context;
+
+ /*
+ * The size in bytes of each value.
+ */
+ uint32_t size;
+
+ /*
+ * Any of these methods can be safely set to NULL if you do not
+ * need the corresponding feature.
+ */
+
+ /*
+ * The btree is making a duplicate of the value, for instance
+ * because previously-shared btree nodes have now diverged.
+ * @value argument is the new copy that the copy function may modify.
+ * (Probably it just wants to increment a reference count
+ * somewhere.) This method is _not_ called for insertion of a new
+ * value: It is assumed the ref count is already 1.
+ */
+ void (*inc)(void *context, void *value);
+
+ /*
+ * This value is being deleted. The btree takes care of freeing
+ * the memory pointed to by @value. Often the del function just
+ * needs to decrement a reference count somewhere.
+ */
+ void (*dec)(void *context, void *value);
+
+ /*
+ * A test for equality between two values. When a value is
+ * overwritten with a new one, the old one has the dec method
+ * called _unless_ the new and old value are deemed equal.
+ */
+ int (*equal)(void *context, void *value1, void *value2);
+};
+
+/*
+ * The shape and contents of a btree.
+ */
+struct dm_btree_info {
+ struct dm_transaction_manager *tm;
+
+ /*
+ * Number of nested btrees. (Not the depth of a single tree.)
+ */
+ unsigned levels;
+ struct dm_btree_value_type value_type;
+};
+
+/*
+ * Set up an empty tree. O(1).
+ */
+int dm_btree_empty(struct dm_btree_info *info, dm_block_t *root);
+
+/*
+ * Delete a tree. O(n) - this is the slow one! It can also block, so
+ * please don't call it on an IO path.
+ */
+int dm_btree_del(struct dm_btree_info *info, dm_block_t root);
+
+/*
+ * All the lookup functions return -ENODATA if the key cannot be found.
+ */
+
+/*
+ * Tries to find a key that matches exactly. O(ln(n))
+ */
+int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value_le);
+
+/*
+ * Insertion (or overwrite an existing value). O(ln(n))
+ */
+int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root)
+ __dm_written_to_disk(value);
+
+/*
+ * A variant of insert that indicates whether it actually inserted or just
+ * overwrote. Useful if you're keeping track of the number of entries in a
+ * tree.
+ */
+int dm_btree_insert_notify(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, void *value, dm_block_t *new_root,
+ int *inserted)
+ __dm_written_to_disk(value);
+
+/*
+ * Remove a key if present. This doesn't remove empty sub trees. Normally
+ * subtrees represent a separate entity, like a snapshot map, so this is
+ * correct behaviour. O(ln(n)).
+ */
+int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, dm_block_t *new_root);
+
+/*
+ * Returns < 0 on failure. Otherwise the number of key entries that have
+ * been filled out. Remember trees can have zero entries, and as such have
+ * no highest key.
+ */
+int dm_btree_find_highest_key(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *result_keys);
+
+#endif /* _LINUX_DM_BTREE_H */
diff --git a/drivers/md/persistent-data/dm-persistent-data-internal.h b/drivers/md/persistent-data/dm-persistent-data-internal.h
new file mode 100644
index 000000000000..c49e26fff36c
--- /dev/null
+++ b/drivers/md/persistent-data/dm-persistent-data-internal.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _DM_PERSISTENT_DATA_INTERNAL_H
+#define _DM_PERSISTENT_DATA_INTERNAL_H
+
+#include "dm-block-manager.h"
+
+static inline unsigned dm_hash_block(dm_block_t b, unsigned hash_mask)
+{
+ const unsigned BIG_PRIME = 4294967291UL;
+
+ return (((unsigned) b) * BIG_PRIME) & hash_mask;
+}
+
+#endif /* _PERSISTENT_DATA_INTERNAL_H */
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c
new file mode 100644
index 000000000000..bb44a937fe63
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-checker.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map-checker.h"
+
+#include <linux/device-mapper.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;
+ ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL);
+ if (!ca->counts)
+ return -ENOMEM;
+
+ return 0;
+}
+
+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 = kzalloc(sizeof(*counts) * nr_blocks, GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
+ kfree(ca->counts);
+ 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;
+}
+
+static void ca_destroy(struct count_array *ca)
+{
+ kfree(ca->counts);
+}
+
+/*----------------------------------------------------------------*/
+
+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 (!sm)
+ return NULL;
+
+ smc = kmalloc(sizeof(*smc), GFP_KERNEL);
+ if (!smc)
+ return NULL;
+
+ memcpy(&smc->sm, &ops_, sizeof(smc->sm));
+ r = ca_create(&smc->old_counts, sm);
+ if (r) {
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_create(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ smc->real_sm = sm;
+
+ r = ca_load(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->counts);
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_commit(&smc->old_counts, &smc->counts);
+ if (r) {
+ ca_destroy(&smc->counts);
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ 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 (!sm)
+ return NULL;
+
+ smc = kmalloc(sizeof(*smc), GFP_KERNEL);
+ if (!smc)
+ return NULL;
+
+ memcpy(&smc->sm, &ops_, sizeof(smc->sm));
+ r = ca_create(&smc->old_counts, sm);
+ if (r) {
+ kfree(smc);
+ return NULL;
+ }
+
+ r = ca_create(&smc->counts, sm);
+ if (r) {
+ ca_destroy(&smc->old_counts);
+ kfree(smc);
+ return NULL;
+ }
+
+ 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
new file mode 100644
index 000000000000..444dccf6688c
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-checker.h
@@ -0,0 +1,26 @@
+/*
+ * 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
new file mode 100644
index 000000000000..df2494c06cdc
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-common.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map-common.h"
+#include "dm-transaction-manager.h"
+
+#include <linux/bitops.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map common"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Index validator.
+ */
+#define INDEX_CSUM_XOR 160478
+
+static void index_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_metadata_index *mi_le = dm_block_data(b);
+
+ mi_le->blocknr = cpu_to_le64(dm_block_location(b));
+ mi_le->csum = cpu_to_le32(dm_bm_checksum(&mi_le->padding,
+ block_size - sizeof(__le32),
+ INDEX_CSUM_XOR));
+}
+
+static int index_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_metadata_index *mi_le = dm_block_data(b);
+ __le32 csum_disk;
+
+ if (dm_block_location(b) != le64_to_cpu(mi_le->blocknr)) {
+ DMERR("index_check failed blocknr %llu wanted %llu",
+ le64_to_cpu(mi_le->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&mi_le->padding,
+ block_size - sizeof(__le32),
+ INDEX_CSUM_XOR));
+ if (csum_disk != mi_le->csum) {
+ DMERR("index_check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(mi_le->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator index_validator = {
+ .name = "index",
+ .prepare_for_write = index_prepare_for_write,
+ .check = index_check
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Bitmap validator
+ */
+#define BITMAP_CSUM_XOR 240779
+
+static void bitmap_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_bitmap_header *disk_header = dm_block_data(b);
+
+ disk_header->blocknr = cpu_to_le64(dm_block_location(b));
+ disk_header->csum = cpu_to_le32(dm_bm_checksum(&disk_header->not_used,
+ block_size - sizeof(__le32),
+ BITMAP_CSUM_XOR));
+}
+
+static int bitmap_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t block_size)
+{
+ struct disk_bitmap_header *disk_header = dm_block_data(b);
+ __le32 csum_disk;
+
+ if (dm_block_location(b) != le64_to_cpu(disk_header->blocknr)) {
+ DMERR("bitmap check failed blocknr %llu wanted %llu",
+ le64_to_cpu(disk_header->blocknr), dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ csum_disk = cpu_to_le32(dm_bm_checksum(&disk_header->not_used,
+ block_size - sizeof(__le32),
+ BITMAP_CSUM_XOR));
+ if (csum_disk != disk_header->csum) {
+ DMERR("bitmap check failed csum %u wanted %u",
+ le32_to_cpu(csum_disk), le32_to_cpu(disk_header->csum));
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static struct dm_block_validator dm_sm_bitmap_validator = {
+ .name = "sm_bitmap",
+ .prepare_for_write = bitmap_prepare_for_write,
+ .check = bitmap_check
+};
+
+/*----------------------------------------------------------------*/
+
+#define ENTRIES_PER_WORD 32
+#define ENTRIES_SHIFT 5
+
+static void *dm_bitmap_data(struct dm_block *b)
+{
+ return dm_block_data(b) + sizeof(struct disk_bitmap_header);
+}
+
+#define WORD_MASK_HIGH 0xAAAAAAAAAAAAAAAAULL
+
+static unsigned bitmap_word_used(void *addr, unsigned b)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+
+ uint64_t bits = le64_to_cpu(*w_le);
+ uint64_t mask = (bits + WORD_MASK_HIGH + 1) & WORD_MASK_HIGH;
+
+ return !(~bits & mask);
+}
+
+static unsigned sm_lookup_bitmap(void *addr, unsigned b)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+ unsigned hi, lo;
+
+ b = (b & (ENTRIES_PER_WORD - 1)) << 1;
+ hi = !!test_bit_le(b, (void *) w_le);
+ lo = !!test_bit_le(b + 1, (void *) w_le);
+ return (hi << 1) | lo;
+}
+
+static void sm_set_bitmap(void *addr, unsigned b, unsigned val)
+{
+ __le64 *words_le = addr;
+ __le64 *w_le = words_le + (b >> ENTRIES_SHIFT);
+
+ b = (b & (ENTRIES_PER_WORD - 1)) << 1;
+
+ if (val & 2)
+ __set_bit_le(b, (void *) w_le);
+ else
+ __clear_bit_le(b, (void *) w_le);
+
+ if (val & 1)
+ __set_bit_le(b + 1, (void *) w_le);
+ else
+ __clear_bit_le(b + 1, (void *) w_le);
+}
+
+static int sm_find_free(void *addr, unsigned begin, unsigned end,
+ unsigned *result)
+{
+ while (begin < end) {
+ if (!(begin & (ENTRIES_PER_WORD - 1)) &&
+ bitmap_word_used(addr, begin)) {
+ begin += ENTRIES_PER_WORD;
+ continue;
+ }
+
+ if (!sm_lookup_bitmap(addr, begin)) {
+ *result = begin;
+ return 0;
+ }
+
+ begin++;
+ }
+
+ return -ENOSPC;
+}
+
+/*----------------------------------------------------------------*/
+
+static int sm_ll_init(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ ll->tm = tm;
+
+ ll->bitmap_info.tm = tm;
+ ll->bitmap_info.levels = 1;
+
+ /*
+ * Because the new bitmap blocks are created via a shadow
+ * operation, the old entry has already had its reference count
+ * decremented and we don't need the btree to do any bookkeeping.
+ */
+ ll->bitmap_info.value_type.size = sizeof(struct disk_index_entry);
+ ll->bitmap_info.value_type.inc = NULL;
+ ll->bitmap_info.value_type.dec = NULL;
+ ll->bitmap_info.value_type.equal = NULL;
+
+ ll->ref_count_info.tm = tm;
+ ll->ref_count_info.levels = 1;
+ ll->ref_count_info.value_type.size = sizeof(uint32_t);
+ ll->ref_count_info.value_type.inc = NULL;
+ ll->ref_count_info.value_type.dec = NULL;
+ ll->ref_count_info.value_type.equal = NULL;
+
+ ll->block_size = dm_bm_block_size(dm_tm_get_bm(tm));
+
+ if (ll->block_size > (1 << 30)) {
+ DMERR("block size too big to hold bitmaps");
+ return -EINVAL;
+ }
+
+ ll->entries_per_block = (ll->block_size - sizeof(struct disk_bitmap_header)) *
+ ENTRIES_PER_BYTE;
+ ll->nr_blocks = 0;
+ ll->bitmap_root = 0;
+ ll->ref_count_root = 0;
+
+ return 0;
+}
+
+int sm_ll_extend(struct ll_disk *ll, dm_block_t extra_blocks)
+{
+ int r;
+ dm_block_t i, nr_blocks, nr_indexes;
+ unsigned old_blocks, blocks;
+
+ nr_blocks = ll->nr_blocks + extra_blocks;
+ old_blocks = dm_sector_div_up(ll->nr_blocks, ll->entries_per_block);
+ blocks = dm_sector_div_up(nr_blocks, ll->entries_per_block);
+
+ nr_indexes = dm_sector_div_up(nr_blocks, ll->entries_per_block);
+ if (nr_indexes > ll->max_entries(ll)) {
+ DMERR("space map too large");
+ return -EINVAL;
+ }
+
+ for (i = old_blocks; i < blocks; i++) {
+ struct dm_block *b;
+ struct disk_index_entry idx;
+
+ r = dm_tm_new_block(ll->tm, &dm_sm_bitmap_validator, &b);
+ if (r < 0)
+ return r;
+ idx.blocknr = cpu_to_le64(dm_block_location(b));
+
+ r = dm_tm_unlock(ll->tm, b);
+ if (r < 0)
+ return r;
+
+ idx.nr_free = cpu_to_le32(ll->entries_per_block);
+ idx.none_free_before = 0;
+
+ r = ll->save_ie(ll, i, &idx);
+ if (r < 0)
+ return r;
+ }
+
+ ll->nr_blocks = nr_blocks;
+ return 0;
+}
+
+int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result)
+{
+ int r;
+ dm_block_t index = b;
+ struct disk_index_entry ie_disk;
+ struct dm_block *blk;
+
+ b = do_div(index, ll->entries_per_block);
+ r = ll->load_ie(ll, index, &ie_disk);
+ if (r < 0)
+ return r;
+
+ r = dm_tm_read_lock(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &blk);
+ if (r < 0)
+ return r;
+
+ *result = sm_lookup_bitmap(dm_bitmap_data(blk), b);
+
+ return dm_tm_unlock(ll->tm, blk);
+}
+
+int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result)
+{
+ __le32 le_rc;
+ int r = sm_ll_lookup_bitmap(ll, b, result);
+
+ if (r)
+ return r;
+
+ if (*result != 3)
+ return r;
+
+ r = dm_btree_lookup(&ll->ref_count_info, ll->ref_count_root, &b, &le_rc);
+ if (r < 0)
+ return r;
+
+ *result = le32_to_cpu(le_rc);
+
+ return r;
+}
+
+int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
+ dm_block_t end, dm_block_t *result)
+{
+ int r;
+ struct disk_index_entry ie_disk;
+ dm_block_t i, index_begin = begin;
+ dm_block_t index_end = dm_sector_div_up(end, ll->entries_per_block);
+
+ /*
+ * FIXME: Use shifts
+ */
+ begin = do_div(index_begin, ll->entries_per_block);
+ end = do_div(end, ll->entries_per_block);
+
+ for (i = index_begin; i < index_end; i++, begin = 0) {
+ struct dm_block *blk;
+ unsigned position;
+ uint32_t bit_end;
+
+ r = ll->load_ie(ll, i, &ie_disk);
+ if (r < 0)
+ return r;
+
+ if (le32_to_cpu(ie_disk.nr_free) == 0)
+ continue;
+
+ r = dm_tm_read_lock(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &blk);
+ if (r < 0)
+ return r;
+
+ bit_end = (i == index_end - 1) ? end : ll->entries_per_block;
+
+ r = sm_find_free(dm_bitmap_data(blk),
+ max_t(unsigned, begin, le32_to_cpu(ie_disk.none_free_before)),
+ bit_end, &position);
+ if (r == -ENOSPC) {
+ /*
+ * This might happen because we started searching
+ * part way through the bitmap.
+ */
+ dm_tm_unlock(ll->tm, blk);
+ continue;
+
+ } else if (r < 0) {
+ dm_tm_unlock(ll->tm, blk);
+ return r;
+ }
+
+ r = dm_tm_unlock(ll->tm, blk);
+ if (r < 0)
+ return r;
+
+ *result = i * ll->entries_per_block + (dm_block_t) position;
+ return 0;
+ }
+
+ return -ENOSPC;
+}
+
+int sm_ll_insert(struct ll_disk *ll, dm_block_t b,
+ uint32_t ref_count, enum allocation_event *ev)
+{
+ int r;
+ uint32_t bit, old;
+ struct dm_block *nb;
+ dm_block_t index = b;
+ struct disk_index_entry ie_disk;
+ void *bm_le;
+ int inc;
+
+ bit = do_div(index, ll->entries_per_block);
+ r = ll->load_ie(ll, index, &ie_disk);
+ if (r < 0)
+ return r;
+
+ r = dm_tm_shadow_block(ll->tm, le64_to_cpu(ie_disk.blocknr),
+ &dm_sm_bitmap_validator, &nb, &inc);
+ if (r < 0) {
+ DMERR("dm_tm_shadow_block() failed");
+ return r;
+ }
+ ie_disk.blocknr = cpu_to_le64(dm_block_location(nb));
+
+ bm_le = dm_bitmap_data(nb);
+ old = sm_lookup_bitmap(bm_le, bit);
+
+ if (ref_count <= 2) {
+ sm_set_bitmap(bm_le, bit, ref_count);
+
+ r = dm_tm_unlock(ll->tm, nb);
+ if (r < 0)
+ return r;
+
+#if 0
+ /* FIXME: dm_btree_remove doesn't handle this yet */
+ if (old > 2) {
+ r = dm_btree_remove(&ll->ref_count_info,
+ ll->ref_count_root,
+ &b, &ll->ref_count_root);
+ if (r)
+ return r;
+ }
+#endif
+
+ } else {
+ __le32 le_rc = cpu_to_le32(ref_count);
+
+ sm_set_bitmap(bm_le, bit, 3);
+ r = dm_tm_unlock(ll->tm, nb);
+ if (r < 0)
+ return r;
+
+ __dm_bless_for_disk(&le_rc);
+ r = dm_btree_insert(&ll->ref_count_info, ll->ref_count_root,
+ &b, &le_rc, &ll->ref_count_root);
+ if (r < 0) {
+ DMERR("ref count insert failed");
+ return r;
+ }
+ }
+
+ if (ref_count && !old) {
+ *ev = SM_ALLOC;
+ ll->nr_allocated++;
+ ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) - 1);
+ if (le32_to_cpu(ie_disk.none_free_before) == bit)
+ ie_disk.none_free_before = cpu_to_le32(bit + 1);
+
+ } else if (old && !ref_count) {
+ *ev = SM_FREE;
+ ll->nr_allocated--;
+ ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) + 1);
+ ie_disk.none_free_before = cpu_to_le32(min(le32_to_cpu(ie_disk.none_free_before), bit));
+ }
+
+ return ll->save_ie(ll, index, &ie_disk);
+}
+
+int sm_ll_inc(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
+{
+ int r;
+ uint32_t rc;
+
+ r = sm_ll_lookup(ll, b, &rc);
+ if (r)
+ return r;
+
+ return sm_ll_insert(ll, b, rc + 1, ev);
+}
+
+int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
+{
+ int r;
+ uint32_t rc;
+
+ r = sm_ll_lookup(ll, b, &rc);
+ if (r)
+ return r;
+
+ if (!rc)
+ return -EINVAL;
+
+ return sm_ll_insert(ll, b, rc - 1, ev);
+}
+
+int sm_ll_commit(struct ll_disk *ll)
+{
+ return ll->commit(ll);
+}
+
+/*----------------------------------------------------------------*/
+
+static int metadata_ll_load_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ memcpy(ie, ll->mi_le.index + index, sizeof(*ie));
+ return 0;
+}
+
+static int metadata_ll_save_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ memcpy(ll->mi_le.index + index, ie, sizeof(*ie));
+ return 0;
+}
+
+static int metadata_ll_init_index(struct ll_disk *ll)
+{
+ int r;
+ struct dm_block *b;
+
+ r = dm_tm_new_block(ll->tm, &index_validator, &b);
+ if (r < 0)
+ return r;
+
+ memcpy(dm_block_data(b), &ll->mi_le, sizeof(ll->mi_le));
+ ll->bitmap_root = dm_block_location(b);
+
+ return dm_tm_unlock(ll->tm, b);
+}
+
+static int metadata_ll_open(struct ll_disk *ll)
+{
+ int r;
+ struct dm_block *block;
+
+ r = dm_tm_read_lock(ll->tm, ll->bitmap_root,
+ &index_validator, &block);
+ if (r)
+ return r;
+
+ memcpy(&ll->mi_le, dm_block_data(block), sizeof(ll->mi_le));
+ return dm_tm_unlock(ll->tm, block);
+}
+
+static dm_block_t metadata_ll_max_entries(struct ll_disk *ll)
+{
+ return MAX_METADATA_BITMAPS;
+}
+
+static int metadata_ll_commit(struct ll_disk *ll)
+{
+ int r, inc;
+ struct dm_block *b;
+
+ r = dm_tm_shadow_block(ll->tm, ll->bitmap_root, &index_validator, &b, &inc);
+ if (r)
+ return r;
+
+ memcpy(dm_block_data(b), &ll->mi_le, sizeof(ll->mi_le));
+ ll->bitmap_root = dm_block_location(b);
+
+ return dm_tm_unlock(ll->tm, b);
+}
+
+int sm_ll_new_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ int r;
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = metadata_ll_load_ie;
+ ll->save_ie = metadata_ll_save_ie;
+ ll->init_index = metadata_ll_init_index;
+ ll->open_index = metadata_ll_open;
+ ll->max_entries = metadata_ll_max_entries;
+ ll->commit = metadata_ll_commit;
+
+ ll->nr_blocks = 0;
+ ll->nr_allocated = 0;
+
+ r = ll->init_index(ll);
+ if (r < 0)
+ return r;
+
+ r = dm_btree_empty(&ll->ref_count_info, &ll->ref_count_root);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct disk_sm_root *smr = root_le;
+
+ if (len < sizeof(struct disk_sm_root)) {
+ DMERR("sm_metadata root too small");
+ return -ENOMEM;
+ }
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = metadata_ll_load_ie;
+ ll->save_ie = metadata_ll_save_ie;
+ ll->init_index = metadata_ll_init_index;
+ ll->open_index = metadata_ll_open;
+ ll->max_entries = metadata_ll_max_entries;
+ ll->commit = metadata_ll_commit;
+
+ ll->nr_blocks = le64_to_cpu(smr->nr_blocks);
+ ll->nr_allocated = le64_to_cpu(smr->nr_allocated);
+ ll->bitmap_root = le64_to_cpu(smr->bitmap_root);
+ ll->ref_count_root = le64_to_cpu(smr->ref_count_root);
+
+ return ll->open_index(ll);
+}
+
+/*----------------------------------------------------------------*/
+
+static int disk_ll_load_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ return dm_btree_lookup(&ll->bitmap_info, ll->bitmap_root, &index, ie);
+}
+
+static int disk_ll_save_ie(struct ll_disk *ll, dm_block_t index,
+ struct disk_index_entry *ie)
+{
+ __dm_bless_for_disk(ie);
+ return dm_btree_insert(&ll->bitmap_info, ll->bitmap_root,
+ &index, ie, &ll->bitmap_root);
+}
+
+static int disk_ll_init_index(struct ll_disk *ll)
+{
+ return dm_btree_empty(&ll->bitmap_info, &ll->bitmap_root);
+}
+
+static int disk_ll_open(struct ll_disk *ll)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static dm_block_t disk_ll_max_entries(struct ll_disk *ll)
+{
+ return -1ULL;
+}
+
+static int disk_ll_commit(struct ll_disk *ll)
+{
+ return 0;
+}
+
+int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm)
+{
+ int r;
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = disk_ll_load_ie;
+ ll->save_ie = disk_ll_save_ie;
+ ll->init_index = disk_ll_init_index;
+ ll->open_index = disk_ll_open;
+ ll->max_entries = disk_ll_max_entries;
+ ll->commit = disk_ll_commit;
+
+ ll->nr_blocks = 0;
+ ll->nr_allocated = 0;
+
+ r = ll->init_index(ll);
+ if (r < 0)
+ return r;
+
+ r = dm_btree_empty(&ll->ref_count_info, &ll->ref_count_root);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sm_ll_open_disk(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct disk_sm_root *smr = root_le;
+
+ if (len < sizeof(struct disk_sm_root)) {
+ DMERR("sm_metadata root too small");
+ return -ENOMEM;
+ }
+
+ r = sm_ll_init(ll, tm);
+ if (r < 0)
+ return r;
+
+ ll->load_ie = disk_ll_load_ie;
+ ll->save_ie = disk_ll_save_ie;
+ ll->init_index = disk_ll_init_index;
+ ll->open_index = disk_ll_open;
+ ll->max_entries = disk_ll_max_entries;
+ ll->commit = disk_ll_commit;
+
+ ll->nr_blocks = le64_to_cpu(smr->nr_blocks);
+ ll->nr_allocated = le64_to_cpu(smr->nr_allocated);
+ ll->bitmap_root = le64_to_cpu(smr->bitmap_root);
+ ll->ref_count_root = le64_to_cpu(smr->ref_count_root);
+
+ return ll->open_index(ll);
+}
+
+/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-space-map-common.h b/drivers/md/persistent-data/dm-space-map-common.h
new file mode 100644
index 000000000000..8f220821a9a9
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-common.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SPACE_MAP_COMMON_H
+#define DM_SPACE_MAP_COMMON_H
+
+#include "dm-btree.h"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Low level disk format
+ *
+ * Bitmap btree
+ * ------------
+ *
+ * Each value stored in the btree is an index_entry. This points to a
+ * block that is used as a bitmap. Within the bitmap hold 2 bits per
+ * entry, which represent UNUSED = 0, REF_COUNT = 1, REF_COUNT = 2 and
+ * REF_COUNT = many.
+ *
+ * Refcount btree
+ * --------------
+ *
+ * Any entry that has a ref count higher than 2 gets entered in the ref
+ * count tree. The leaf values for this tree is the 32-bit ref count.
+ */
+
+struct disk_index_entry {
+ __le64 blocknr;
+ __le32 nr_free;
+ __le32 none_free_before;
+} __packed;
+
+
+#define MAX_METADATA_BITMAPS 255
+struct disk_metadata_index {
+ __le32 csum;
+ __le32 padding;
+ __le64 blocknr;
+
+ struct disk_index_entry index[MAX_METADATA_BITMAPS];
+} __packed;
+
+struct ll_disk;
+
+typedef int (*load_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *result);
+typedef int (*save_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *ie);
+typedef int (*init_index_fn)(struct ll_disk *ll);
+typedef int (*open_index_fn)(struct ll_disk *ll);
+typedef dm_block_t (*max_index_entries_fn)(struct ll_disk *ll);
+typedef int (*commit_fn)(struct ll_disk *ll);
+
+struct ll_disk {
+ struct dm_transaction_manager *tm;
+ struct dm_btree_info bitmap_info;
+ struct dm_btree_info ref_count_info;
+
+ uint32_t block_size;
+ uint32_t entries_per_block;
+ dm_block_t nr_blocks;
+ dm_block_t nr_allocated;
+
+ /*
+ * bitmap_root may be a btree root or a simple index.
+ */
+ dm_block_t bitmap_root;
+
+ dm_block_t ref_count_root;
+
+ struct disk_metadata_index mi_le;
+ load_ie_fn load_ie;
+ save_ie_fn save_ie;
+ init_index_fn init_index;
+ open_index_fn open_index;
+ max_index_entries_fn max_entries;
+ commit_fn commit;
+};
+
+struct disk_sm_root {
+ __le64 nr_blocks;
+ __le64 nr_allocated;
+ __le64 bitmap_root;
+ __le64 ref_count_root;
+} __packed;
+
+#define ENTRIES_PER_BYTE 4
+
+struct disk_bitmap_header {
+ __le32 csum;
+ __le32 not_used;
+ __le64 blocknr;
+} __packed;
+
+enum allocation_event {
+ SM_NONE,
+ SM_ALLOC,
+ SM_FREE,
+};
+
+/*----------------------------------------------------------------*/
+
+int sm_ll_extend(struct ll_disk *ll, dm_block_t extra_blocks);
+int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result);
+int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result);
+int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
+ dm_block_t end, dm_block_t *result);
+int sm_ll_insert(struct ll_disk *ll, dm_block_t b, uint32_t ref_count, enum allocation_event *ev);
+int sm_ll_inc(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev);
+int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev);
+int sm_ll_commit(struct ll_disk *ll);
+
+int sm_ll_new_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm);
+int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm);
+int sm_ll_open_disk(struct ll_disk *ll, struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+/*----------------------------------------------------------------*/
+
+#endif /* DM_SPACE_MAP_COMMON_H */
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
new file mode 100644
index 000000000000..aeff7852cf79
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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"
+#include "dm-transaction-manager.h"
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map disk"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Space map interface.
+ */
+struct sm_disk {
+ struct dm_space_map sm;
+
+ struct ll_disk ll;
+ struct ll_disk old_ll;
+
+ dm_block_t begin;
+ dm_block_t nr_allocated_this_transaction;
+};
+
+static void sm_disk_destroy(struct dm_space_map *sm)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ kfree(smd);
+}
+
+static int sm_disk_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ return sm_ll_extend(&smd->ll, extra_blocks);
+}
+
+static int sm_disk_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ *count = smd->old_ll.nr_blocks;
+
+ return 0;
+}
+
+static int sm_disk_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ *count = (smd->old_ll.nr_blocks - smd->old_ll.nr_allocated) - smd->nr_allocated_this_transaction;
+
+ return 0;
+}
+
+static int sm_disk_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ return sm_ll_lookup(&smd->ll, b, result);
+}
+
+static int sm_disk_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b,
+ int *result)
+{
+ int r;
+ uint32_t count;
+
+ r = sm_disk_get_count(sm, b, &count);
+ if (r)
+ return r;
+
+ return count > 1;
+}
+
+static int sm_disk_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ int r;
+ uint32_t old_count;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_insert(&smd->ll, b, count, &ev);
+ if (!r) {
+ switch (ev) {
+ case SM_NONE:
+ break;
+
+ case SM_ALLOC:
+ /*
+ * This _must_ be free in the prior transaction
+ * otherwise we've lost atomicity.
+ */
+ smd->nr_allocated_this_transaction++;
+ break;
+
+ case SM_FREE:
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (r)
+ return r;
+
+ if (!old_count)
+ smd->nr_allocated_this_transaction--;
+ break;
+ }
+ }
+
+ return r;
+}
+
+static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_inc(&smd->ll, b, &ev);
+ if (!r && (ev == SM_ALLOC))
+ /*
+ * This _must_ be free in the prior transaction
+ * otherwise we've lost atomicity.
+ */
+ smd->nr_allocated_this_transaction++;
+
+ return r;
+}
+
+static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r;
+ uint32_t old_count;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_ll_dec(&smd->ll, b, &ev);
+ if (!r && (ev == SM_FREE)) {
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (r)
+ return r;
+
+ if (!old_count)
+ smd->nr_allocated_this_transaction--;
+ }
+
+ return r;
+}
+
+static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r;
+ enum allocation_event ev;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ /* FIXME: we should loop round a couple of times */
+ r = sm_ll_find_free_block(&smd->old_ll, smd->begin, smd->old_ll.nr_blocks, b);
+ if (r)
+ return r;
+
+ smd->begin = *b + 1;
+ r = sm_ll_inc(&smd->ll, *b, &ev);
+ if (!r) {
+ BUG_ON(ev != SM_ALLOC);
+ smd->nr_allocated_this_transaction++;
+ }
+
+ return r;
+}
+
+static int sm_disk_commit(struct dm_space_map *sm)
+{
+ int r;
+ dm_block_t nr_free;
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+
+ r = sm_disk_get_nr_free(sm, &nr_free);
+ if (r)
+ return r;
+
+ r = sm_ll_commit(&smd->ll);
+ if (r)
+ return r;
+
+ memcpy(&smd->old_ll, &smd->ll, sizeof(smd->old_ll));
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+
+ r = sm_disk_get_nr_free(sm, &nr_free);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int sm_disk_root_size(struct dm_space_map *sm, size_t *result)
+{
+ *result = sizeof(struct disk_sm_root);
+
+ return 0;
+}
+
+static int sm_disk_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
+{
+ struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
+ struct disk_sm_root root_le;
+
+ root_le.nr_blocks = cpu_to_le64(smd->ll.nr_blocks);
+ root_le.nr_allocated = cpu_to_le64(smd->ll.nr_allocated);
+ root_le.bitmap_root = cpu_to_le64(smd->ll.bitmap_root);
+ root_le.ref_count_root = cpu_to_le64(smd->ll.ref_count_root);
+
+ if (max < sizeof(root_le))
+ return -ENOSPC;
+
+ memcpy(where_le, &root_le, sizeof(root_le));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_space_map ops = {
+ .destroy = sm_disk_destroy,
+ .extend = sm_disk_extend,
+ .get_nr_blocks = sm_disk_get_nr_blocks,
+ .get_nr_free = sm_disk_get_nr_free,
+ .get_count = sm_disk_get_count,
+ .count_is_more_than_one = sm_disk_count_is_more_than_one,
+ .set_count = sm_disk_set_count,
+ .inc_block = sm_disk_inc_block,
+ .dec_block = sm_disk_dec_block,
+ .new_block = sm_disk_new_block,
+ .commit = sm_disk_commit,
+ .root_size = sm_disk_root_size,
+ .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)
+{
+ int r;
+ struct sm_disk *smd;
+
+ smd = kmalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+ memcpy(&smd->sm, &ops, sizeof(smd->sm));
+
+ r = sm_ll_new_disk(&smd->ll, tm);
+ if (r)
+ goto bad;
+
+ r = sm_ll_extend(&smd->ll, nr_blocks);
+ if (r)
+ goto bad;
+
+ r = sm_disk_commit(&smd->sm);
+ if (r)
+ goto bad;
+
+ return &smd->sm;
+
+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);
+ return dm_sm_checker_create_fresh(sm);
+}
+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)
+{
+ int r;
+ struct sm_disk *smd;
+
+ smd = kmalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ smd->begin = 0;
+ smd->nr_allocated_this_transaction = 0;
+ memcpy(&smd->sm, &ops, sizeof(smd->sm));
+
+ r = sm_ll_open_disk(&smd->ll, tm, root_le, len);
+ if (r)
+ goto bad;
+
+ r = sm_disk_commit(&smd->sm);
+ if (r)
+ goto bad;
+
+ return &smd->sm;
+
+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-space-map-disk.h b/drivers/md/persistent-data/dm-space-map-disk.h
new file mode 100644
index 000000000000..447a0a9a2d9f
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-disk.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_SPACE_MAP_DISK_H
+#define _LINUX_DM_SPACE_MAP_DISK_H
+
+#include "dm-block-manager.h"
+
+struct dm_space_map;
+struct dm_transaction_manager;
+
+/*
+ * Unfortunately we have to use two-phase construction due to the cycle
+ * between the tm and sm.
+ */
+struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks);
+
+struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
+ void *root, size_t len);
+
+#endif /* _LINUX_DM_SPACE_MAP_DISK_H */
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
new file mode 100644
index 000000000000..e89ae5e7a519
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-space-map.h"
+#include "dm-space-map-common.h"
+#include "dm-space-map-metadata.h"
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "space map metadata"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Space map interface.
+ *
+ * The low level disk format is written using the standard btree and
+ * transaction manager. This means that performing disk operations may
+ * cause us to recurse into the space map in order to allocate new blocks.
+ * For this reason we have a pool of pre-allocated blocks large enough to
+ * service any metadata_ll_disk operation.
+ */
+
+/*
+ * FIXME: we should calculate this based on the size of the device.
+ * Only the metadata space map needs this functionality.
+ */
+#define MAX_RECURSIVE_ALLOCATIONS 1024
+
+enum block_op_type {
+ BOP_INC,
+ BOP_DEC
+};
+
+struct block_op {
+ enum block_op_type type;
+ dm_block_t block;
+};
+
+struct sm_metadata {
+ struct dm_space_map sm;
+
+ struct ll_disk ll;
+ struct ll_disk old_ll;
+
+ dm_block_t begin;
+
+ unsigned recursion_count;
+ unsigned allocated_this_transaction;
+ unsigned nr_uncommitted;
+ struct block_op uncommitted[MAX_RECURSIVE_ALLOCATIONS];
+};
+
+static int add_bop(struct sm_metadata *smm, enum block_op_type type, dm_block_t b)
+{
+ struct block_op *op;
+
+ if (smm->nr_uncommitted == MAX_RECURSIVE_ALLOCATIONS) {
+ DMERR("too many recursive allocations");
+ return -ENOMEM;
+ }
+
+ op = smm->uncommitted + smm->nr_uncommitted++;
+ op->type = type;
+ op->block = b;
+
+ return 0;
+}
+
+static int commit_bop(struct sm_metadata *smm, struct block_op *op)
+{
+ int r = 0;
+ enum allocation_event ev;
+
+ switch (op->type) {
+ case BOP_INC:
+ r = sm_ll_inc(&smm->ll, op->block, &ev);
+ break;
+
+ case BOP_DEC:
+ r = sm_ll_dec(&smm->ll, op->block, &ev);
+ break;
+ }
+
+ return r;
+}
+
+static void in(struct sm_metadata *smm)
+{
+ smm->recursion_count++;
+}
+
+static int out(struct sm_metadata *smm)
+{
+ int r = 0;
+
+ /*
+ * If we're not recursing then very bad things are happening.
+ */
+ if (!smm->recursion_count) {
+ DMERR("lost track of recursion depth");
+ return -ENOMEM;
+ }
+
+ if (smm->recursion_count == 1 && smm->nr_uncommitted) {
+ while (smm->nr_uncommitted && !r) {
+ smm->nr_uncommitted--;
+ r = commit_bop(smm, smm->uncommitted +
+ smm->nr_uncommitted);
+ if (r)
+ break;
+ }
+ }
+
+ smm->recursion_count--;
+
+ return r;
+}
+
+/*
+ * When using the out() function above, we often want to combine an error
+ * code for the operation run in the recursive context with that from
+ * out().
+ */
+static int combine_errors(int r1, int r2)
+{
+ return r1 ? r1 : r2;
+}
+
+static int recursing(struct sm_metadata *smm)
+{
+ return smm->recursion_count;
+}
+
+static void sm_metadata_destroy(struct dm_space_map *sm)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ kfree(smm);
+}
+
+static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ DMERR("doesn't support extend");
+ return -EINVAL;
+}
+
+static int sm_metadata_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->ll.nr_blocks;
+
+ return 0;
+}
+
+static int sm_metadata_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->old_ll.nr_blocks - smm->old_ll.nr_allocated -
+ smm->allocated_this_transaction;
+
+ return 0;
+}
+
+static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ int r, i;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ unsigned adjustment = 0;
+
+ /*
+ * We may have some uncommitted adjustments to add. This list
+ * should always be really short.
+ */
+ for (i = 0; i < smm->nr_uncommitted; i++) {
+ struct block_op *op = smm->uncommitted + i;
+
+ if (op->block != b)
+ continue;
+
+ switch (op->type) {
+ case BOP_INC:
+ adjustment++;
+ break;
+
+ case BOP_DEC:
+ adjustment--;
+ break;
+ }
+ }
+
+ r = sm_ll_lookup(&smm->ll, b, result);
+ if (r)
+ return r;
+
+ *result += adjustment;
+
+ return 0;
+}
+
+static int sm_metadata_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ int r, i, adjustment = 0;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ uint32_t rc;
+
+ /*
+ * We may have some uncommitted adjustments to add. This list
+ * should always be really short.
+ */
+ for (i = 0; i < smm->nr_uncommitted; i++) {
+ struct block_op *op = smm->uncommitted + i;
+
+ if (op->block != b)
+ continue;
+
+ switch (op->type) {
+ case BOP_INC:
+ adjustment++;
+ break;
+
+ case BOP_DEC:
+ adjustment--;
+ break;
+ }
+ }
+
+ if (adjustment > 1) {
+ *result = 1;
+ return 0;
+ }
+
+ r = sm_ll_lookup_bitmap(&smm->ll, b, &rc);
+ if (r)
+ return r;
+
+ if (rc == 3)
+ /*
+ * We err on the side of caution, and always return true.
+ */
+ *result = 1;
+ else
+ *result = rc + adjustment > 1;
+
+ return 0;
+}
+
+static int sm_metadata_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ int r, r2;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (smm->recursion_count) {
+ DMERR("cannot recurse set_count()");
+ return -EINVAL;
+ }
+
+ in(smm);
+ r = sm_ll_insert(&smm->ll, b, count, &ev);
+ r2 = out(smm);
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_INC, b);
+ else {
+ in(smm);
+ r = sm_ll_inc(&smm->ll, b, &ev);
+ r2 = out(smm);
+ }
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_DEC, b);
+ else {
+ in(smm);
+ r = sm_ll_dec(&smm->ll, b, &ev);
+ r2 = out(smm);
+ }
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_new_block_(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r, r2 = 0;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_find_free_block(&smm->old_ll, smm->begin, smm->old_ll.nr_blocks, b);
+ if (r)
+ return r;
+
+ smm->begin = *b + 1;
+
+ if (recursing(smm))
+ r = add_bop(smm, BOP_INC, *b);
+ else {
+ in(smm);
+ r = sm_ll_inc(&smm->ll, *b, &ev);
+ r2 = out(smm);
+ }
+
+ if (!r)
+ smm->allocated_this_transaction++;
+
+ return combine_errors(r, r2);
+}
+
+static int sm_metadata_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ int r = sm_metadata_new_block_(sm, b);
+ if (r)
+ DMERR("out of metadata space");
+ return r;
+}
+
+static int sm_metadata_commit(struct dm_space_map *sm)
+{
+ int r;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_commit(&smm->ll);
+ if (r)
+ return r;
+
+ memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
+ smm->begin = 0;
+ smm->allocated_this_transaction = 0;
+
+ return 0;
+}
+
+static int sm_metadata_root_size(struct dm_space_map *sm, size_t *result)
+{
+ *result = sizeof(struct disk_sm_root);
+
+ return 0;
+}
+
+static int sm_metadata_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ struct disk_sm_root root_le;
+
+ root_le.nr_blocks = cpu_to_le64(smm->ll.nr_blocks);
+ root_le.nr_allocated = cpu_to_le64(smm->ll.nr_allocated);
+ root_le.bitmap_root = cpu_to_le64(smm->ll.bitmap_root);
+ root_le.ref_count_root = cpu_to_le64(smm->ll.ref_count_root);
+
+ if (max < sizeof(root_le))
+ return -ENOSPC;
+
+ memcpy(where_le, &root_le, sizeof(root_le));
+
+ return 0;
+}
+
+static struct dm_space_map ops = {
+ .destroy = sm_metadata_destroy,
+ .extend = sm_metadata_extend,
+ .get_nr_blocks = sm_metadata_get_nr_blocks,
+ .get_nr_free = sm_metadata_get_nr_free,
+ .get_count = sm_metadata_get_count,
+ .count_is_more_than_one = sm_metadata_count_is_more_than_one,
+ .set_count = sm_metadata_set_count,
+ .inc_block = sm_metadata_inc_block,
+ .dec_block = sm_metadata_dec_block,
+ .new_block = sm_metadata_new_block,
+ .commit = sm_metadata_commit,
+ .root_size = sm_metadata_root_size,
+ .copy_root = sm_metadata_copy_root
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * When a new space map is created that manages its own space. We use
+ * this tiny bootstrap allocator.
+ */
+static void sm_bootstrap_destroy(struct dm_space_map *sm)
+{
+}
+
+static int sm_bootstrap_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ DMERR("boostrap doesn't support extend");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return smm->ll.nr_blocks;
+}
+
+static int sm_bootstrap_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ *count = smm->ll.nr_blocks - smm->begin;
+
+ return 0;
+}
+
+static int sm_bootstrap_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return b < smm->begin ? 1 : 0;
+}
+
+static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ *result = 0;
+
+ return 0;
+}
+
+static int sm_bootstrap_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ DMERR("boostrap doesn't support set_count");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ /*
+ * We know the entire device is unused.
+ */
+ if (smm->begin == smm->ll.nr_blocks)
+ return -ENOSPC;
+
+ *b = smm->begin++;
+
+ return 0;
+}
+
+static int sm_bootstrap_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return add_bop(smm, BOP_INC, b);
+}
+
+static int sm_bootstrap_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ return add_bop(smm, BOP_DEC, b);
+}
+
+static int sm_bootstrap_commit(struct dm_space_map *sm)
+{
+ return 0;
+}
+
+static int sm_bootstrap_root_size(struct dm_space_map *sm, size_t *result)
+{
+ DMERR("boostrap doesn't support root_size");
+
+ return -EINVAL;
+}
+
+static int sm_bootstrap_copy_root(struct dm_space_map *sm, void *where,
+ size_t max)
+{
+ DMERR("boostrap doesn't support copy_root");
+
+ return -EINVAL;
+}
+
+static struct dm_space_map bootstrap_ops = {
+ .destroy = sm_bootstrap_destroy,
+ .extend = sm_bootstrap_extend,
+ .get_nr_blocks = sm_bootstrap_get_nr_blocks,
+ .get_nr_free = sm_bootstrap_get_nr_free,
+ .get_count = sm_bootstrap_get_count,
+ .count_is_more_than_one = sm_bootstrap_count_is_more_than_one,
+ .set_count = sm_bootstrap_set_count,
+ .inc_block = sm_bootstrap_inc_block,
+ .dec_block = sm_bootstrap_dec_block,
+ .new_block = sm_bootstrap_new_block,
+ .commit = sm_bootstrap_commit,
+ .root_size = sm_bootstrap_root_size,
+ .copy_root = sm_bootstrap_copy_root
+};
+
+/*----------------------------------------------------------------*/
+
+struct dm_space_map *dm_sm_metadata_init(void)
+{
+ struct sm_metadata *smm;
+
+ smm = kmalloc(sizeof(*smm), GFP_KERNEL);
+ if (!smm)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
+
+ return &smm->sm;
+}
+
+int dm_sm_metadata_create(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks,
+ dm_block_t superblock)
+{
+ int r;
+ dm_block_t i;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ smm->begin = superblock + 1;
+ smm->recursion_count = 0;
+ smm->allocated_this_transaction = 0;
+ smm->nr_uncommitted = 0;
+
+ memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
+
+ r = sm_ll_new_metadata(&smm->ll, tm);
+ if (r)
+ return r;
+
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ if (r)
+ return r;
+
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
+
+ /*
+ * Now we need to update the newly created data structures with the
+ * allocated blocks that they were built from.
+ */
+ for (i = superblock; !r && i < smm->begin; i++)
+ r = sm_ll_inc(&smm->ll, i, &ev);
+
+ if (r)
+ return r;
+
+ return sm_metadata_commit(sm);
+}
+
+int dm_sm_metadata_open(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
+{
+ int r;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ r = sm_ll_open_metadata(&smm->ll, tm, root_le, len);
+ if (r)
+ return r;
+
+ smm->begin = 0;
+ smm->recursion_count = 0;
+ smm->allocated_this_transaction = 0;
+ smm->nr_uncommitted = 0;
+
+ memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
+ return 0;
+}
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.h b/drivers/md/persistent-data/dm-space-map-metadata.h
new file mode 100644
index 000000000000..39bba0801cf2
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map-metadata.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SPACE_MAP_METADATA_H
+#define DM_SPACE_MAP_METADATA_H
+
+#include "dm-transaction-manager.h"
+
+/*
+ * Unfortunately we have to use two-phase construction due to the cycle
+ * between the tm and sm.
+ */
+struct dm_space_map *dm_sm_metadata_init(void);
+
+/*
+ * Create a fresh space map.
+ */
+int dm_sm_metadata_create(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks,
+ dm_block_t superblock);
+
+/*
+ * Open from a previously-recorded root.
+ */
+int dm_sm_metadata_open(struct dm_space_map *sm,
+ struct dm_transaction_manager *tm,
+ void *root_le, size_t len);
+
+#endif /* DM_SPACE_MAP_METADATA_H */
diff --git a/drivers/md/persistent-data/dm-space-map.h b/drivers/md/persistent-data/dm-space-map.h
new file mode 100644
index 000000000000..1cbfc6b1638a
--- /dev/null
+++ b/drivers/md/persistent-data/dm-space-map.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_SPACE_MAP_H
+#define _LINUX_DM_SPACE_MAP_H
+
+#include "dm-block-manager.h"
+
+/*
+ * struct dm_space_map keeps a record of how many times each block in a device
+ * is referenced. It needs to be fixed on disk as part of the transaction.
+ */
+struct dm_space_map {
+ void (*destroy)(struct dm_space_map *sm);
+
+ /*
+ * You must commit before allocating the newly added space.
+ */
+ int (*extend)(struct dm_space_map *sm, dm_block_t extra_blocks);
+
+ /*
+ * Extensions do not appear in this count until after commit has
+ * been called.
+ */
+ int (*get_nr_blocks)(struct dm_space_map *sm, dm_block_t *count);
+
+ /*
+ * Space maps must never allocate a block from the previous
+ * transaction, in case we need to rollback. This complicates the
+ * semantics of get_nr_free(), it should return the number of blocks
+ * that are available for allocation _now_. For instance you may
+ * have blocks with a zero reference count that will not be
+ * available for allocation until after the next commit.
+ */
+ int (*get_nr_free)(struct dm_space_map *sm, dm_block_t *count);
+
+ int (*get_count)(struct dm_space_map *sm, dm_block_t b, uint32_t *result);
+ int (*count_is_more_than_one)(struct dm_space_map *sm, dm_block_t b,
+ int *result);
+ int (*set_count)(struct dm_space_map *sm, dm_block_t b, uint32_t count);
+
+ int (*commit)(struct dm_space_map *sm);
+
+ int (*inc_block)(struct dm_space_map *sm, dm_block_t b);
+ int (*dec_block)(struct dm_space_map *sm, dm_block_t b);
+
+ /*
+ * new_block will increment the returned block.
+ */
+ int (*new_block)(struct dm_space_map *sm, dm_block_t *b);
+
+ /*
+ * The root contains all the information needed to fix the space map.
+ * Generally this info is small, so squirrel it away in a disk block
+ * along with other info.
+ */
+ int (*root_size)(struct dm_space_map *sm, size_t *result);
+ int (*copy_root)(struct dm_space_map *sm, void *copy_to_here_le, size_t len);
+};
+
+/*----------------------------------------------------------------*/
+
+static inline void dm_sm_destroy(struct dm_space_map *sm)
+{
+ sm->destroy(sm);
+}
+
+static inline int dm_sm_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ return sm->extend(sm, extra_blocks);
+}
+
+static inline int dm_sm_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
+{
+ return sm->get_nr_blocks(sm, count);
+}
+
+static inline int dm_sm_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
+{
+ return sm->get_nr_free(sm, count);
+}
+
+static inline int dm_sm_get_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t *result)
+{
+ return sm->get_count(sm, b, result);
+}
+
+static inline int dm_sm_count_is_more_than_one(struct dm_space_map *sm,
+ dm_block_t b, int *result)
+{
+ return sm->count_is_more_than_one(sm, b, result);
+}
+
+static inline int dm_sm_set_count(struct dm_space_map *sm, dm_block_t b,
+ uint32_t count)
+{
+ return sm->set_count(sm, b, count);
+}
+
+static inline int dm_sm_commit(struct dm_space_map *sm)
+{
+ return sm->commit(sm);
+}
+
+static inline int dm_sm_inc_block(struct dm_space_map *sm, dm_block_t b)
+{
+ return sm->inc_block(sm, b);
+}
+
+static inline int dm_sm_dec_block(struct dm_space_map *sm, dm_block_t b)
+{
+ return sm->dec_block(sm, b);
+}
+
+static inline int dm_sm_new_block(struct dm_space_map *sm, dm_block_t *b)
+{
+ return sm->new_block(sm, b);
+}
+
+static inline int dm_sm_root_size(struct dm_space_map *sm, size_t *result)
+{
+ return sm->root_size(sm, result);
+}
+
+static inline int dm_sm_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
+{
+ return sm->copy_root(sm, copy_to_here_le, len);
+}
+
+#endif /* _LINUX_DM_SPACE_MAP_H */
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
new file mode 100644
index 000000000000..728e89a3f978
--- /dev/null
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+#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"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "transaction manager"
+
+/*----------------------------------------------------------------*/
+
+struct shadow_info {
+ struct hlist_node hlist;
+ dm_block_t where;
+};
+
+/*
+ * It would be nice if we scaled with the size of transaction.
+ */
+#define HASH_SIZE 256
+#define HASH_MASK (HASH_SIZE - 1)
+
+struct dm_transaction_manager {
+ int is_clone;
+ struct dm_transaction_manager *real;
+
+ struct dm_block_manager *bm;
+ struct dm_space_map *sm;
+
+ spinlock_t lock;
+ struct hlist_head buckets[HASH_SIZE];
+};
+
+/*----------------------------------------------------------------*/
+
+static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ int r = 0;
+ unsigned bucket = dm_hash_block(b, HASH_MASK);
+ struct shadow_info *si;
+ struct hlist_node *n;
+
+ spin_lock(&tm->lock);
+ hlist_for_each_entry(si, n, tm->buckets + bucket, hlist)
+ if (si->where == b) {
+ r = 1;
+ break;
+ }
+ spin_unlock(&tm->lock);
+
+ return r;
+}
+
+/*
+ * This can silently fail if there's no memory. We're ok with this since
+ * creating redundant shadows causes no harm.
+ */
+static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ unsigned bucket;
+ struct shadow_info *si;
+
+ si = kmalloc(sizeof(*si), GFP_NOIO);
+ if (si) {
+ si->where = b;
+ bucket = dm_hash_block(b, HASH_MASK);
+ spin_lock(&tm->lock);
+ hlist_add_head(&si->hlist, tm->buckets + bucket);
+ spin_unlock(&tm->lock);
+ }
+}
+
+static void wipe_shadow_table(struct dm_transaction_manager *tm)
+{
+ struct shadow_info *si;
+ struct hlist_node *n, *tmp;
+ struct hlist_head *bucket;
+ int i;
+
+ spin_lock(&tm->lock);
+ for (i = 0; i < HASH_SIZE; i++) {
+ bucket = tm->buckets + i;
+ hlist_for_each_entry_safe(si, n, tmp, bucket, hlist)
+ kfree(si);
+
+ INIT_HLIST_HEAD(bucket);
+ }
+
+ spin_unlock(&tm->lock);
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
+ struct dm_space_map *sm)
+{
+ int i;
+ struct dm_transaction_manager *tm;
+
+ tm = kmalloc(sizeof(*tm), GFP_KERNEL);
+ if (!tm)
+ return ERR_PTR(-ENOMEM);
+
+ tm->is_clone = 0;
+ tm->real = NULL;
+ tm->bm = bm;
+ tm->sm = sm;
+
+ spin_lock_init(&tm->lock);
+ for (i = 0; i < HASH_SIZE; i++)
+ INIT_HLIST_HEAD(tm->buckets + i);
+
+ return tm;
+}
+
+struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real)
+{
+ struct dm_transaction_manager *tm;
+
+ tm = kmalloc(sizeof(*tm), GFP_KERNEL);
+ if (tm) {
+ tm->is_clone = 1;
+ tm->real = real;
+ }
+
+ return tm;
+}
+EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone);
+
+void dm_tm_destroy(struct dm_transaction_manager *tm)
+{
+ kfree(tm);
+}
+EXPORT_SYMBOL_GPL(dm_tm_destroy);
+
+int dm_tm_pre_commit(struct dm_transaction_manager *tm)
+{
+ int r;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_commit(tm->sm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
+
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
+{
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ wipe_shadow_table(tm);
+
+ return dm_bm_flush_and_unlock(tm->bm, root);
+}
+EXPORT_SYMBOL_GPL(dm_tm_commit);
+
+int dm_tm_new_block(struct dm_transaction_manager *tm,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ dm_block_t new_block;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_new_block(tm->sm, &new_block);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_write_lock_zero(tm->bm, new_block, v, result);
+ if (r < 0) {
+ dm_sm_dec_block(tm->sm, new_block);
+ return r;
+ }
+
+ /*
+ * New blocks count as shadows in that they don't need to be
+ * shadowed again.
+ */
+ insert_shadow(tm, new_block);
+
+ return 0;
+}
+
+static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v,
+ struct dm_block **result)
+{
+ int r;
+ dm_block_t new;
+ struct dm_block *orig_block;
+
+ r = dm_sm_new_block(tm->sm, &new);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_dec_block(tm->sm, orig);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_read_lock(tm->bm, orig, v, &orig_block);
+ if (r < 0)
+ return r;
+
+ r = dm_bm_unlock_move(orig_block, new);
+ if (r < 0) {
+ dm_bm_unlock(orig_block);
+ return r;
+ }
+
+ return dm_bm_write_lock(tm->bm, new, v, result);
+}
+
+int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v, struct dm_block **result,
+ int *inc_children)
+{
+ int r;
+
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ r = dm_sm_count_is_more_than_one(tm->sm, orig, inc_children);
+ if (r < 0)
+ return r;
+
+ if (is_shadow(tm, orig) && !*inc_children)
+ return dm_bm_write_lock(tm->bm, orig, v, result);
+
+ r = __shadow_block(tm, orig, v, result);
+ if (r < 0)
+ return r;
+ insert_shadow(tm, dm_block_location(*result));
+
+ return r;
+}
+
+int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **blk)
+{
+ if (tm->is_clone)
+ return dm_bm_read_try_lock(tm->real->bm, b, v, blk);
+
+ return dm_bm_read_lock(tm->bm, b, v, blk);
+}
+
+int dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b)
+{
+ return dm_bm_unlock(b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_unlock);
+
+void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ /*
+ * The non-blocking clone doesn't support this.
+ */
+ BUG_ON(tm->is_clone);
+
+ dm_sm_inc_block(tm->sm, b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_inc);
+
+void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b)
+{
+ /*
+ * The non-blocking clone doesn't support this.
+ */
+ BUG_ON(tm->is_clone);
+
+ dm_sm_dec_block(tm->sm, b);
+}
+EXPORT_SYMBOL_GPL(dm_tm_dec);
+
+int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
+ uint32_t *result)
+{
+ if (tm->is_clone)
+ return -EWOULDBLOCK;
+
+ return dm_sm_get_count(tm->sm, b, result);
+}
+
+struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
+{
+ return tm->bm;
+}
+
+/*----------------------------------------------------------------*/
+
+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 r;
+ struct dm_space_map *inner;
+
+ inner = dm_sm_metadata_init();
+ if (IS_ERR(inner))
+ return PTR_ERR(inner);
+
+ *tm = dm_tm_create(bm, inner);
+ if (IS_ERR(*tm)) {
+ dm_sm_destroy(inner);
+ 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),
+ sb_location);
+ if (r) {
+ DMERR("couldn't create metadata space map");
+ goto bad2;
+ }
+
+ *sm = dm_sm_checker_create(inner);
+ if (!*sm)
+ goto bad2;
+
+ } 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);
+ if (r) {
+ DMERR("couldn't open metadata space map");
+ goto bad2;
+ }
+
+ *sm = dm_sm_checker_create(inner);
+ if (!*sm)
+ goto bad2;
+ }
+
+ return 0;
+
+bad2:
+ dm_tm_unlock(*tm, *sblock);
+bad1:
+ dm_tm_destroy(*tm);
+ dm_sm_destroy(inner);
+ 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)
+{
+ return dm_tm_create_internal(bm, sb_location, sb_validator,
+ 0, 0, tm, sm, sblock, 1);
+}
+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,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock)
+{
+ return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset,
+ root_max_len, tm, sm, sblock, 0);
+}
+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
new file mode 100644
index 000000000000..6da784871db4
--- /dev/null
+++ b/drivers/md/persistent-data/dm-transaction-manager.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_TRANSACTION_MANAGER_H
+#define _LINUX_DM_TRANSACTION_MANAGER_H
+
+#include "dm-block-manager.h"
+
+struct dm_transaction_manager;
+struct dm_space_map;
+
+/*----------------------------------------------------------------*/
+
+/*
+ * This manages the scope of a transaction. It also enforces immutability
+ * of the on-disk data structures by limiting access to writeable blocks.
+ *
+ * Clients should not fiddle with the block manager directly.
+ */
+
+void dm_tm_destroy(struct dm_transaction_manager *tm);
+
+/*
+ * The non-blocking version of a transaction manager is intended for use in
+ * fast path code that needs to do lookups e.g. a dm mapping function.
+ * You create the non-blocking variant from a normal tm. The interface is
+ * the same, except that most functions will just return -EWOULDBLOCK.
+ * Methods that return void yet may block should not be called on a clone
+ * viz. dm_tm_inc, dm_tm_dec. Call dm_tm_destroy() as you would with a normal
+ * tm when you've finished with it. You may not destroy the original prior
+ * to clones.
+ */
+struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real);
+
+/*
+ * We use a 2-phase commit here.
+ *
+ * i) In the first phase the block manager is told to start flushing, and
+ * the changes to the space map are written to disk. You should interrogate
+ * your particular space map to get detail of its root node etc. to be
+ * included in your superblock.
+ *
+ * ii) @root will be committed last. You shouldn't use more than the
+ * first 512 bytes of @root if you wish the transaction to survive a power
+ * failure. You *must* have a write lock held on @root for both stage (i)
+ * and (ii). The commit will drop the write lock.
+ */
+int dm_tm_pre_commit(struct dm_transaction_manager *tm);
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root);
+
+/*
+ * These methods are the only way to get hold of a writeable block.
+ */
+
+/*
+ * dm_tm_new_block() is pretty self-explanatory. Make sure you do actually
+ * write to the whole of @data before you unlock, otherwise you could get
+ * a data leak. (The other option is for tm_new_block() to zero new blocks
+ * before handing them out, which will be redundant in most, if not all,
+ * cases).
+ * Zeroes the new block and returns with write lock held.
+ */
+int dm_tm_new_block(struct dm_transaction_manager *tm,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+/*
+ * dm_tm_shadow_block() allocates a new block and copies the data from @orig
+ * to it. It then decrements the reference count on original block. Use
+ * this to update the contents of a block in a data structure, don't
+ * confuse this with a clone - you shouldn't access the orig block after
+ * this operation. Because the tm knows the scope of the transaction it
+ * can optimise requests for a shadow of a shadow to a no-op. Don't forget
+ * to unlock when you've finished with the shadow.
+ *
+ * The @inc_children flag is used to tell the caller whether it needs to
+ * adjust reference counts for children. (Data in the block may refer to
+ * other blocks.)
+ *
+ * Shadowing implicitly drops a reference on @orig so you must not have
+ * it locked when you call this.
+ */
+int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
+ struct dm_block_validator *v,
+ struct dm_block **result, int *inc_children);
+
+/*
+ * Read access. You can lock any block you want. If there's a write lock
+ * on it outstanding then it'll block.
+ */
+int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
+ struct dm_block_validator *v,
+ struct dm_block **result);
+
+int dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b);
+
+/*
+ * Functions for altering the reference count of a block directly.
+ */
+void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b);
+
+void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b);
+
+int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
+ uint32_t *result);
+
+struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
+
+/*
+ * A little utility that ties the knot by producing a transaction manager
+ * that has a space map managed by the transaction manager...
+ *
+ * Returns a tm that has an open transaction to write the new disk sm.
+ * Caller should store the new sm root and commit.
+ */
+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);
+
+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,
+ struct dm_transaction_manager **tm,
+ struct dm_space_map **sm, struct dm_block **sblock);
+
+#endif /* _LINUX_DM_TRANSACTION_MANAGER_H */
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 0eb08a4df759..27e19e2b51d4 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -468,7 +468,7 @@ static inline int is_io_in_chunk_boundary(struct mddev *mddev,
}
}
-static int raid0_make_request(struct mddev *mddev, struct bio *bio)
+static void raid0_make_request(struct mddev *mddev, struct bio *bio)
{
unsigned int chunk_sects;
sector_t sector_offset;
@@ -477,7 +477,7 @@ static int raid0_make_request(struct mddev *mddev, struct bio *bio)
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
chunk_sects = mddev->chunk_sectors;
@@ -497,13 +497,10 @@ static int raid0_make_request(struct mddev *mddev, struct bio *bio)
else
bp = bio_split(bio, chunk_sects -
sector_div(sector, chunk_sects));
- if (raid0_make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (raid0_make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
-
+ raid0_make_request(mddev, &bp->bio1);
+ raid0_make_request(mddev, &bp->bio2);
bio_pair_release(bp);
- return 0;
+ return;
}
sector_offset = bio->bi_sector;
@@ -513,10 +510,9 @@ static int raid0_make_request(struct mddev *mddev, struct bio *bio)
bio->bi_bdev = tmp_dev->bdev;
bio->bi_sector = sector_offset + zone->dev_start +
tmp_dev->data_offset;
- /*
- * Let the main block layer submit the IO and resolve recursion:
- */
- return 1;
+
+ generic_make_request(bio);
+ return;
bad_map:
printk("md/raid0:%s: make_request bug: can't convert block across chunks"
@@ -525,7 +521,7 @@ bad_map:
(unsigned long long)bio->bi_sector, bio->bi_size >> 10);
bio_io_error(bio);
- return 0;
+ return;
}
static void raid0_status(struct seq_file *seq, struct mddev *mddev)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 4602fc57c961..cae874646d9e 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -807,7 +807,7 @@ do_sync_io:
pr_debug("%dB behind alloc failed, doing sync I/O\n", bio->bi_size);
}
-static int make_request(struct mddev *mddev, struct bio * bio)
+static void make_request(struct mddev *mddev, struct bio * bio)
{
struct r1conf *conf = mddev->private;
struct mirror_info *mirror;
@@ -892,7 +892,7 @@ read_again:
if (rdisk < 0) {
/* couldn't find anywhere to read from */
raid_end_bio_io(r1_bio);
- return 0;
+ return;
}
mirror = conf->mirrors + rdisk;
@@ -950,7 +950,7 @@ read_again:
goto read_again;
} else
generic_make_request(read_bio);
- return 0;
+ return;
}
/*
@@ -1151,8 +1151,6 @@ read_again:
if (do_sync || !bitmap || !plugged)
md_wakeup_thread(mddev->thread);
-
- return 0;
}
static void status(struct seq_file *seq, struct mddev *mddev)
@@ -2193,7 +2191,6 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
bio->bi_next = NULL;
bio->bi_flags &= ~(BIO_POOL_MASK-1);
bio->bi_flags |= 1 << BIO_UPTODATE;
- bio->bi_comp_cpu = -1;
bio->bi_rw = READ;
bio->bi_vcnt = 0;
bio->bi_idx = 0;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 132c18ef8665..dde6dd4b47ec 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -842,7 +842,7 @@ static void unfreeze_array(struct r10conf *conf)
spin_unlock_irq(&conf->resync_lock);
}
-static int make_request(struct mddev *mddev, struct bio * bio)
+static void make_request(struct mddev *mddev, struct bio * bio)
{
struct r10conf *conf = mddev->private;
struct mirror_info *mirror;
@@ -861,7 +861,7 @@ static int make_request(struct mddev *mddev, struct bio * bio)
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
- return 0;
+ return;
}
/* If this request crosses a chunk boundary, we need to
@@ -893,10 +893,8 @@ static int make_request(struct mddev *mddev, struct bio * bio)
conf->nr_waiting++;
spin_unlock_irq(&conf->resync_lock);
- if (make_request(mddev, &bp->bio1))
- generic_make_request(&bp->bio1);
- if (make_request(mddev, &bp->bio2))
- generic_make_request(&bp->bio2);
+ make_request(mddev, &bp->bio1);
+ make_request(mddev, &bp->bio2);
spin_lock_irq(&conf->resync_lock);
conf->nr_waiting--;
@@ -904,14 +902,14 @@ static int make_request(struct mddev *mddev, struct bio * bio)
spin_unlock_irq(&conf->resync_lock);
bio_pair_release(bp);
- return 0;
+ return;
bad_map:
printk("md/raid10:%s: make_request bug: can't convert block across chunks"
" or bigger than %dk %llu %d\n", mdname(mddev), chunk_sects/2,
(unsigned long long)bio->bi_sector, bio->bi_size >> 10);
bio_io_error(bio);
- return 0;
+ return;
}
md_write_start(mddev, bio);
@@ -954,7 +952,7 @@ read_again:
slot = r10_bio->read_slot;
if (disk < 0) {
raid_end_bio_io(r10_bio);
- return 0;
+ return;
}
mirror = conf->mirrors + disk;
@@ -1002,7 +1000,7 @@ read_again:
goto read_again;
} else
generic_make_request(read_bio);
- return 0;
+ return;
}
/*
@@ -1176,7 +1174,6 @@ retry_write:
if (do_sync || !mddev->bitmap || !plugged)
md_wakeup_thread(mddev->thread);
- return 0;
}
static void status(struct seq_file *seq, struct mddev *mddev)
@@ -1355,7 +1352,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
struct mirror_info *p = &conf->mirrors[mirror];
if (p->recovery_disabled == mddev->recovery_disabled)
continue;
- if (!p->rdev)
+ if (p->rdev)
continue;
disk_stack_limits(mddev->gendisk, rdev->bdev,
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index f6fe053a5bed..bb1b46143fb6 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3688,7 +3688,7 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf)
return sh;
}
-static int make_request(struct mddev *mddev, struct bio * bi)
+static void make_request(struct mddev *mddev, struct bio * bi)
{
struct r5conf *conf = mddev->private;
int dd_idx;
@@ -3701,7 +3701,7 @@ static int make_request(struct mddev *mddev, struct bio * bi)
if (unlikely(bi->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bi);
- return 0;
+ return;
}
md_write_start(mddev, bi);
@@ -3709,7 +3709,7 @@ static int make_request(struct mddev *mddev, struct bio * bi)
if (rw == READ &&
mddev->reshape_position == MaxSector &&
chunk_aligned_read(mddev,bi))
- return 0;
+ return;
logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
last_sector = bi->bi_sector + (bi->bi_size>>9);
@@ -3844,8 +3844,6 @@ static int make_request(struct mddev *mddev, struct bio * bi)
bio_endio(bi, 0);
}
-
- return 0;
}
static sector_t raid5_size(struct mddev *mddev, sector_t sectors, int raid_disks);
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
index 9af2140b57a4..f5d53a202344 100644
--- a/drivers/media/common/saa7146_core.c
+++ b/drivers/media/common/saa7146_core.c
@@ -18,6 +18,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146.h>
LIST_HEAD(saa7146_devices);
@@ -35,10 +37,9 @@ static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
- INFO((" @ %li jiffies:\n",jiffies));
- for(i = 0; i <= 0x148; i+=4) {
- printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
- }
+ pr_info(" @ %li jiffies:\n", jiffies);
+ for (i = 0; i <= 0x148; i += 4)
+ pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
}
#endif
@@ -72,9 +73,8 @@ static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
if (saa7146_read(dev, MC2) & 2)
break;
if (err) {
- printk(KERN_ERR "%s: %s timed out while waiting for "
- "registers getting programmed\n",
- dev->name, __func__);
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
@@ -88,8 +88,8 @@ static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
break;
saa7146_read(dev, MC2);
if (err) {
- DEB_S(("%s: %s timed out while waiting for transfer "
- "completion\n", dev->name, __func__));
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
@@ -109,9 +109,8 @@ static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
if (saa7146_read(dev, MC2) & 2)
break;
if (!loops--) {
- printk(KERN_ERR "%s: %s timed out while waiting for "
- "registers getting programmed\n",
- dev->name, __func__);
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
udelay(1);
@@ -124,8 +123,8 @@ static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
break;
saa7146_read(dev, MC2);
if (!loops--) {
- DEB_S(("%s: %s timed out while waiting for transfer "
- "completion\n", dev->name, __func__));
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
return -ETIMEDOUT;
}
udelay(5);
@@ -264,7 +263,9 @@ int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt
ptr = pt->cpu;
for (i = 0; i < sglen; i++, list++) {
/*
- printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
+ pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
+ i, sg_dma_address(list), sg_dma_len(list),
+ list->offset);
*/
for (p = 0; p * 4096 < list->length; p++, ptr++) {
*ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
@@ -281,9 +282,9 @@ int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt
/*
ptr = pt->cpu;
- printk("offset: %d\n",pt->offset);
+ pr_debug("offset: %d\n", pt->offset);
for(i=0;i<5;i++) {
- printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
+ pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
}
*/
return 0;
@@ -314,7 +315,7 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id)
}
}
if (0 != (isr & (MASK_27))) {
- DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
if (dev->vv_data && dev->vv_callback)
dev->vv_callback(dev,isr);
isr &= ~MASK_27;
@@ -333,14 +334,15 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id)
} else {
u32 psr = saa7146_read(dev, PSR);
u32 ssr = saa7146_read(dev, SSR);
- printk(KERN_WARNING "%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
- dev->name, isr, psr, ssr);
+ pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
+ dev->name, isr, psr, ssr);
}
isr &= ~(MASK_16|MASK_17);
}
if( 0 != isr ) {
- ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
- ERR(("disabling interrupt source(s)!\n"));
+ ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
+ isr);
+ ERR("disabling interrupt source(s)!\n");
SAA7146_IER_DISABLE(dev,isr);
}
saa7146_write(dev, ISR, ack_isr);
@@ -360,15 +362,15 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
/* clear out mem for sure */
dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
if (!dev) {
- ERR(("out of memory.\n"));
+ ERR("out of memory\n");
goto out;
}
- DEB_EE(("pci:%p\n",pci));
+ DEB_EE("pci:%p\n", pci);
err = pci_enable_device(pci);
if (err < 0) {
- ERR(("pci_enable_device() failed.\n"));
+ ERR("pci_enable_device() failed\n");
goto err_free;
}
@@ -389,7 +391,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
dev->mem = ioremap(pci_resource_start(pci, 0),
pci_resource_len(pci, 0));
if (!dev->mem) {
- ERR(("ioremap() failed.\n"));
+ ERR("ioremap() failed\n");
err = -ENODEV;
goto err_release;
}
@@ -414,7 +416,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
dev->name, dev);
if (err < 0) {
- ERR(("request_irq() failed.\n"));
+ ERR("request_irq() failed\n");
goto err_unmap;
}
@@ -444,7 +446,9 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
- INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device));
+ pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
+ dev->mem, dev->revision, pci->irq,
+ pci->subsystem_vendor, pci->subsystem_device);
dev->ext = ext;
mutex_init(&dev->v4l2_lock);
@@ -464,12 +468,12 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
err = -ENODEV;
if (ext->probe && ext->probe(dev)) {
- DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
+ DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
goto err_free_i2c;
}
if (ext->attach(dev, pci_ext)) {
- DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
+ DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
goto err_free_i2c;
}
/* V4L extensions will set the pci drvdata to the v4l2_device in the
@@ -521,7 +525,7 @@ static void saa7146_remove_one(struct pci_dev *pdev)
{ NULL, 0 }
}, *p;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
dev->ext->detach(dev);
/* Zero the PCI drvdata after use. */
@@ -552,21 +556,21 @@ static void saa7146_remove_one(struct pci_dev *pdev)
int saa7146_register_extension(struct saa7146_extension* ext)
{
- DEB_EE(("ext:%p\n",ext));
+ DEB_EE("ext:%p\n", ext);
ext->driver.name = ext->name;
ext->driver.id_table = ext->pci_tbl;
ext->driver.probe = saa7146_init_one;
ext->driver.remove = saa7146_remove_one;
- printk("saa7146: register extension '%s'.\n",ext->name);
+ pr_info("register extension '%s'\n", ext->name);
return pci_register_driver(&ext->driver);
}
int saa7146_unregister_extension(struct saa7146_extension* ext)
{
- DEB_EE(("ext:%p\n",ext));
- printk("saa7146: unregister extension '%s'.\n",ext->name);
+ DEB_EE("ext:%p\n", ext);
+ pr_info("unregister extension '%s'\n", ext->name);
pci_unregister_driver(&ext->driver);
return 0;
}
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 1bd3dd762c6b..a92546144eaa 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
/****************************************************************************/
@@ -9,21 +11,23 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
struct saa7146_vv *vv = dev->vv_data;
if (fh->resources & bit) {
- DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
/* have it already allocated */
return 1;
}
/* is it free? */
if (vv->resources & bit) {
- DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
/* no, someone else uses it */
return 0;
}
/* it's free, grab it */
- fh->resources |= bit;
+ fh->resources |= bit;
vv->resources |= bit;
- DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
return 1;
}
@@ -34,9 +38,9 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
BUG_ON((fh->resources & bits) != bits);
- fh->resources &= ~bits;
+ fh->resources &= ~bits;
vv->resources &= ~bits;
- DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
}
@@ -47,7 +51,7 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
struct saa7146_buf *buf)
{
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- DEB_EE(("dev:%p, buf:%p\n",dev,buf));
+ DEB_EE("dev:%p, buf:%p\n", dev, buf);
BUG_ON(in_interrupt());
@@ -66,18 +70,19 @@ int saa7146_buffer_queue(struct saa7146_dev *dev,
struct saa7146_buf *buf)
{
assert_spin_locked(&dev->slock);
- DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
BUG_ON(!q);
if (NULL == q->curr) {
q->curr = buf;
- DEB_D(("immediately activating buffer %p\n", buf));
+ DEB_D("immediately activating buffer %p\n", buf);
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
buf->vb.state = VIDEOBUF_QUEUED;
- DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
}
return 0;
}
@@ -87,14 +92,14 @@ void saa7146_buffer_finish(struct saa7146_dev *dev,
int state)
{
assert_spin_locked(&dev->slock);
- DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
- DEB_EE(("q->curr:%p\n",q->curr));
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
BUG_ON(!q->curr);
/* finish current buffer */
if (NULL == q->curr) {
- DEB_D(("aiii. no current buffer\n"));
+ DEB_D("aiii. no current buffer\n");
return;
}
@@ -112,7 +117,7 @@ void saa7146_buffer_next(struct saa7146_dev *dev,
BUG_ON(!q);
- DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
assert_spin_locked(&dev->slock);
if (!list_empty(&q->queue)) {
@@ -122,10 +127,11 @@ void saa7146_buffer_next(struct saa7146_dev *dev,
if (!list_empty(&q->queue))
next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
q->curr = buf;
- DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
buf->activate(dev,buf,next);
} else {
- DEB_INT(("no next buffer. stopping.\n"));
+ DEB_INT("no next buffer. stopping.\n");
if( 0 != vbi ) {
/* turn off video-dma3 */
saa7146_write(dev,MC1, MASK_20);
@@ -162,11 +168,11 @@ void saa7146_buffer_timeout(unsigned long data)
struct saa7146_dev *dev = q->dev;
unsigned long flags;
- DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
- DEB_D(("timeout on %p\n", q->curr));
+ DEB_D("timeout on %p\n", q->curr);
saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
}
@@ -194,12 +200,12 @@ static int fops_open(struct file *file)
enum v4l2_buf_type type;
- DEB_EE(("file:%p, dev:%s\n", file, video_device_node_name(vdev)));
+ DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
- DEB_D(("using: %p\n",dev));
+ DEB_D("using: %p\n", dev);
type = vdev->vfl_type == VFL_TYPE_GRABBER
? V4L2_BUF_TYPE_VIDEO_CAPTURE
@@ -207,7 +213,7 @@ static int fops_open(struct file *file)
/* check if an extension is registered */
if( NULL == dev->ext ) {
- DEB_S(("no extension registered for this device.\n"));
+ DEB_S("no extension registered for this device\n");
result = -ENODEV;
goto out;
}
@@ -215,7 +221,7 @@ static int fops_open(struct file *file)
/* allocate per open data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
if (NULL == fh) {
- DEB_S(("cannot allocate memory for per open data.\n"));
+ DEB_S("cannot allocate memory for per open data\n");
result = -ENOMEM;
goto out;
}
@@ -225,13 +231,13 @@ static int fops_open(struct file *file)
fh->type = type;
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- DEB_S(("initializing vbi...\n"));
+ DEB_S("initializing vbi...\n");
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
result = saa7146_vbi_uops.open(dev,file);
if (dev->ext_vv_data->vbi_fops.open)
dev->ext_vv_data->vbi_fops.open(file);
} else {
- DEB_S(("initializing video...\n"));
+ DEB_S("initializing video...\n");
result = saa7146_video_uops.open(dev,file);
}
@@ -259,7 +265,7 @@ static int fops_release(struct file *file)
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
- DEB_EE(("file:%p\n", file));
+ DEB_EE("file:%p\n", file);
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
@@ -289,12 +295,14 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma)
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
q = &fh->video_q;
break;
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
- DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
q = &fh->vbi_q;
break;
}
@@ -312,14 +320,14 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
- DEB_EE(("file:%p, poll:%p\n",file, wait));
+ DEB_EE("file:%p, poll:%p\n", file, wait);
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
if( 0 == fh->vbi_q.streaming )
return videobuf_poll_stream(file, &fh->vbi_q, wait);
q = &fh->vbi_q;
} else {
- DEB_D(("using video queue.\n"));
+ DEB_D("using video queue\n");
q = &fh->video_q;
}
@@ -327,17 +335,17 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
if (!buf) {
- DEB_D(("buf == NULL!\n"));
+ DEB_D("buf == NULL!\n");
return POLLERR;
}
poll_wait(file, &buf->done, wait);
if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
- DEB_D(("poll succeeded!\n"));
+ DEB_D("poll succeeded!\n");
return POLLIN|POLLRDNORM;
}
- DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
+ DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
return 0;
}
@@ -346,18 +354,20 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
struct saa7146_fh *fh = file->private_data;
switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
-// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
+ file, data, (unsigned long)count);
+*/
return saa7146_video_uops.read(file,data,count,ppos);
- }
- case V4L2_BUF_TYPE_VBI_CAPTURE: {
-// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
+ file, data, (unsigned long)count);
+*/
if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
return saa7146_vbi_uops.read(file,data,count,ppos);
- else
- return -EINVAL;
- }
- break;
+ return -EINVAL;
default:
BUG();
return 0;
@@ -398,22 +408,22 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
{
u32 isr = status;
- DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
if (0 != (isr & (MASK_27))) {
- DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
saa7146_video_uops.irq_done(dev,isr);
}
if (0 != (isr & (MASK_28))) {
u32 mc2 = saa7146_read(dev, MC2);
if( 0 != (mc2 & MASK_15)) {
- DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
wake_up(&dev->vv_data->vbi_wq);
saa7146_write(dev,MC2, MASK_31);
return;
}
- DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
saa7146_vbi_uops.irq_done(dev,isr);
}
}
@@ -429,13 +439,13 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
if (vv == NULL) {
- ERR(("out of memory. aborting.\n"));
+ ERR("out of memory. aborting.\n");
return -ENOMEM;
}
ext_vv->ops = saa7146_video_ioctl_ops;
ext_vv->core_ops = &saa7146_video_ioctl_ops;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
/* set default values for video parts of the saa7146 */
saa7146_write(dev, BCS_CTRL, 0x80400040);
@@ -450,7 +460,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
if( NULL == vv->d_clipping.cpu_addr ) {
- ERR(("out of memory. aborting.\n"));
+ ERR("out of memory. aborting.\n");
kfree(vv);
return -1;
}
@@ -471,7 +481,7 @@ int saa7146_vv_release(struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
v4l2_device_unregister(&dev->v4l2_dev);
pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
@@ -490,7 +500,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
int err;
int i;
- DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
// released by vfd->release
vfd = video_device_alloc();
@@ -509,13 +519,13 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
err = video_register_device(vfd, type, -1);
if (err < 0) {
- ERR(("cannot register v4l2 device. skipping.\n"));
+ ERR("cannot register v4l2 device. skipping.\n");
video_device_release(vfd);
return err;
}
- INFO(("%s: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(vfd)));
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
*vid = vfd;
return 0;
@@ -524,7 +534,7 @@ EXPORT_SYMBOL_GPL(saa7146_register_device);
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE("dev:%p\n", dev);
video_unregister_device(*vid);
*vid = NULL;
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
index 1d1d8d200755..79ad73accb27 100644
--- a/drivers/media/common/saa7146_hlp.c
+++ b/drivers/media/common/saa7146_hlp.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <media/saa7146_vv.h>
@@ -711,8 +713,8 @@ static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa71
int depth = sfmt->depth;
- DEB_CAP(("[size=%dx%d,fields=%s]\n",
- width,height,v4l2_field_names[field]));
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
if( bytesperline != 0) {
vdma1.pitch = bytesperline*2;
@@ -837,8 +839,8 @@ static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa71
BUG_ON(0 == buf->pt[1].dma);
BUG_ON(0 == buf->pt[2].dma);
- DEB_CAP(("[size=%dx%d,fields=%s]\n",
- width,height,v4l2_field_names[field]));
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
/* fixme: look at bytesperline! */
@@ -998,12 +1000,12 @@ void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struc
struct saa7146_vv *vv = dev->vv_data;
u32 vdma1_prot_addr;
- DEB_CAP(("buf:%p, next:%p\n",buf,next));
+ DEB_CAP("buf:%p, next:%p\n", buf, next);
vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
if( 0 == vdma1_prot_addr ) {
/* clear out beginning of streaming bit (rps register 0)*/
- DEB_CAP(("forcing sync to new frame\n"));
+ DEB_CAP("forcing sync to new frame\n");
saa7146_write(dev, MC2, MASK_27 );
}
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index b2ba9dc0dd6d..22027198129d 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -1,8 +1,10 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
-//fm DEB_I2C(("'%s'.\n", adapter->name));
+ /* DEB_I2C("'%s'\n", adapter->name); */
return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_QUICK
@@ -14,9 +16,7 @@ static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
{
u32 iicsta = saa7146_read(dev, I2C_STATUS);
-/*
- DEB_I2C(("status: 0x%08x\n",iicsta));
-*/
+ /* DEB_I2C("status: 0x%08x\n", iicsta); */
return iicsta;
}
@@ -39,10 +39,11 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
plus one extra byte to address the device */
mem = 1 + ((mem-1) / 3);
- /* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
- size. if we exceed this limit... */
- if ( (4*mem) > SAA7146_I2C_MEM ) {
-//fm DEB_I2C(("cannot prepare i2c-message.\n"));
+ /* we assume that op points to a memory of at least
+ * SAA7146_I2C_MEM bytes size. if we exceed this limit...
+ */
+ if ((4 * mem) > SAA7146_I2C_MEM) {
+ /* DEB_I2C("cannot prepare i2c-message\n"); */
return -ENOMEM;
}
@@ -123,7 +124,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
/* yes, kill ongoing operation */
- DEB_I2C(("busy_state detected.\n"));
+ DEB_I2C("busy_state detected\n");
/* set "ABORT-OPERATION"-bit (bit 7)*/
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
@@ -141,7 +142,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
if ( dev->i2c_bitrate != status ) {
- DEB_I2C(("error_state detected. status:0x%08x\n",status));
+ DEB_I2C("error_state detected. status:0x%08x\n", status);
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
@@ -164,7 +165,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
/* if any error is still present, a fatal error has occurred ... */
status = saa7146_i2c_status(dev);
if ( dev->i2c_bitrate != status ) {
- DEB_I2C(("fatal error. status:0x%08x\n",status));
+ DEB_I2C("fatal error. status:0x%08x\n", status);
return -1;
}
@@ -181,7 +182,8 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
unsigned long timeout;
/* write out i2c-command */
- DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
+ DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n",
+ *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op);
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
@@ -202,7 +204,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* a signal arrived */
return -ERESTARTSYS;
- printk(KERN_WARNING "%s %s [irq]: timed out waiting for end of xfer\n",
+ pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
@@ -220,7 +222,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
break;
}
if (time_after(jiffies,timeout)) {
- printk(KERN_WARNING "%s %s: timed out waiting for MC2\n",
+ pr_warn("%s %s: timed out waiting for MC2\n",
dev->name, __func__);
return -EIO;
}
@@ -237,7 +239,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* this is normal when probing the bus
* (no answer from nonexisistant device...)
*/
- printk(KERN_WARNING "%s %s [poll]: timed out waiting for end of xfer\n",
+ pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
@@ -257,24 +259,24 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
if ( 0 == (status & SAA7146_I2C_ERR) ||
0 == (status & SAA7146_I2C_BUSY) ) {
/* it may take some time until ERR goes high - ignore */
- DEB_I2C(("unexpected i2c status %04x\n", status));
+ DEB_I2C("unexpected i2c status %04x\n", status);
}
if( 0 != (status & SAA7146_I2C_SPERR) ) {
- DEB_I2C(("error due to invalid start/stop condition.\n"));
+ DEB_I2C("error due to invalid start/stop condition\n");
}
if( 0 != (status & SAA7146_I2C_DTERR) ) {
- DEB_I2C(("error in data transmission.\n"));
+ DEB_I2C("error in data transmission\n");
}
if( 0 != (status & SAA7146_I2C_DRERR) ) {
- DEB_I2C(("error when receiving data.\n"));
+ DEB_I2C("error when receiving data\n");
}
if( 0 != (status & SAA7146_I2C_AL) ) {
- DEB_I2C(("error because arbitration lost.\n"));
+ DEB_I2C("error because arbitration lost\n");
}
/* we handle address-errors here */
if( 0 != (status & SAA7146_I2C_APERR) ) {
- DEB_I2C(("error in address phase.\n"));
+ DEB_I2C("error in address phase\n");
return -EREMOTEIO;
}
@@ -284,7 +286,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int shor
/* read back data, just in case we were reading ... */
*dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
- DEB_I2C(("after: 0x%08x\n",*dword));
+ DEB_I2C("after: 0x%08x\n", *dword);
return 0;
}
@@ -299,7 +301,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
return -ERESTARTSYS;
for(i=0;i<num;i++) {
- DEB_I2C(("msg:%d/%d\n",i+1,num));
+ DEB_I2C("msg:%d/%d\n", i+1, num);
}
/* prepare the message(s), get number of u32s to transfer */
@@ -316,7 +318,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* reset the i2c-device if necessary */
err = saa7146_i2c_reset(dev);
if ( 0 > err ) {
- DEB_I2C(("could not reset i2c-device.\n"));
+ DEB_I2C("could not reset i2c-device\n");
goto out;
}
@@ -336,7 +338,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
address error and trust the saa7146 address error detection. */
if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
goto out;
- DEB_I2C(("error while sending message(s). starting again.\n"));
+ DEB_I2C("error while sending message(s). starting again\n");
break;
}
}
@@ -356,13 +358,13 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
- DEB_I2C(("could not cleanup i2c-message.\n"));
+ DEB_I2C("could not cleanup i2c-message\n");
err = -1;
goto out;
}
/* return the number of delivered messages */
- DEB_I2C(("transmission successful. (msg:%d).\n",err));
+ DEB_I2C("transmission successful. (msg:%d)\n", err);
out:
/* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continuing */
@@ -370,7 +372,7 @@ out:
__le32 zero = 0;
saa7146_i2c_reset(dev);
if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
- INFO(("revision 0 error. this should never happen.\n"));
+ pr_info("revision 0 error. this should never happen\n");
}
}
@@ -400,7 +402,7 @@ static struct i2c_algorithm saa7146_algo = {
int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
{
- DEB_EE(("bitrate: 0x%08x\n",bitrate));
+ DEB_EE("bitrate: 0x%08x\n", bitrate);
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24));
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
index afe85801d6ca..b2e718343739 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146_vbi.c
@@ -14,7 +14,7 @@ static int vbi_workaround(struct saa7146_dev *dev)
DECLARE_WAITQUEUE(wait, current);
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
/* once again, a bug in the saa7146: the brs acquisition
is buggy and especially the BXO-counter does not work
@@ -40,14 +40,14 @@ static int vbi_workaround(struct saa7146_dev *dev)
WRITE_RPS1(0xc000008c);
/* wait for vbi_a or vbi_b*/
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
- DEB_D(("...using port b\n"));
+ DEB_D("...using port b\n");
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
/*
WRITE_RPS1(CMD_PAUSE | MASK_09);
*/
} else {
- DEB_D(("...using port a\n"));
+ DEB_D("...using port a\n");
WRITE_RPS1(CMD_PAUSE | MASK_10);
}
/* upload brs */
@@ -103,7 +103,7 @@ static int vbi_workaround(struct saa7146_dev *dev)
schedule();
- DEB_VBI(("brs bug workaround %d/1.\n",i));
+ DEB_VBI("brs bug workaround %d/1\n", i);
remove_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_RUNNING;
@@ -116,7 +116,8 @@ static int vbi_workaround(struct saa7146_dev *dev)
if(signal_pending(current)) {
- DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1)));
+ DEB_VBI("aborted (rps:0x%08x)\n",
+ saa7146_read(dev, RPS_ADDR1));
/* stop rps1 for sure */
saa7146_write(dev, MC1, MASK_29);
@@ -207,7 +208,7 @@ static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_vv *vv = dev->vv_data;
buf->vb.state = VIDEOBUF_ACTIVE;
- DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
+ DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
saa7146_set_vbi_capture(dev,buf,next);
mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
@@ -228,10 +229,10 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
llength = vbi_pixel_to_capture;
size = lines * llength;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_VBI(("size mismatch.\n"));
+ DEB_VBI("size mismatch\n");
return -EINVAL;
}
@@ -263,7 +264,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
return 0;
oops:
- DEB_VBI(("error out.\n"));
+ DEB_VBI("error out\n");
saa7146_dma_free(dev,q,buf);
return err;
@@ -279,7 +280,7 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned
*size = lines * llength;
*count = 2;
- DEB_VBI(("count:%d, size:%d\n",*count,*size));
+ DEB_VBI("count:%d, size:%d\n", *count, *size);
return 0;
}
@@ -292,7 +293,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
saa7146_buffer_queue(dev,&vv->vbi_q,buf);
}
@@ -303,7 +304,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_VBI(("vb:%p\n",vb));
+ DEB_VBI("vb:%p\n", vb);
saa7146_dma_free(dev,q,buf);
}
@@ -321,7 +322,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
unsigned long flags;
- DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
spin_lock_irqsave(&dev->slock,flags);
@@ -354,14 +355,14 @@ static void vbi_read_timeout(unsigned long data)
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
- DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
vbi_stop(fh, file);
}
static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
INIT_LIST_HEAD(&vv->vbi_q.queue);
@@ -380,11 +381,11 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
int ret = 0;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
if (0 == ret) {
- DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
+ DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
return -EBUSY;
}
@@ -425,7 +426,7 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
saa7146_write(dev, BRS_CTRL, 0x00000001);
if (0 != (ret = vbi_workaround(dev))) {
- DEB_VBI(("vbi workaround failed!\n"));
+ DEB_VBI("vbi workaround failed!\n");
/* return ret;*/
}
}
@@ -439,7 +440,7 @@ static void vbi_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_vv *vv = dev->vv_data;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
if( fh == vv->vbi_streaming ) {
vbi_stop(fh, file);
@@ -453,13 +454,13 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
spin_lock(&dev->slock);
if (vv->vbi_q.curr) {
- DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr));
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr);
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
} else {
- DEB_VBI(("dev:%p\n",dev));
+ DEB_VBI("dev:%p\n", dev);
}
saa7146_buffer_next(dev,&vv->vbi_q,1);
@@ -473,7 +474,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
- DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
if( NULL == vv->vbi_streaming ) {
// fixme: check if dma3 is available
@@ -482,7 +483,8 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff
}
if( fh != vv->vbi_streaming ) {
- DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming));
+ DEB_VBI("open %p is already using vbi capture\n",
+ vv->vbi_streaming);
return -EBUSY;
}
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 9aafa4e969a8..384b358d3037 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/saa7146_vv.h>
#include <media/v4l2-chip-ident.h>
@@ -94,7 +96,7 @@ struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fou
}
}
- DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc));
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
return NULL;
}
@@ -107,32 +109,32 @@ int saa7146_start_preview(struct saa7146_fh *fh)
struct v4l2_format fmt;
int ret = 0, err = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
/* check if we have overlay informations */
if( NULL == fh->ov.fh ) {
- DEB_D(("no overlay data available. try S_FMT first.\n"));
+ DEB_D("no overlay data available. try S_FMT first.\n");
return -EAGAIN;
}
/* check if streaming capture is running */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("streaming capture is active.\n"));
+ DEB_D("streaming capture is active\n");
return -EBUSY;
}
/* check if overlay is running */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
if (vv->video_fh == fh) {
- DEB_D(("overlay is already active.\n"));
+ DEB_D("overlay is already active\n");
return 0;
}
- DEB_D(("overlay is already active in another open.\n"));
+ DEB_D("overlay is already active in another open\n");
return -EBUSY;
}
if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D(("cannot get necessary overlay resources\n"));
+ DEB_D("cannot get necessary overlay resources\n");
return -EBUSY;
}
@@ -145,13 +147,13 @@ int saa7146_start_preview(struct saa7146_fh *fh)
fh->ov.win = fmt.fmt.win;
vv->ov_data = &fh->ov;
- DEB_D(("%dx%d+%d+%d %s field=%s\n",
- fh->ov.win.w.width,fh->ov.win.w.height,
- fh->ov.win.w.left,fh->ov.win.w.top,
- vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
+ DEB_D("%dx%d+%d+%d %s field=%s\n",
+ fh->ov.win.w.width, fh->ov.win.w.height,
+ fh->ov.win.w.left, fh->ov.win.w.top,
+ vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]);
if (0 != (ret = saa7146_enable_overlay(fh))) {
- DEB_D(("enabling overlay failed: %d\n",ret));
+ DEB_D("enabling overlay failed: %d\n", ret);
saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
return ret;
}
@@ -168,22 +170,22 @@ int saa7146_stop_preview(struct saa7146_fh *fh)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
/* check if streaming capture is running */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("streaming capture is active.\n"));
+ DEB_D("streaming capture is active\n");
return -EBUSY;
}
/* check if overlay is running at all */
if ((vv->video_status & STATUS_OVERLAY) == 0) {
- DEB_D(("no active overlay.\n"));
+ DEB_D("no active overlay\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_D(("overlay is active, but in another open.\n"));
+ DEB_D("overlay is active, but in another open\n");
return -EBUSY;
}
@@ -268,7 +270,7 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
int length = dma->sglen;
struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
- DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length));
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
if( 0 != IS_PLANAR(sfmt->trans)) {
struct saa7146_pgtable *pt1 = &buf->pt[0];
@@ -288,7 +290,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/4))%PAGE_SIZE;
- DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
break;
}
case 16: {
@@ -298,7 +301,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/2))%PAGE_SIZE;
- DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
break;
}
default: {
@@ -387,23 +391,23 @@ static int video_begin(struct saa7146_fh *fh)
unsigned int resource;
int ret = 0, err = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
if ((vv->video_status & STATUS_CAPTURE) != 0) {
if (vv->video_fh == fh) {
- DEB_S(("already capturing.\n"));
+ DEB_S("already capturing\n");
return 0;
}
- DEB_S(("already capturing in another open.\n"));
+ DEB_S("already capturing in another open\n");
return -EBUSY;
}
if ((vv->video_status & STATUS_OVERLAY) != 0) {
- DEB_S(("warning: suspending overlay video for streaming capture.\n"));
+ DEB_S("warning: suspending overlay video for streaming capture\n");
vv->ov_suspend = vv->video_fh;
err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
if (0 != err) {
- DEB_D(("suspending video failed. aborting\n"));
+ DEB_D("suspending video failed. aborting\n");
return err;
}
}
@@ -420,7 +424,7 @@ static int video_begin(struct saa7146_fh *fh)
ret = saa7146_res_get(fh, resource);
if (0 == ret) {
- DEB_S(("cannot get capture resource %d\n",resource));
+ DEB_S("cannot get capture resource %d\n", resource);
if (vv->ov_suspend != NULL) {
saa7146_start_preview(vv->ov_suspend);
vv->ov_suspend = NULL;
@@ -448,15 +452,15 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
unsigned long flags;
unsigned int resource;
u32 dmas = 0;
- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S(("not capturing.\n"));
+ DEB_S("not capturing\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_S(("capturing, but in another open.\n"));
+ DEB_S("capturing, but in another open\n");
return -EBUSY;
}
@@ -530,7 +534,7 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_format *fmt;
- DEB_EE(("VIDIOC_S_FBUF\n"));
+ DEB_EE("VIDIOC_S_FBUF\n");
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
return -EPERM;
@@ -542,13 +546,13 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
/* planar formats are not allowed for overlay video, clipping and video dma would clash */
if (fmt->flags & FORMAT_IS_PLANAR)
- DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",
- (char *)&fmt->pixelformat));
+ DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
+ (char *)&fmt->pixelformat);
/* check if overlay is running */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
if (vv->video_fh != fh) {
- DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+ DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
return -EBUSY;
}
}
@@ -559,7 +563,7 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
- DEB_D(("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline));
+ DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
}
return 0;
}
@@ -588,7 +592,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
if (ctrl == NULL)
return -EINVAL;
- DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n", c->id));
+ DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id);
*c = *ctrl;
return 0;
}
@@ -607,25 +611,25 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
case V4L2_CID_BRIGHTNESS:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0xff & (value >> 24);
- DEB_D(("V4L2_CID_BRIGHTNESS: %d\n", c->value));
+ DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value);
break;
case V4L2_CID_CONTRAST:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 16);
- DEB_D(("V4L2_CID_CONTRAST: %d\n", c->value));
+ DEB_D("V4L2_CID_CONTRAST: %d\n", c->value);
break;
case V4L2_CID_SATURATION:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 0);
- DEB_D(("V4L2_CID_SATURATION: %d\n", c->value));
+ DEB_D("V4L2_CID_SATURATION: %d\n", c->value);
break;
case V4L2_CID_VFLIP:
c->value = vv->vflip;
- DEB_D(("V4L2_CID_VFLIP: %d\n", c->value));
+ DEB_D("V4L2_CID_VFLIP: %d\n", c->value);
break;
case V4L2_CID_HFLIP:
c->value = vv->hflip;
- DEB_D(("V4L2_CID_HFLIP: %d\n", c->value));
+ DEB_D("V4L2_CID_HFLIP: %d\n", c->value);
break;
default:
return -EINVAL;
@@ -641,7 +645,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl) {
- DEB_D(("unknown control %d\n", c->id));
+ DEB_D("unknown control %d\n", c->id);
return -EINVAL;
}
@@ -686,14 +690,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
case V4L2_CID_HFLIP:
/* fixme: we can support changing VFLIP and HFLIP here... */
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+ DEB_D("V4L2_CID_HFLIP while active capture\n");
return -EBUSY;
}
vv->hflip = c->value;
break;
case V4L2_CID_VFLIP:
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+ DEB_D("V4L2_CID_VFLIP while active capture\n");
return -EBUSY;
}
vv->vflip = c->value;
@@ -748,7 +752,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
int maxw, maxh;
int calc_bpl;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
if (NULL == fmt)
@@ -777,7 +781,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
vv->last_field = V4L2_FIELD_INTERLACED;
break;
default:
- DEB_D(("no known field mode '%d'.\n", field));
+ DEB_D("no known field mode '%d'\n", field);
return -EINVAL;
}
@@ -796,8 +800,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
f->fmt.pix.bytesperline = calc_bpl;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
- DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", f->fmt.pix.width,
- f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage));
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
return 0;
}
@@ -811,22 +816,23 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_f
enum v4l2_field field;
int maxw, maxh;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
if (NULL == vv->ov_fb.base) {
- DEB_D(("no fb base set.\n"));
+ DEB_D("no fb base set\n");
return -EINVAL;
}
if (NULL == vv->ov_fmt) {
- DEB_D(("no fb fmt set.\n"));
+ DEB_D("no fb fmt set\n");
return -EINVAL;
}
if (win->w.width < 48 || win->w.height < 32) {
- DEB_D(("min width/height. (%d,%d)\n", win->w.width, win->w.height));
+ DEB_D("min width/height. (%d,%d)\n",
+ win->w.width, win->w.height);
return -EINVAL;
}
if (win->clipcount > 16) {
- DEB_D(("clipcount too big.\n"));
+ DEB_D("clipcount too big\n");
return -EINVAL;
}
@@ -848,7 +854,7 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_f
case V4L2_FIELD_INTERLACED:
break;
default:
- DEB_D(("no known field mode '%d'.\n", field));
+ DEB_D("no known field mode '%d'\n", field);
return -EINVAL;
}
@@ -868,16 +874,17 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_EE(("streaming capture is active\n"));
+ DEB_EE("streaming capture is active\n");
return -EBUSY;
}
err = vidioc_try_fmt_vid_cap(file, fh, f);
if (0 != err)
return err;
fh->video_fmt = f->fmt.pix;
- DEB_EE(("set to pixelformat '%4.4s'\n", (char *)&fh->video_fmt.pixelformat));
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&fh->video_fmt.pixelformat);
return 0;
}
@@ -888,7 +895,7 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh));
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
err = vidioc_try_fmt_vid_overlay(file, fh, f);
if (0 != err)
return err;
@@ -931,7 +938,7 @@ static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
if (e->index < 0 )
return -EINVAL;
if( e->index < dev->ext_vv_data->num_stds ) {
- DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
+ DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
return 0;
}
@@ -946,10 +953,10 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
int found = 0;
int err, i;
- DEB_EE(("VIDIOC_S_STD\n"));
+ DEB_EE("VIDIOC_S_STD\n");
if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
- DEB_D(("cannot change video standard while streaming capture is active\n"));
+ DEB_D("cannot change video standard while streaming capture is active\n");
return -EBUSY;
}
@@ -957,7 +964,7 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
vv->ov_suspend = vv->video_fh;
err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
if (0 != err) {
- DEB_D(("suspending video failed. aborting\n"));
+ DEB_D("suspending video failed. aborting\n");
return err;
}
}
@@ -978,11 +985,11 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
}
if (!found) {
- DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
return -EINVAL;
}
- DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name));
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
return 0;
}
@@ -990,7 +997,7 @@ static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
{
int err;
- DEB_D(("VIDIOC_OVERLAY on:%d\n", on));
+ DEB_D("VIDIOC_OVERLAY on:%d\n", on);
if (on)
err = saa7146_start_preview(fh);
else
@@ -1047,7 +1054,7 @@ static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type typ
struct saa7146_fh *fh = __fh;
int err;
- DEB_D(("VIDIOC_STREAMON, type:%d\n", type));
+ DEB_D("VIDIOC_STREAMON, type:%d\n", type);
err = video_begin(fh);
if (err)
@@ -1066,18 +1073,18 @@ static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type ty
struct saa7146_vv *vv = dev->vv_data;
int err;
- DEB_D(("VIDIOC_STREAMOFF, type:%d\n", type));
+ DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
/* ugly: we need to copy some checks from video_end(),
because videobuf_streamoff() relies on the capture running.
check and fix this */
if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S(("not capturing.\n"));
+ DEB_S("not capturing\n");
return 0;
}
if (vv->video_fh != fh) {
- DEB_S(("capturing, but in another open.\n"));
+ DEB_S("capturing, but in another open\n");
return -EBUSY;
}
@@ -1087,7 +1094,7 @@ static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type ty
else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
err = videobuf_streamoff(&fh->vbi_q);
if (0 != err) {
- DEB_D(("warning: videobuf_streamoff() failed.\n"));
+ DEB_D("warning: videobuf_streamoff() failed\n");
video_end(fh, file);
} else {
err = video_end(fh, file);
@@ -1174,25 +1181,27 @@ static int buffer_prepare(struct videobuf_queue *q,
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
int size,err = 0;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
/* sanity checks */
if (fh->video_fmt.width < 48 ||
fh->video_fmt.height < 32 ||
fh->video_fmt.width > vv->standard->h_max_out ||
fh->video_fmt.height > vv->standard->v_max_out) {
- DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height));
+ DEB_D("w (%d) / h (%d) out of bounds\n",
+ fh->video_fmt.width, fh->video_fmt.height);
return -EINVAL;
}
size = fh->video_fmt.sizeimage;
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_D(("size mismatch.\n"));
+ DEB_D("size mismatch\n");
return -EINVAL;
}
- DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
- fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
+ DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ fh->video_fmt.width, fh->video_fmt.height,
+ size, v4l2_field_names[fh->video_fmt.field]);
if (buf->vb.width != fh->video_fmt.width ||
buf->vb.bytesperline != fh->video_fmt.bytesperline ||
buf->vb.height != fh->video_fmt.height ||
@@ -1238,7 +1247,7 @@ static int buffer_prepare(struct videobuf_queue *q,
return 0;
oops:
- DEB_D(("error out.\n"));
+ DEB_D("error out\n");
saa7146_dma_free(dev,q,buf);
return err;
@@ -1259,7 +1268,7 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned
*count = (max_memory*1048576) / *size;
}
- DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size));
+ DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
return 0;
}
@@ -1272,7 +1281,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
saa7146_buffer_queue(fh->dev,&vv->video_q,buf);
}
@@ -1283,7 +1292,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- DEB_CAP(("vbuf:%p\n",vb));
+ DEB_CAP("vbuf:%p\n", vb);
saa7146_dma_free(dev,q,buf);
@@ -1347,18 +1356,14 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
struct saa7146_fh *fh = file->private_data;
struct saa7146_vv *vv = dev->vv_data;
struct videobuf_queue *q = &fh->video_q;
- int err;
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- err = video_end(fh, file);
- } else if (IS_OVERLAY_ACTIVE(fh) != 0) {
- err = saa7146_stop_preview(fh);
- }
+ if (IS_CAPTURE_ACTIVE(fh) != 0)
+ video_end(fh, file);
+ else if (IS_OVERLAY_ACTIVE(fh) != 0)
+ saa7146_stop_preview(fh);
videobuf_stop(q);
-
/* hmm, why is this function declared void? */
- /* return err */
}
@@ -1368,7 +1373,7 @@ static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
struct saa7146_dmaqueue *q = &vv->video_q;
spin_lock(&dev->slock);
- DEB_CAP(("called.\n"));
+ DEB_CAP("called\n");
/* only finish the buffer if we have one... */
if( NULL != q->curr ) {
@@ -1386,15 +1391,15 @@ static ssize_t video_read(struct file *file, char __user *data, size_t count, lo
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
- DEB_EE(("called.\n"));
+ DEB_EE("called\n");
if ((vv->video_status & STATUS_CAPTURE) != 0) {
/* fixme: should we allow read() captures while streaming capture? */
if (vv->video_fh == fh) {
- DEB_S(("already capturing.\n"));
+ DEB_S("already capturing\n");
return -EBUSY;
}
- DEB_S(("already capturing in another open.\n"));
+ DEB_S("already capturing in another open\n");
return -EBUSY;
}
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 20d24fca2cfb..196c12a55f9a 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -28,5 +28,5 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/common/tuners/mt20xx.c
index d0e70e10a717..0e74e97e0d1a 100644
--- a/drivers/media/common/tuners/mt20xx.c
+++ b/drivers/media/common/tuners/mt20xx.c
@@ -430,11 +430,10 @@ static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna)
{
struct microtune_priv *priv = fe->tuner_priv;
unsigned char buf[2];
- int ret;
buf[0] = 6;
buf[1] = antenna ? 0x11 : 0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
tuner_dbg("mt2050: enabled antenna connector %d\n", antenna);
}
@@ -574,21 +573,20 @@ static int mt2050_init(struct dvb_frontend *fe)
{
struct microtune_priv *priv = fe->tuner_priv;
unsigned char buf[2];
- int ret;
- buf[0]=6;
- buf[1]=0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // power
+ buf[0] = 6;
+ buf[1] = 0x10;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */
- buf[0]=0x0f;
- buf[1]=0x0f;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // m1lo
+ buf[0] = 0x0f;
+ buf[1] = 0x0f;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */
- buf[0]=0x0d;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
+ buf[0] = 0x0d;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1);
- tuner_dbg("mt2050: sro is %x\n",buf[0]);
+ tuner_dbg("mt2050: sro is %x\n", buf[0]);
memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops));
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index 56fe75c94deb..54be9e6faaaf 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -309,7 +309,6 @@ static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val);
static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
u8 *RegVal, int *count);
-static u32 MXL_GetXtalInt(u32 Xtal_Freq);
static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
@@ -2307,14 +2306,6 @@ static u16 MXL_IFSynthInit(struct dvb_frontend *fe)
return status ;
}
-static u32 MXL_GetXtalInt(u32 Xtal_Freq)
-{
- if ((Xtal_Freq % 1000000) == 0)
- return (Xtal_Freq / 10000);
- else
- return (((Xtal_Freq / 1000000) + 1)*100);
-}
-
static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
{
struct mxl5005s_state *state = fe->tuner_priv;
@@ -2324,13 +2315,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
u32 Kdbl_RF = 2;
u32 tg_divval;
u32 tg_lo;
- u32 Xtal_Int;
u32 Fref_TG;
u32 Fvco;
- Xtal_Int = MXL_GetXtalInt(state->Fxtal);
-
state->RF_IN = RF_Freq;
MXL_SynthRFTGLO_Calc(fe);
@@ -2779,6 +2767,16 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8;
/* below equation is same as above but much harder to debug.
+ *
+ * static u32 MXL_GetXtalInt(u32 Xtal_Freq)
+ * {
+ * if ((Xtal_Freq % 1000000) == 0)
+ * return (Xtal_Freq / 10000);
+ * else
+ * return (((Xtal_Freq / 1000000) + 1)*100);
+ * }
+ *
+ * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal);
* tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) -
* ((state->TG_LO/10000)*divider_val *
* (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 *
diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c
index 1f1db20d46b1..e29cc2bc113a 100644
--- a/drivers/media/common/tuners/tda18212.c
+++ b/drivers/media/common/tuners/tda18212.c
@@ -18,7 +18,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "tda18212_priv.h"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "tda18212.h"
+
+struct tda18212_priv {
+ struct tda18212_config *cfg;
+ struct i2c_adapter *i2c;
+};
+
+#define dbg(fmt, arg...) \
+do { \
+ if (debug) \
+ pr_info("%s: " fmt, __func__, ##arg); \
+} while (0)
static int debug;
module_param(debug, int, 0644);
@@ -46,7 +59,8 @@ static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
if (ret == 1) {
ret = 0;
} else {
- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
+ pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n",
+ ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -77,7 +91,8 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
memcpy(val, buf, len);
ret = 0;
} else {
- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
+ pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n",
+ ret, reg, len);
ret = -EREMOTEIO;
}
@@ -129,8 +144,8 @@ static int tda18212_set_params(struct dvb_frontend *fe,
{ 0x92, 0x53, 0x03 }, /* DVB-C */
};
- dbg("%s: delsys=%d RF=%d BW=%d", __func__,
- c->delivery_system, c->frequency, c->bandwidth_hz);
+ dbg("delsys=%d RF=%d BW=%d\n",
+ c->delivery_system, c->frequency, c->bandwidth_hz);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
@@ -196,7 +211,7 @@ exit:
return ret;
error:
- dbg("%s: failed:%d", __func__, ret);
+ dbg("failed:%d\n", ret);
goto exit;
}
@@ -245,13 +260,13 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
- dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
+ dbg("ret:%d chip ID:%02x\n", ret, val);
if (ret || val != 0xc7) {
kfree(priv);
return NULL;
}
- info("NXP TDA18212HN successfully identified.");
+ pr_info("NXP TDA18212HN successfully identified\n");
memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
sizeof(struct dvb_tuner_ops));
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
index aae40e52af5b..39c645787b62 100644
--- a/drivers/media/common/tuners/tda18271-common.c
+++ b/drivers/media/common/tuners/tda18271-common.c
@@ -676,10 +676,28 @@ fail:
return ret;
}
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+int _tda_printk(struct tda18271_priv *state, const char *level,
+ const char *func, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int rtn;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (state)
+ rtn = printk("%s%s: [%d-%04x|%c] %pV",
+ level, func, i2c_adapter_id(state->i2c_props.adap),
+ state->i2c_props.addr,
+ (state->role == TDA18271_MASTER) ? 'M' : 'S',
+ &vaf);
+ else
+ rtn = printk("%s%s: %pV", level, func, &vaf);
+
+ va_end(args);
+
+ return rtn;
+}
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 57022e88e338..63cc4004e211 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -1230,7 +1230,7 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
return 0;
}
-static struct dvb_tuner_ops tda18271_tuner_ops = {
+static const struct dvb_tuner_ops tda18271_tuner_ops = {
.info = {
.name = "NXP TDA18271HD",
.frequency_min = 45000000,
diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h
index 9589ab0576d2..94340f47562b 100644
--- a/drivers/media/common/tuners/tda18271-priv.h
+++ b/drivers/media/common/tuners/tda18271-priv.h
@@ -136,29 +136,26 @@ extern int tda18271_debug;
#define DBG_ADV 8
#define DBG_CAL 16
-#define tda_printk(st, kern, fmt, arg...) do {\
- if (st) { \
- struct tda18271_priv *state = st; \
- printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \
- i2c_adapter_id(state->i2c_props.adap), \
- state->i2c_props.addr, \
- (state->role == TDA18271_MASTER) \
- ? "M" : "S", ##arg); \
- } else \
- printk(kern "%s: " fmt, __func__, ##arg); \
+__attribute__((format(printf, 4, 5)))
+int _tda_printk(struct tda18271_priv *state, const char *level,
+ const char *func, const char *fmt, ...);
+
+#define tda_printk(st, lvl, fmt, arg...) \
+ _tda_printk(st, lvl, __func__, fmt, ##arg)
+
+#define tda_dprintk(st, lvl, fmt, arg...) \
+do { \
+ if (tda18271_debug & lvl) \
+ tda_printk(st, KERN_DEBUG, fmt, ##arg); \
} while (0)
-#define tda_dprintk(st, lvl, fmt, arg...) do {\
- if (tda18271_debug & lvl) \
- tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0)
-
-#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
-#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
-#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg)
-#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg)
-#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg)
-#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg)
-#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg)
+#define tda_info(fmt, arg...) pr_info(fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg)
+#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg)
+#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg)
+#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg)
+#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg)
#define tda_fail(ret) \
({ \
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
index b21b6ea68b25..e0d5b43772b8 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/common/tuners/tda827x.c
@@ -176,7 +176,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
if_freq = 5000000;
break;
}
- tuner_freq = params->frequency + if_freq;
+ tuner_freq = params->frequency;
i = 0;
while (tda827x_table[i].lomax < tuner_freq) {
@@ -185,6 +185,8 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
i++;
}
+ tuner_freq += if_freq;
+
N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2);
buf[0] = 0;
buf[1] = (N>>8) | 0x40;
@@ -540,7 +542,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
if_freq = 5000000;
break;
}
- tuner_freq = params->frequency + if_freq;
+ tuner_freq = params->frequency;
if (fe->ops.info.type == FE_QAM) {
dprintk("%s select tda827xa_dvbc\n", __func__);
@@ -554,6 +556,8 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
i++;
}
+ tuner_freq += if_freq;
+
N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd;
buf[0] = 0; // subaddress
buf[1] = N >> 8;
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index 16fba6b59616..3acbaa04e1b3 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -614,6 +614,13 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
p += len;
size -= len;
}
+
+ /* silently fail if the frontend doesn't support I2C flush */
+ rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
+ if ((rc < 0) && (rc != -EINVAL)) {
+ tuner_err("error executing flush: %d\n", rc);
+ return rc;
+ }
}
return 0;
}
@@ -933,11 +940,16 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
* that xc2028 will be in a safe state.
* Maybe this might also be needed for DTV.
*/
- if (new_type == V4L2_TUNER_ANALOG_TV) {
+ switch (new_type) {
+ case V4L2_TUNER_ANALOG_TV:
rc = send_seq(priv, {0x00, 0x00});
- /* Analog modes require offset = 0 */
- } else {
+ /* Analog mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_RADIO:
+ /* Radio mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_DIGITAL_TV:
/*
* Digital modes require an offset to adjust to the
* proper frequency. The offset depends on what
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
index 9778c96a5006..9ebfb2d0ff14 100644
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ b/drivers/media/common/tuners/tuner-xc2028.h
@@ -54,6 +54,7 @@ struct xc2028_config {
/* xc2028 commands for callback */
#define XC2028_TUNER_RESET 0
#define XC2028_RESET_CLK 1
+#define XC2028_I2C_FLUSH 2
#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE))
extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
index b97cf7208a18..3d04a8dba99e 100644
--- a/drivers/media/dvb/b2c2/Makefile
+++ b/drivers/media/dvb/b2c2/Makefile
@@ -12,5 +12,5 @@ obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
b2c2-flexcop-usb-objs = flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
index d98f1d49ffa8..0713b3af2050 100644
--- a/drivers/media/dvb/bt8xx/Makefile
+++ b/drivers/media/dvb/bt8xx/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/video/bt8xx
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video/bt8xx
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile
index de4fe193c3ef..38019bafb862 100644
--- a/drivers/media/dvb/ddbridge/Makefile
+++ b/drivers/media/dvb/ddbridge/Makefile
@@ -6,9 +6,9 @@ ddbridge-objs := ddbridge-core.o
obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
# For the staging CI driver cxd2099
-EXTRA_CFLAGS += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
index 573d540f213e..ba9a643b9c6a 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -507,15 +507,14 @@ static u32 ddb_input_avail(struct ddb_input *input)
return 0;
}
-static size_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
{
struct ddb *dev = input->port->dev;
u32 left = count;
- u32 idx, off, free, stat = input->stat;
+ u32 idx, free, stat = input->stat;
int ret;
idx = (stat >> 11) & 0x1f;
- off = (stat & 0x7ff) << 7;
while (left) {
if (input->cbuf == idx)
@@ -525,6 +524,8 @@ static size_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
free = left;
ret = copy_to_user(buf, input->vbuf[input->cbuf] +
input->coff, free);
+ if (ret)
+ return -EFAULT;
input->coff += free;
if (input->coff == input->dma_buf_size) {
input->coff = 0;
@@ -939,6 +940,8 @@ static ssize_t ts_read(struct file *file, char *buf,
break;
}
read = ddb_input_read(input, buf, left);
+ if (read < 0)
+ return read;
left -= read;
buf += read;
}
@@ -1438,7 +1441,7 @@ static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ddb *dev = file->private_data;
void *parg = (void *)arg;
- int res = -EFAULT;
+ int res;
switch (cmd) {
case IOCTL_DDB_FLASHIO:
@@ -1447,29 +1450,29 @@ static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
u8 *rbuf, *wbuf;
if (copy_from_user(&fio, parg, sizeof(fio)))
- break;
- if (fio.write_len + fio.read_len > 1028) {
- printk(KERN_ERR "IOBUF too small\n");
- return -ENOMEM;
- }
+ return -EFAULT;
+
+ if (fio.write_len > 1028 || fio.read_len > 1028)
+ return -EINVAL;
+ if (fio.write_len + fio.read_len > 1028)
+ return -EINVAL;
+
wbuf = &dev->iobuf[0];
- if (!wbuf)
- return -ENOMEM;
rbuf = wbuf + fio.write_len;
- if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) {
- vfree(wbuf);
- break;
- }
- res = flashio(dev, wbuf, fio.write_len,
- rbuf, fio.read_len);
+
+ if (copy_from_user(wbuf, fio.write_buf, fio.write_len))
+ return -EFAULT;
+ res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len);
+ if (res)
+ return res;
if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
- res = -EFAULT;
+ return -EFAULT;
break;
}
default:
- break;
+ return -ENOTTY;
}
- return res;
+ return 0;
}
static const struct file_operations ddb_fops = {
diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile
index 8ac28b0546af..95a008b71fe5 100644
--- a/drivers/media/dvb/dm1105/Makefile
+++ b/drivers/media/dvb/dm1105/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_DM1105) += dm1105.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index efe9c30605e8..2c0acdb4d811 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -149,30 +149,25 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
dprintk ("%s\n", __func__);
- if (mutex_lock_interruptible (&events->mtx))
- return;
+ if ((status & FE_HAS_LOCK) && fe->ops.get_frontend)
+ fe->ops.get_frontend(fe, &fepriv->parameters_out);
- wp = (events->eventw + 1) % MAX_EVENT;
+ mutex_lock(&events->mtx);
+ wp = (events->eventw + 1) % MAX_EVENT;
if (wp == events->eventr) {
events->overflow = 1;
events->eventr = (events->eventr + 1) % MAX_EVENT;
}
e = &events->events[events->eventw];
-
- if (status & FE_HAS_LOCK)
- if (fe->ops.get_frontend)
- fe->ops.get_frontend(fe, &fepriv->parameters_out);
-
+ e->status = status;
e->parameters = fepriv->parameters_out;
events->eventw = wp;
mutex_unlock(&events->mtx);
- e->status = status;
-
wake_up_interruptible (&events->wait_queue);
}
@@ -207,19 +202,24 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
return ret;
}
- if (mutex_lock_interruptible (&events->mtx))
- return -ERESTARTSYS;
-
- memcpy (event, &events->events[events->eventr],
- sizeof(struct dvb_frontend_event));
-
+ mutex_lock(&events->mtx);
+ *event = events->events[events->eventr];
events->eventr = (events->eventr + 1) % MAX_EVENT;
-
mutex_unlock(&events->mtx);
return 0;
}
+static void dvb_frontend_clear_events(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ mutex_lock(&events->mtx);
+ events->eventr = events->eventw;
+ mutex_unlock(&events->mtx);
+}
+
static void dvb_frontend_init(struct dvb_frontend *fe)
{
dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
@@ -537,7 +537,6 @@ static int dvb_frontend_thread(void *data)
{
struct dvb_frontend *fe = data;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
- unsigned long timeout;
fe_status_t s;
enum dvbfe_algo algo;
@@ -558,7 +557,7 @@ static int dvb_frontend_thread(void *data)
while (1) {
up(&fepriv->sem); /* is locked when we enter the thread... */
restart:
- timeout = wait_event_interruptible_timeout(fepriv->wait_queue,
+ wait_event_interruptible_timeout(fepriv->wait_queue,
dvb_frontend_should_wakeup(fe) || kthread_should_stop()
|| freezing(current),
fepriv->delay);
@@ -577,12 +576,10 @@ restart:
if (fepriv->reinitialise) {
dvb_frontend_init(fe);
- if (fepriv->tone != -1) {
+ if (fe->ops.set_tone && fepriv->tone != -1)
fe->ops.set_tone(fe, fepriv->tone);
- }
- if (fepriv->voltage != -1) {
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
fe->ops.set_voltage(fe, fepriv->voltage);
- }
fepriv->reinitialise = 0;
}
@@ -1019,6 +1016,29 @@ static int is_legacy_delivery_system(fe_delivery_system_t s)
return 0;
}
+/* Initialize the cache with some default values derived from the
+ * legacy frontend_info structure.
+ */
+static void dtv_property_cache_init(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ break;
+ }
+}
+
/* Synchronise the legacy tuning parameters into the cache, so that demodulator
* drivers can use a single set_frontend tuning function, regardless of whether
* it's being used for the legacy or new API, reducing code and complexity.
@@ -1032,17 +1052,13 @@ static void dtv_property_cache_sync(struct dvb_frontend *fe,
switch (fe->ops.info.type) {
case FE_QPSK:
- c->modulation = QPSK; /* implied for DVB-S in legacy API */
- c->rolloff = ROLLOFF_35;/* implied for DVB-S */
c->symbol_rate = p->u.qpsk.symbol_rate;
c->fec_inner = p->u.qpsk.fec_inner;
- c->delivery_system = SYS_DVBS;
break;
case FE_QAM:
c->symbol_rate = p->u.qam.symbol_rate;
c->fec_inner = p->u.qam.fec_inner;
c->modulation = p->u.qam.modulation;
- c->delivery_system = SYS_DVBC_ANNEX_AC;
break;
case FE_OFDM:
if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
@@ -1060,7 +1076,6 @@ static void dtv_property_cache_sync(struct dvb_frontend *fe,
c->transmission_mode = p->u.ofdm.transmission_mode;
c->guard_interval = p->u.ofdm.guard_interval;
c->hierarchy = p->u.ofdm.hierarchy_information;
- c->delivery_system = SYS_DVBT;
break;
case FE_ATSC:
c->modulation = p->u.vsb.modulation;
@@ -1132,16 +1147,13 @@ static void dtv_property_adv_params_sync(struct dvb_frontend *fe)
p->frequency = c->frequency;
p->inversion = c->inversion;
- switch(c->modulation) {
- case PSK_8:
- case APSK_16:
- case APSK_32:
- case QPSK:
+ if (c->delivery_system == SYS_DSS ||
+ c->delivery_system == SYS_DVBS ||
+ c->delivery_system == SYS_DVBS2 ||
+ c->delivery_system == SYS_ISDBS ||
+ c->delivery_system == SYS_TURBO) {
p->u.qpsk.symbol_rate = c->symbol_rate;
p->u.qpsk.fec_inner = c->fec_inner;
- break;
- default:
- break;
}
/* Fake out a generic DVB-T request so we pass validation in the ioctl */
@@ -1824,9 +1836,17 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
memcpy (&fepriv->parameters_in, parg,
sizeof (struct dvb_frontend_parameters));
+ dtv_property_cache_init(fe, c);
dtv_property_cache_sync(fe, c, &fepriv->parameters_in);
}
+ /*
+ * Initialize output parameters to match the values given by
+ * the user. FE_SET_FRONTEND triggers an initial frontend event
+ * with status = 0, which copies output parameters to userspace.
+ */
+ fepriv->parameters_out = fepriv->parameters_in;
+
memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
memcpy(&fetunesettings.parameters, parg,
sizeof (struct dvb_frontend_parameters));
@@ -1884,8 +1904,9 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
/* Request the search algorithm to search */
fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
- dvb_frontend_wakeup(fe);
+ dvb_frontend_clear_events(fe);
dvb_frontend_add_event(fe, 0);
+ dvb_frontend_wakeup(fe);
fepriv->status = 0;
err = 0;
break;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index 5590eb6eb408..67bbfa728016 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -209,6 +209,7 @@ struct dvb_tuner_ops {
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency);
#define TUNER_STATUS_LOCKED 1
#define TUNER_STATUS_STEREO 2
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 5d73dec8ac07..58257165761e 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -258,6 +258,19 @@ config DVB_USB_AF9005_REMOTE
Say Y here to support the default remote control decoding for the
Afatech AF9005 based receiver.
+config DVB_USB_PCTV452E
+ tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600"
+ depends on DVB_USB
+ select TTPCI_EEPROM
+ select DVB_LNBP22 if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ help
+ Support for external USB adapter designed by Pinnacle,
+ shipped under the brand name 'PCTV HDTV Pro USB'.
+ Also supports TT Connect S2-3600/3650 cards.
+ Say Y if you own such a device and want to use it.
+
config DVB_USB_DW2102
tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
depends on DVB_USB
@@ -374,3 +387,18 @@ config DVB_USB_TECHNISAT_USB2
select DVB_STV6110x if !DVB_FE_CUSTOMISE
help
Say Y here to support the Technisat USB2 DVB-S/S2 device
+
+config DVB_USB_IT913X
+ tristate "it913x driver"
+ depends on DVB_USB
+ select DVB_IT913X_FE
+ help
+ Say Y here to support the it913x device
+
+config DVB_USB_MXL111SF
+ tristate "MxL111SF DTV USB2.0 support"
+ depends on DVB_USB
+ select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
+ select VIDEO_TVEEPROM
+ help
+ Say Y here to support the MxL111SF USB2.0 DTV receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 4bac13da0c39..26c8b9e57050 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -64,6 +64,9 @@ obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
dvb-usb-anysee-objs = anysee.o
obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
+dvb-usb-pctv452e-objs = pctv452e.o
+obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o
+
dvb-usb-dw2102-objs = dw2102.o
obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
@@ -94,7 +97,16 @@ obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
dvb-usb-technisat-usb2-objs = technisat-usb2.o
obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+dvb-usb-it913x-objs := it913x.o
+obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
+
+dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
+
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/common/tuners
+EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index b95a95e17840..2aef3c89e9fa 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -127,6 +127,8 @@ static struct dvb_usb_device_properties a800_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
@@ -147,7 +149,7 @@ static struct dvb_usb_device_properties a800_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
},
},
diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/dvb/dvb-usb/af9005-fe.c
index 6ad94745bbdd..3263e9749d09 100644
--- a/drivers/media/dvb/dvb-usb/af9005-fe.c
+++ b/drivers/media/dvb/dvb-usb/af9005-fe.c
@@ -63,11 +63,9 @@ static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi,
u16 reglo, u8 pos, u8 len, u16 value)
{
int ret;
- u8 temp;
if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff))))
return ret;
- temp = (u8) ((value & 0x0300) >> 8);
return af9005_write_register_bits(d, reghi, pos, len,
(u8) ((value & 0x300) >> 8));
}
diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c
index 0351c0e52dd2..bd51a764351b 100644
--- a/drivers/media/dvb/dvb-usb/af9005.c
+++ b/drivers/media/dvb/dvb-usb/af9005.c
@@ -815,7 +815,7 @@ static int af9005_frontend_attach(struct dvb_usb_adapter *adap)
debug_dump(buf, 8, printk);
}
}
- adap->fe = af9005_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = af9005_fe_attach(adap->dev);
return 0;
}
@@ -999,6 +999,8 @@ static struct dvb_usb_device_properties af9005_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps =
DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1018,6 +1020,7 @@ static struct dvb_usb_device_properties af9005_properties = {
}
}
},
+ }},
}
},
.power_ctrl = af9005_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index d7ad05fc383b..c6c275bac08e 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -744,29 +744,31 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
};
static const struct af9015_rc_setup af9015_rc_setup_usbids[] = {
- { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_RC,
+ { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_RC,
RC_MAP_TERRATEC_SLIM_2 },
- { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
+ { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
RC_MAP_TERRATEC_SLIM },
- { (USB_VID_VISIONPLUS << 16) + USB_PID_AZUREWAVE_AD_TU700,
+ { (USB_VID_VISIONPLUS << 16) | USB_PID_AZUREWAVE_AD_TU700,
RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_VISIONPLUS << 16) + USB_PID_TINYTWIN,
+ { (USB_VID_VISIONPLUS << 16) | USB_PID_TINYTWIN,
RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGI_VOX_MINI_III,
+ { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGI_VOX_MINI_III,
RC_MAP_MSI_DIGIVOX_III },
- { (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGIVOX_DUO,
+ { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGIVOX_DUO,
RC_MAP_MSI_DIGIVOX_III },
- { (USB_VID_LEADTEK << 16) + USB_PID_WINFAST_DTV_DONGLE_GOLD,
+ { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV_DONGLE_GOLD,
RC_MAP_LEADTEK_Y04G0051 },
- { (USB_VID_AVERMEDIA << 16) + USB_PID_AVERMEDIA_VOLAR_X,
+ { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV2000DS,
+ RC_MAP_LEADTEK_Y04G0051 },
+ { (USB_VID_AVERMEDIA << 16) | USB_PID_AVERMEDIA_VOLAR_X,
RC_MAP_AVERMEDIA_M135A },
- { (USB_VID_AFATECH << 16) + USB_PID_TREKSTOR_DVBT,
+ { (USB_VID_AFATECH << 16) | USB_PID_TREKSTOR_DVBT,
RC_MAP_TREKSTOR },
- { (USB_VID_KWORLD_2 << 16) + USB_PID_TINYTWIN_2,
+ { (USB_VID_KWORLD_2 << 16) | USB_PID_TINYTWIN_2,
RC_MAP_DIGITALNOW_TINYTWIN },
- { (USB_VID_GTEK << 16) + USB_PID_TINYTWIN_3,
+ { (USB_VID_GTEK << 16) | USB_PID_TINYTWIN_3,
RC_MAP_DIGITALNOW_TINYTWIN },
- { (USB_VID_KWORLD_2 << 16) + USB_PID_SVEON_STV22,
+ { (USB_VID_KWORLD_2 << 16) | USB_PID_SVEON_STV22,
RC_MAP_MSI_DIGIVOX_III },
{ }
};
@@ -859,13 +861,13 @@ static int af9015_read_config(struct usb_device *udev)
for (i = 0; i < af9015_properties_count; i++) {
/* USB1.1 set smaller buffersize and disable 2nd adapter */
if (udev->speed == USB_SPEED_FULL) {
- af9015_properties[i].adapter[0].stream.u.bulk.buffersize
+ af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
= TS_USB11_FRAME_SIZE;
/* disable 2nd adapter because we don't have
PID-filters */
af9015_config.dual_mode = 0;
} else {
- af9015_properties[i].adapter[0].stream.u.bulk.buffersize
+ af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
= TS_USB20_FRAME_SIZE;
}
}
@@ -1111,10 +1113,10 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
}
/* attach demodulator */
- adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+ adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
&adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static struct mt2060_config af9015_mt2060_config = {
@@ -1188,49 +1190,49 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
switch (af9015_af9013_config[adap->id].tuner) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_MT2060_2:
- ret = dvb_attach(mt2060_attach, adap->fe, &adap->dev->i2c_adap,
+ ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&af9015_mt2060_config,
af9015_config.mt2060_if1[adap->id])
== NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_QT1010:
case AF9013_TUNER_QT1010A:
- ret = dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
+ ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
- ret = dvb_attach(tda18271_attach, adap->fe, 0xc0,
+ ret = dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap,
&af9015_tda18271_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18218:
- ret = dvb_attach(tda18218_attach, adap->fe,
+ ret = dvb_attach(tda18218_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap,
&af9015_tda18218_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5003D:
- ret = dvb_attach(mxl5005s_attach, adap->fe,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap,
&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
- ret = dvb_attach(mxl5005s_attach, adap->fe,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap,
&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_ENV77H11D5:
- ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0,
+ ret = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap,
DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MC44S803:
- ret = dvb_attach(mc44s803_attach, adap->fe,
+ ret = dvb_attach(mc44s803_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap,
&af9015_mc44s803_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5007T:
- ret = dvb_attach(mxl5007t_attach, adap->fe,
+ ret = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap,
0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
break;
@@ -1304,6 +1306,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1319,8 +1323,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1335,6 +1342,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
@@ -1432,6 +1440,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1447,8 +1457,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1463,6 +1476,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
@@ -1549,6 +1563,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1564,8 +1580,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.count = 6,
.endpoint = 0x84,
},
+ }},
},
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach =
af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
@@ -1580,6 +1599,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 2cbf19a52e38..5f2278b73ee9 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -446,6 +446,114 @@ static struct isl6423_config anysee_isl6423_config = {
* IOE[5] STV0903 1=enabled
*/
+static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct anysee_state *state = adap->dev->priv;
+ int ret;
+
+ deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ /* no frontend sleep control */
+ if (onoff == 0)
+ return 0;
+
+ switch (state->hw) {
+ case ANYSEE_HW_507FA: /* 15 */
+ /* E30 Combo Plus */
+ /* E30 C Plus */
+
+ if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
+ /* E7 TC */
+ /* E7 PTC */
+
+ if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
+ 0x40);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ 0x40);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
+ 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ default:
+ ret = 0;
+ }
+
+error:
+ return ret;
+}
+
static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
@@ -466,41 +574,54 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
}
};
- /* Check which hardware we have.
- * We must do this call two times to get reliable values (hw bug).
- */
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
+ /* detect hardware only once */
+ if (adap->fe_adap[0].fe == NULL) {
+ /* Check which hardware we have.
+ * We must do this call two times to get reliable values (hw bug).
+ */
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ goto error;
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ goto error;
- /* Meaning of these info bytes are guessed. */
- info("firmware version:%d.%d hardware id:%d",
- hw_info[1], hw_info[2], hw_info[0]);
+ /* Meaning of these info bytes are guessed. */
+ info("firmware version:%d.%d hardware id:%d",
+ hw_info[1], hw_info[2], hw_info[0]);
- state->hw = hw_info[0];
+ state->hw = hw_info[0];
+ }
+
+ /* set current frondend ID for devices having two frondends */
+ if (adap->fe_adap[0].fe)
+ state->fe_id++;
switch (state->hw) {
case ANYSEE_HW_507T: /* 2 */
/* E30 */
+ if (state->fe_id)
+ break;
+
/* attach demod */
- adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config,
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &anysee_mt352_config,
&adap->dev->i2c_adap);
- if (adap->fe)
+ if (adap->fe_adap[0].fe)
break;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
&adap->dev->i2c_adap);
break;
case ANYSEE_HW_507CD: /* 6 */
/* E30 Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-T demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
@@ -512,33 +633,39 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &anysee_zl10353_config, &adap->dev->i2c_adap);
break;
case ANYSEE_HW_507DC: /* 10 */
/* E30 C Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-C demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
+ &anysee_tda10023_config, &adap->dev->i2c_adap, 0x48);
break;
case ANYSEE_HW_507SI: /* 11 */
/* E30 S2 Plus */
+ if (state->fe_id)
+ break;
+
/* enable DVB-S/S2 demod on IOD[0] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(cx24116_attach, &anysee_cx24116_config,
+ adap->fe_adap[0].fe = dvb_attach(cx24116_attach, &anysee_cx24116_config,
&adap->dev->i2c_adap);
break;
@@ -564,55 +691,59 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
if (ret)
goto error;
- if (dvb_usb_anysee_delsys) {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
+ if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
+ 0x01);
if (ret)
goto error;
- /* enable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
- 0x01);
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
if (ret)
goto error;
/* attach demod */
if (tmp == 0xc7) {
/* TDA18212 config */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_tda18212_config2,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &adap->dev->i2c_adap, 0x48);
} else {
/* PLL config */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ tda10023_attach,
+ &anysee_tda10023_config,
+ &adap->dev->i2c_adap, 0x48);
}
} else {
- /* disable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
- 0x01);
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
+ 0x20);
if (ret)
goto error;
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
+ 0x01);
if (ret)
goto error;
/* attach demod */
if (tmp == 0xc7) {
/* TDA18212 config */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ zl10353_attach,
+ &anysee_zl10353_tda18212_config2,
+ &adap->dev->i2c_adap);
} else {
/* PLL config */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(
+ zl10353_attach,
+ &anysee_zl10353_config,
+ &adap->dev->i2c_adap);
}
}
@@ -627,52 +758,40 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
if (ret)
goto error;
- if (dvb_usb_anysee_delsys) {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
0x40);
if (ret)
goto error;
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ 0x20);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_tda18212_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &adap->dev->i2c_adap, 0x48);
} else {
- /* disable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
0x20);
if (ret)
goto error;
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
+ 0x40);
if (ret)
goto error;
/* attach demod */
- adap->fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
+ adap->fe_adap[state->fe_id].fe = dvb_attach(zl10353_attach,
+ &anysee_zl10353_tda18212_config,
+ &adap->dev->i2c_adap);
}
break;
@@ -681,6 +800,9 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
/* E7 S2 */
/* E7 PS2 */
+ if (state->fe_id)
+ break;
+
/* enable transport stream on IOA[7] */
ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80);
if (ret)
@@ -692,13 +814,13 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach demod */
- adap->fe = dvb_attach(stv0900_attach, &anysee_stv0900_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0900_attach, &anysee_stv0900_config,
&adap->dev->i2c_adap, 0);
break;
}
- if (!adap->fe) {
+ if (!adap->fe_adap[0].fe) {
/* we have no frontend :-( */
ret = -ENODEV;
err("Unsupported Anysee version. " \
@@ -713,14 +835,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
struct anysee_state *state = adap->dev->priv;
struct dvb_frontend *fe;
int ret;
- deb_info("%s:\n", __func__);
+ deb_info("%s: fe=%d\n", __func__, state->fe_id);
switch (state->hw) {
case ANYSEE_HW_507T: /* 2 */
/* E30 */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1),
NULL, DVB_PLL_THOMSON_DTT7579);
break;
@@ -728,7 +850,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 Plus */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1),
&adap->dev->i2c_adap, DVB_PLL_THOMSON_DTT7579);
break;
@@ -736,7 +858,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 C Plus */
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1),
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc0 >> 1),
&adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
break;
@@ -744,28 +866,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E30 S2 Plus */
/* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_isl6423_config);
+ fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &anysee_isl6423_config);
break;
case ANYSEE_HW_507FA: /* 15 */
/* E30 Combo Plus */
/* E30 C Plus */
- if (dvb_usb_anysee_delsys) {
- /* enable DVB-T tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
- if (ret)
- goto error;
- } else {
- /* enable DVB-C tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
- if (ret)
- goto error;
- }
-
/* Try first attach TDA18212 silicon tuner on IOE[4], if that
* fails attach old simple PLL. */
@@ -775,8 +883,8 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_tda18212_config);
+ fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
+ &adap->dev->i2c_adap, &anysee_tda18212_config);
if (fe)
break;
@@ -786,8 +894,9 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1),
- &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+ fe = dvb_attach(dvb_pll_attach, adap->fe_adap[state->fe_id].fe,
+ (0xc0 >> 1), &adap->dev->i2c_adap,
+ DVB_PLL_SAMSUNG_DTOS403IH102A);
break;
case ANYSEE_HW_508TC: /* 18 */
@@ -801,8 +910,8 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
goto error;
/* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap,
- &anysee_tda18212_config);
+ fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
+ &adap->dev->i2c_adap, &anysee_tda18212_config);
break;
case ANYSEE_HW_508S2: /* 19 */
@@ -811,12 +920,12 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
/* E7 PS2 */
/* attach tuner */
- fe = dvb_attach(stv6110_attach, adap->fe,
+ fe = dvb_attach(stv6110_attach, adap->fe_adap[0].fe,
&anysee_stv6110_config, &adap->dev->i2c_adap);
if (fe) {
/* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe,
+ fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &anysee_isl6423_config);
}
@@ -918,6 +1027,23 @@ static struct dvb_usb_device_properties anysee_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 2,
+ .frontend_ctrl = anysee_frontend_ctrl,
+ .fe = {{
+ .streaming_ctrl = anysee_streaming_ctrl,
+ .frontend_attach = anysee_frontend_attach,
+ .tuner_attach = anysee_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = (16*512),
+ }
+ }
+ },
+ }, {
.streaming_ctrl = anysee_streaming_ctrl,
.frontend_attach = anysee_frontend_attach,
.tuner_attach = anysee_tuner_attach,
@@ -931,6 +1057,7 @@ static struct dvb_usb_device_properties anysee_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
index ad6ccd1ea2d9..57ee500b8c0e 100644
--- a/drivers/media/dvb/dvb-usb/anysee.h
+++ b/drivers/media/dvb/dvb-usb/anysee.h
@@ -59,6 +59,7 @@ enum cmd {
struct anysee_state {
u8 hw; /* PCB ID */
u8 seq;
+ u8 fe_id:1; /* frondend ID */
};
#define ANYSEE_HW_507T 2 /* E30 */
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 2351077ff2b3..b77994967b9b 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -140,9 +140,9 @@ static struct zl10353_config au6610_zl10353_config = {
static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
@@ -155,7 +155,7 @@ static struct qt1010_config au6610_qt1010_config = {
static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&au6610_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -204,6 +204,8 @@ static struct dvb_usb_device_properties au6610_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = au6610_zl10353_frontend_attach,
.tuner_attach = au6610_qt1010_tuner_attach,
@@ -219,6 +221,7 @@ static struct dvb_usb_device_properties au6610_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c
index 57e2444d51ab..bf67b4dfd82b 100644
--- a/drivers/media/dvb/dvb-usb/az6027.c
+++ b/drivers/media/dvb/dvb-usb/az6027.c
@@ -40,7 +40,6 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x99 },
{ STB0899_DISF22RX , 0xa8 },
@@ -782,7 +781,6 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
u8 buf;
- int ret;
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct i2c_msg i2c_msg = {
@@ -800,17 +798,17 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
switch (voltage) {
case SEC_VOLTAGE_13:
buf = 1;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
case SEC_VOLTAGE_18:
buf = 2;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
case SEC_VOLTAGE_OFF:
buf = 0;
- ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
break;
default:
@@ -910,16 +908,16 @@ static int az6027_frontend_attach(struct dvb_usb_adapter *adap)
az6027_frontend_reset(adap);
deb_info("adap = %p, dev = %p\n", adap, adap->dev);
- adap->fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address);
- if (stb6100_attach(adap->fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) {
+ if (stb6100_attach(adap->fe_adap[0].fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) {
deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address);
- adap->fe->ops.set_voltage = az6027_set_voltage;
+ adap->fe_adap[0].fe->ops.set_voltage = az6027_set_voltage;
az6027_ci_init(adap);
} else {
- adap->fe = NULL;
+ adap->fe_adap[0].fe = NULL;
}
} else
warn("no front-end attached\n");
@@ -954,7 +952,6 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0, j = 0, len = 0;
- int ret;
u16 index;
u16 value;
int length;
@@ -990,7 +987,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff));
value = msg[i].addr + (msg[i].len << 8);
length = msg[i + 1].len + 6;
- ret = az6027_usb_in_op(d, req, value, index, data, length);
+ az6027_usb_in_op(d, req, value, index, data, length);
len = msg[i + 1].len;
for (j = 0; j < len; j++)
msg[i + 1].buf[j] = data[j + 5];
@@ -1017,7 +1014,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n
index = 0x0;
value = msg[i].addr;
length = msg[i].len + 6;
- ret = az6027_usb_in_op(d, req, value, index, data, length);
+ az6027_usb_in_op(d, req, value, index, data, length);
len = msg[i].len;
for (j = 0; j < len; j++)
msg[i].buf[j] = data[j + 5];
@@ -1106,6 +1103,8 @@ static struct dvb_usb_device_properties az6027_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = az6027_streaming_ctrl,
.frontend_attach = az6027_frontend_attach,
@@ -1120,6 +1119,7 @@ static struct dvb_usb_device_properties az6027_properties = {
}
}
},
+ }},
}
},
/*
diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c
index 6d1a3041540d..57afb5a9157e 100644
--- a/drivers/media/dvb/dvb-usb/ce6230.c
+++ b/drivers/media/dvb/dvb-usb/ce6230.c
@@ -186,9 +186,9 @@ static struct zl10353_config ce6230_zl10353_config = {
static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- adap->fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
}
@@ -214,7 +214,7 @@ static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
int ret;
deb_info("%s:\n", __func__);
- ret = dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+ ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
return ret;
}
@@ -273,6 +273,8 @@ static struct dvb_usb_device_properties ce6230_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = ce6230_zl10353_frontend_attach,
.tuner_attach = ce6230_mxl5003s_tuner_attach,
.stream = {
@@ -285,6 +287,7 @@ static struct dvb_usb_device_properties ce6230_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
index 16f2ce2bc15a..f9d905002ec9 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
@@ -69,7 +69,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
char state[3];
int ret;
- adap->fe = cinergyt2_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev);
ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
sizeof(state), 0);
@@ -198,6 +198,8 @@ static struct dvb_usb_device_properties cinergyt2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cinergyt2_streaming_ctrl,
.frontend_attach = cinergyt2_frontend_attach,
@@ -212,6 +214,7 @@ static struct dvb_usb_device_properties cinergyt2_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index acb5fb2d2e73..9f2a02c48377 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -347,7 +347,7 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d)
static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
{
- struct usb_data_stream_properties *p = &d->props.adapter[0].stream;
+ struct usb_data_stream_properties *p = &d->props.adapter[0].fe[0].stream;
const int timeout = 100;
const int junk_len = p->u.bulk.buffersize;
u8 *junk;
@@ -725,7 +725,7 @@ static struct max2165_config mygica_d689_max2165_cfg = {
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61,
TUNER_PHILIPS_FMD1216ME_MK3);
return 0;
@@ -733,27 +733,27 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61,
NULL, DVB_PLL_THOMSON_DTT7579);
return 0;
}
static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_LG_Z201);
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201);
return 0;
}
static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
NULL, DVB_PLL_THOMSON_DTT7579);
return 0;
}
static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF);
return 0;
}
@@ -795,9 +795,9 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
};
/* FIXME: generalize & move to common area */
- adap->fe->callback = dvico_bluebird_xc2028_callback;
+ adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback;
- fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
+ fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg);
if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
return -EIO;
@@ -808,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(mxl5005s_attach, adap->fe,
+ dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &aver_a868r_tuner);
return 0;
}
@@ -816,7 +816,7 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_frontend *fe;
- fe = dvb_attach(mxl5005s_attach, adap->fe,
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &d680_dmb_tuner);
return (fe == NULL) ? -EIO : 0;
}
@@ -824,7 +824,7 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_frontend *fe;
- fe = dvb_attach(max2165_attach, adap->fe,
+ fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, &mygica_d689_max2165_cfg);
return (fe == NULL) ? -EIO : 0;
}
@@ -837,8 +837,9 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
- if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -851,8 +852,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach,
+ &cxusb_lgdt3303_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -860,9 +863,9 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
+ adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
&adap->dev->i2c_adap);
- if (adap->fe != NULL)
+ if (adap->fe_adap[0].fe != NULL)
return 0;
return -EIO;
@@ -876,8 +879,9 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -890,11 +894,15 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
- if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
- &adap->dev->i2c_adap)) != NULL) ||
- ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_dee1601_config,
- &adap->dev->i2c_adap)) != NULL))
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
+ return 0;
+
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_dee1601_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -917,9 +925,11 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
- if ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_xc3028_config_no_i2c_gate,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe =
+ dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config_no_i2c_gate,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
/* try to determine if there is no IR decoder on the I2C bus */
@@ -1031,9 +1041,9 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&cxusb_dualdig4_rev2_config);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1084,15 +1094,15 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c =
- dib7000p_get_i2c_master(adap->fe,
+ dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib7070p_dib0070_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override;
return 0;
}
@@ -1108,14 +1118,16 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
- if ((adap->fe = dvb_attach(zl10353_attach,
- &cxusb_zl10353_xc3028_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
- if ((adap->fe = dvb_attach(mt352_attach,
- &cxusb_mt352_xc3028_config,
- &adap->dev->i2c_adap)) != NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach,
+ &cxusb_mt352_xc3028_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL)
return 0;
return -EIO;
@@ -1150,7 +1162,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
usb_clear_halt(d->udev,
usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
/* Drain USB pipes to avoid hang after reboot */
for (n = 0; n < 5; n++) {
@@ -1172,8 +1184,8 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1207,7 +1219,7 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
usb_clear_halt(d->udev,
usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
/* Reset the tuner */
@@ -1223,9 +1235,9 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
+ adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
&d->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -1383,6 +1395,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_cx22702_frontend_attach,
.tuner_attach = cxusb_fmd1216me_tuner_attach,
@@ -1397,7 +1411,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
}
}
},
-
+ }},
},
},
.power_ctrl = cxusb_power_ctrl,
@@ -1429,6 +1443,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_lgdt3303_frontend_attach,
.tuner_attach = cxusb_lgh064f_tuner_attach,
@@ -1444,6 +1460,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
}
}
},
+ }},
},
},
@@ -1483,6 +1500,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dee1601_frontend_attach,
.tuner_attach = cxusb_dee1601_tuner_attach,
@@ -1497,6 +1516,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
}
}
},
+ }},
},
},
@@ -1544,6 +1564,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_lgz201_tuner_attach,
@@ -1559,6 +1581,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
}
}
},
+ }},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -1596,6 +1619,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_dtt7579_tuner_attach,
@@ -1611,6 +1636,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
}
}
},
+ }},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -1645,6 +1671,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dualdig4_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1659,6 +1687,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
}
}
},
+ }},
},
},
@@ -1695,6 +1724,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_nano2_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1709,6 +1740,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
}
}
},
+ }},
},
},
@@ -1747,6 +1779,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_nano2_frontend_attach,
.tuner_attach = cxusb_dvico_xc3028_tuner_attach,
@@ -1761,6 +1795,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
}
}
},
+ }},
},
},
@@ -1796,6 +1831,8 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_aver_streaming_ctrl,
.frontend_attach = cxusb_aver_lgdt3303_frontend_attach,
.tuner_attach = cxusb_mxl5003s_tuner_attach,
@@ -1810,7 +1847,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
}
}
},
-
+ }},
},
},
.power_ctrl = cxusb_aver_power_ctrl,
@@ -1839,10 +1876,12 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .size_of_priv = sizeof(struct dib0700_adapter_state),
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dualdig4_rev2_frontend_attach,
.tuner_attach = cxusb_dualdig4_rev2_tuner_attach,
- .size_of_priv = sizeof(struct dib0700_adapter_state),
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
@@ -1854,6 +1893,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
}
}
},
+ }},
},
},
@@ -1889,6 +1929,8 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
.frontend_attach = cxusb_d680_dmb_frontend_attach,
.tuner_attach = cxusb_d680_dmb_tuner_attach,
@@ -1904,6 +1946,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
}
}
},
+ }},
},
},
@@ -1940,6 +1983,8 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
.frontend_attach = cxusb_mygica_d689_frontend_attach,
.tuner_attach = cxusb_mygica_d689_tuner_attach,
@@ -1955,6 +2000,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
}
}
},
+ }},
},
},
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 5eb91b4f8fd0..156cbfc9c79d 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -30,6 +30,11 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
struct dib0700_state *st = d->priv;
int ret;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
REQUEST_GET_VERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
@@ -46,6 +51,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
if (fwtype != NULL)
*fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
(st->buf[14] << 8) | st->buf[15];
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -108,7 +114,12 @@ int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen
int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
{
struct dib0700_state *st = d->priv;
- s16 ret;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_SET_GPIO;
st->buf[1] = gpio;
@@ -116,6 +127,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -125,6 +137,11 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
int ret;
if (st->fw_version >= 0x10201) {
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_USB_XFER_LEN;
st->buf[1] = (nb_ts_packets >> 8) & 0xff;
st->buf[2] = nb_ts_packets & 0xff;
@@ -132,6 +149,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
} else {
deb_info("this firmware does not allow to change the USB xfer len\n");
ret = -EIO;
@@ -208,6 +226,10 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
} else {
/* Write request */
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_NEW_I2C_WRITE;
st->buf[1] = msg[i].addr << 1;
st->buf[2] = (en_start << 7) | (en_stop << 6) |
@@ -227,6 +249,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0, st->buf, msg[i].len + 4,
USB_CTRL_GET_TIMEOUT);
+ mutex_unlock(&d->usb_mutex);
if (result < 0) {
deb_info("i2c write error (status = %d)\n", result);
break;
@@ -249,6 +272,10 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
for (i = 0; i < num; i++) {
/* fill in the address */
@@ -279,6 +306,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
break;
}
}
+ mutex_unlock(&d->usb_mutex);
mutex_unlock(&d->i2c_mutex);
return i;
@@ -337,7 +365,12 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
{
struct dib0700_state *st = d->priv;
- s16 ret;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
st->buf[0] = REQUEST_SET_CLOCK;
st->buf[1] = (en_pll << 7) | (pll_src << 6) |
@@ -352,6 +385,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
st->buf[9] = dsuScaler & 0xff; /* LSB */
ret = dib0700_ctrl_wr(d, st->buf, 10);
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -360,10 +394,16 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
{
struct dib0700_state *st = d->priv;
u16 divider;
+ int ret;
if (scl_kHz == 0)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_I2C_PARAM;
divider = (u16) (30000 / scl_kHz);
st->buf[1] = 0;
@@ -379,7 +419,11 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
(st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
- return dib0700_ctrl_wr(d, st->buf, 8);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 8);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
}
@@ -484,13 +528,13 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
adap_num++) {
if (fw_version >= 0x10201) {
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
} else {
/* for fw version older than 1.20.1,
* the buffersize has to be n times 512 */
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
- if (dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize < 512)
- dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 512;
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
+ if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
}
}
}
@@ -515,6 +559,11 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
}
}
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_ENABLE_VIDEO;
/* this bit gives a kind of command,
* rather than enabling something or not */
@@ -530,25 +579,28 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
st->channel_state &= ~0x3;
- if ((adap->stream.props.endpoint != 2)
- && (adap->stream.props.endpoint != 3)) {
- deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint);
+ if ((adap->fe_adap[0].stream.props.endpoint != 2)
+ && (adap->fe_adap[0].stream.props.endpoint != 3)) {
+ deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint);
if (onoff)
st->channel_state |= 1 << (adap->id);
else
st->channel_state |= 1 << ~(adap->id);
} else {
if (onoff)
- st->channel_state |= 1 << (adap->stream.props.endpoint-2);
+ st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2);
else
- st->channel_state |= 1 << (3-adap->stream.props.endpoint);
+ st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint);
}
st->buf[2] |= st->channel_state;
deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
- return dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
}
int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
@@ -557,6 +609,11 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
struct dib0700_state *st = d->priv;
int new_proto, ret;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return 0;
+ }
+
st->buf[0] = REQUEST_SET_RC;
st->buf[1] = 0;
st->buf[2] = 0;
@@ -567,23 +624,29 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
else if (rc_type == RC_TYPE_NEC)
new_proto = 0;
else if (rc_type == RC_TYPE_RC6) {
- if (st->fw_version < 0x10200)
- return -EINVAL;
+ if (st->fw_version < 0x10200) {
+ ret = -EINVAL;
+ goto out;
+ }
new_proto = 2;
- } else
- return -EINVAL;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
st->buf[1] = new_proto;
ret = dib0700_ctrl_wr(d, st->buf, 3);
if (ret < 0) {
err("ir protocol setup failed");
- return ret;
+ goto out;
}
d->props.rc.core.protocol = rc_type;
+out:
+ mutex_unlock(&d->usb_mutex);
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index d0ea5b64f6b4..f313182eb9d5 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -101,7 +101,7 @@ static int bristol_frontend_attach(struct dvb_usb_adapter *adap)
}
}
st->mt2060_if1[adap->id] = 1220;
- return (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap,
+ return (adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap,
(10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0;
}
@@ -118,15 +118,16 @@ static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval)
static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
- struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
+ struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1);
s8 a;
int if1=1220;
if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) &&
adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) {
if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a;
}
- return dvb_attach(mt2060_attach,adap->fe, tun_i2c,&bristol_mt2060_config[adap->id],
- if1) == NULL ? -ENODEV : 0;
+ return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c,
+ &bristol_mt2060_config[adap->id], if1) == NULL ?
+ -ENODEV : 0;
}
/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */
@@ -279,10 +280,12 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap)
}
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
- &stk7700d_dib7000p_mt2266_config[adap->id]);
+ adap->fe_adap[0].fe =
+ dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,
+ 0x80 + (adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
@@ -306,17 +309,19 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
}
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
- &stk7700d_dib7000p_mt2266_config[adap->id]);
+ adap->fe_adap[0].fe =
+ dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,
+ 0x80 + (adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *tun_i2c;
- tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- return dvb_attach(mt2266_attach, adap->fe, tun_i2c,
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c,
&stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;
}
@@ -396,8 +401,8 @@ static int stk7700ph_xc3028_callback(void *ptr, int component,
switch (command) {
case XC2028_TUNER_RESET:
/* Send the tuner in then out of reset */
- dib7000p_set_gpio(adap->fe, 8, 0, 0); msleep(10);
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
break;
case XC2028_RESET_CLK:
break;
@@ -447,25 +452,25 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&stk7700ph_dib7700_xc3028_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *tun_i2c;
- tun_i2c = dib7000p_get_i2c_master(adap->fe,
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
stk7700ph_xc3028_config.i2c_adap = tun_i2c;
/* FIXME: generalize & move to common area */
- adap->fe->callback = stk7700ph_xc3028_callback;
+ adap->fe_adap[0].fe->callback = stk7700ph_xc3028_callback;
- return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config)
+ return dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &stk7700ph_xc3028_config)
== NULL ? -ENODEV : 0;
}
@@ -685,12 +690,12 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap)
st->mt2060_if1[0] = 1220;
if (dib7000pc_detection(&adap->dev->i2c_adap)) {
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
st->is_dib7000pc = 1;
} else
- adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static struct mt2060_config stk7700p_mt2060_config = {
@@ -709,11 +714,11 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a;
}
if (st->is_dib7000pc)
- tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
else
- tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
+ return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700p_mt2060_config,
if1) == NULL ? -ENODEV : 0;
}
@@ -843,33 +848,33 @@ static int dib7770_set_param_override(struct dvb_frontend *fe,
static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe,
+ struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
- &dib7770p_dib0070_config) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
+ &dib7770p_dib0070_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override;
return 0;
}
static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
if (adap->id == 0) {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL)
return -ENODEV;
} else {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL)
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL)
return -ENODEV;
}
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override;
return 0;
}
@@ -878,26 +883,26 @@ static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index,
{
struct dib0700_state *st = adapter->dev->priv;
if (st->is_dib7000pc)
- return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
- return dib7000m_pid_filter(adapter->fe, index, pid, onoff);
+ return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
+ return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
struct dib0700_state *st = adapter->dev->priv;
if (st->is_dib7000pc)
- return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
- return dib7000m_pid_filter_ctrl(adapter->fe, onoff);
+ return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
+ return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
{
- return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
+ return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
- return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
+ return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
@@ -955,9 +960,9 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&dib7070p_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK7770P */
@@ -1007,9 +1012,9 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
&dib7770p_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* DIB807x generic */
@@ -1225,34 +1230,34 @@ static int dib807x_set_param_override(struct dvb_frontend *fe,
static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe,
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
if (adap->id == 0) {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib807x_dib0070_config[0]) == NULL)
return -ENODEV;
} else {
- if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
&dib807x_dib0070_config[1]) == NULL)
return -ENODEV;
}
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib807x_set_param_override;
return 0;
}
static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index,
u16 pid, int onoff)
{
- return dib8000_pid_filter(adapter->fe, index, pid, onoff);
+ return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter,
int onoff)
{
- return dib8000_pid_filter_ctrl(adapter->fe, onoff);
+ return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
/* STK807x */
@@ -1276,10 +1281,10 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK807xPVR */
@@ -1305,10 +1310,10 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap)
/* initialize IC 0 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
@@ -1316,10 +1321,10 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
/* initialize IC 1 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
&dib807x_dib8000_config[1]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* STK8096GP */
@@ -1546,13 +1551,13 @@ static int dib8096_set_param_override(struct dvb_frontend *fe,
static int dib809x_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override;
return 0;
}
@@ -1575,30 +1580,30 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c;
- struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe, 1);
+ struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1);
if (fe_slave) {
tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1);
if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- fe_slave->dvb = adap->fe->dvb;
+ fe_slave->dvb = adap->fe_adap[0].fe->dvb;
fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override;
}
- tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override;
return 0;
}
@@ -1626,12 +1631,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]);
- dib8000_set_slave_frontend(adap->fe, fe_slave);
+ dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave);
return fe_slave == NULL ? -ENODEV : 0;
}
@@ -1639,12 +1644,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap)
/* STK9090M */
static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
{
- return dib9000_fw_pid_filter(adapter->fe, index, pid, onoff);
+ return dib9000_fw_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff);
}
static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
{
- return dib9000_fw_pid_filter_ctrl(adapter->fe, onoff);
+ return dib9000_fw_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff);
}
static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff)
@@ -1856,15 +1861,15 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap)
stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size;
stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data;
- adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
+ adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *state = adap->priv;
- struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe);
+ struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe);
u16 data_dib190[10] = {
1, 0x1374,
2, 0x01a2,
@@ -1873,13 +1878,13 @@ static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
8, 0x0486,
};
- if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &dib9090_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &dib9090_dib0090_config) == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0)
return -ENODEV;
dib0700_set_i2c_speed(adap->dev, 2000);
- if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0)
return -ENODEV;
release_firmware(state->frontend_firmware);
return 0;
@@ -1925,16 +1930,16 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap)
nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data;
dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80);
- adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
+ adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82);
fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]);
- dib9000_set_slave_frontend(adap->fe, fe_slave);
+ dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave);
return fe_slave == NULL ? -ENODEV : 0;
}
@@ -1951,26 +1956,26 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap)
0, 0x00ef,
8, 0x0406,
};
- i2c = dib9000_get_tuner_interface(adap->fe);
- if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &nim9090md_dib0090_config[0]) == NULL)
+ i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe);
+ if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &nim9090md_dib0090_config[0]) == NULL)
return -ENODEV;
- i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0)
return -ENODEV;
dib0700_set_i2c_speed(adap->dev, 2000);
- if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0)
return -ENODEV;
- fe_slave = dib9000_get_slave_frontend(adap->fe, 1);
+ fe_slave = dib9000_get_slave_frontend(adap->fe_adap[0].fe, 1);
if (fe_slave != NULL) {
- i2c = dib9000_get_component_bus_interface(adap->fe);
+ i2c = dib9000_get_component_bus_interface(adap->fe_adap[0].fe);
dib9000_set_i2c_adapter(fe_slave, i2c);
i2c = dib9000_get_tuner_interface(fe_slave);
if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL)
return -ENODEV;
- fe_slave->dvb = adap->fe->dvb;
- dib9000_fw_set_component_bus_speed(adap->fe, 2000);
+ fe_slave->dvb = adap->fe_adap[0].fe->dvb;
+ dib9000_fw_set_component_bus_speed(adap->fe_adap[0].fe, 2000);
if (dib9000_firmware_post_pll_init(fe_slave) < 0)
return -ENODEV;
}
@@ -2393,23 +2398,23 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap)
err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int nim7090_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &nim7090_dib0090_config) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
@@ -2439,11 +2444,11 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap)
}
dib0700_set_i2c_speed(adap->dev, 340);
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
- dib7090_slave_reset(adap->fe);
+ dib7090_slave_reset(adap->fe_adap[0].fe);
return 0;
}
@@ -2452,50 +2457,50 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap)
{
struct i2c_adapter *i2c;
- if (adap->dev->adapter[0].fe == NULL) {
+ if (adap->dev->adapter[0].fe_adap[0].fe == NULL) {
err("the master dib7090 has to be initialized first");
return -ENODEV; /* the master device has not been initialized */
}
- i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1);
+ i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1);
if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) {
err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]);
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]);
dib0700_set_i2c_speed(adap->dev, 200);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL)
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL)
return -ENODEV;
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup;
return 0;
}
@@ -2555,14 +2560,14 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]);
- return adap->fe == NULL ? -ENODEV : 0;
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]);
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]);
- return adap->fe == NULL ? -ENODEV : 0;
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]);
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* S5H1411 */
@@ -2617,9 +2622,9 @@ static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap)
dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
/* GPIOs are initialized, do the attach */
- adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
+ adap->fe_adap[0].fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
&adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int dib0700_xc5000_tuner_callback(void *priv, int component,
@@ -2649,9 +2654,9 @@ static struct xc5000_config s5h1411_xc5000_tunerconfig = {
static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
{
/* FIXME: generalize & move to common area */
- adap->fe->callback = dib0700_xc5000_tuner_callback;
+ adap->fe_adap[0].fe->callback = dib0700_xc5000_tuner_callback;
- return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap,
+ return dvb_attach(xc5000_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&s5h1411_xc5000_tunerconfig)
== NULL ? -ENODEV : 0;
}
@@ -2663,9 +2668,9 @@ static int dib0700_xc4000_tuner_callback(void *priv, int component,
if (command == XC4000_TUNER_RESET) {
/* Reset the tuner */
- dib7000p_set_gpio(adap->fe, 8, 0, 0);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0);
msleep(10);
- dib7000p_set_gpio(adap->fe, 8, 0, 1);
+ dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1);
} else {
err("xc4000: unknown tuner callback command: %d\n", command);
return -EINVAL;
@@ -2771,11 +2776,11 @@ static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap)
return -ENODEV;
}
- adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12,
+ adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12,
&pctv_340e_config);
st->is_dib7000pc = 1;
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static struct xc4000_config dib7000p_xc4000_tunerconfig = {
@@ -2791,7 +2796,7 @@ static int xc4000_tuner_attach(struct dvb_usb_adapter *adap)
struct i2c_adapter *tun_i2c;
/* The xc4000 is not on the main i2c bus */
- tun_i2c = dib7000p_get_i2c_master(adap->fe,
+ tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
if (tun_i2c == NULL) {
printk(KERN_ERR "Could not reach tuner i2c bus\n");
@@ -2799,9 +2804,9 @@ static int xc4000_tuner_attach(struct dvb_usb_adapter *adap)
}
/* Setup the reset callback */
- adap->fe->callback = dib0700_xc4000_tuner_callback;
+ adap->fe_adap[0].fe->callback = dib0700_xc4000_tuner_callback;
- return dvb_attach(xc4000_attach, adap->fe, tun_i2c,
+ return dvb_attach(xc4000_attach, adap->fe_adap[0].fe, tun_i2c,
&dib7000p_xc4000_tunerconfig)
== NULL ? -ENODEV : 0;
}
@@ -2857,16 +2862,16 @@ static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
msleep(30);
- adap->fe = dvb_attach(lgdt3305_attach,
+ adap->fe_adap[0].fe = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
&adap->dev->i2c_adap);
- return adap->fe == NULL ? -ENODEV : 0;
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap)
{
- return dvb_attach(mxl5007t_attach, adap->fe,
+ return dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x60,
&hcw_mxl5007t_config) == NULL ? -ENODEV : 0;
}
@@ -2989,6 +2994,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk7700p_pid_filter,
@@ -2997,6 +3004,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
},
},
@@ -3050,15 +3058,21 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
}, {
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+ }},
}
},
@@ -3084,6 +3098,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3092,7 +3108,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3101,6 +3120,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+ }},
}
},
@@ -3143,6 +3163,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3151,6 +3173,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ }},
},
},
@@ -3185,6 +3208,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3193,7 +3218,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -3261,6 +3286,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3269,7 +3296,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -3305,6 +3332,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3313,9 +3342,11 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3324,7 +3355,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -3373,6 +3404,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3381,9 +3414,11 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}, {
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3392,7 +3427,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -3420,6 +3455,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3428,7 +3465,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = stk7700ph_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3488,11 +3525,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = s5h1411_frontend_attach,
.tuner_attach = xc5000_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3524,11 +3563,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = lgdt3305_frontend_attach,
.tuner_attach = mxl5007t_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
@@ -3550,6 +3591,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk70x0p_pid_filter,
@@ -3558,7 +3601,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib7770p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3600,6 +3643,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3608,7 +3653,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3644,6 +3689,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3652,11 +3699,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.pid_filter = stk80xx_pid_filter,
@@ -3665,7 +3714,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3693,6 +3742,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3702,7 +3753,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib809x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3730,6 +3781,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3739,7 +3792,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = dib9090_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3767,6 +3820,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3776,7 +3831,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim8096md_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3804,6 +3859,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3813,7 +3870,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim9090md_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3841,6 +3898,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3850,7 +3909,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = nim7090_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3878,6 +3937,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3887,11 +3948,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = tfe7090pvr_tuner0_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -3901,7 +3964,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.tuner_attach = tfe7090pvr_tuner1_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
@@ -3929,11 +3992,13 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = pctv340e_frontend_attach,
.tuner_attach = xc4000_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
-
+ }},
.size_of_priv = sizeof(struct
dib0700_adapter_state),
},
diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c
index 4c2a689c820e..a76bbb29ca36 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-common.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-common.c
@@ -23,7 +23,7 @@ int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.fifo_ctrl != NULL)
- if (st->ops.fifo_ctrl(adap->fe,onoff)) {
+ if (st->ops.fifo_ctrl(adap->fe_adap[0].fe, onoff)) {
err("error while controlling the fifo of the demod.");
return -ENODEV;
}
@@ -37,7 +37,8 @@ int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onof
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_ctrl != NULL)
- st->ops.pid_ctrl(adap->fe,index,pid,onoff);
+ st->ops.pid_ctrl(adap->fe_adap[0].fe,
+ index, pid, onoff);
}
return 0;
}
@@ -48,7 +49,7 @@ int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_parse != NULL)
- if (st->ops.pid_parse(adap->fe,onoff) < 0)
+ if (st->ops.pid_parse(adap->fe_adap[0].fe, onoff) < 0)
err("could not handle pid_parser");
}
return 0;
@@ -254,8 +255,16 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap)
msleep(1000);
}
- if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL ||
- (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
+ &adap->dev->i2c_adap,
+ DEFAULT_DIB3000P_I2C_ADDRESS,
+ &mod3000p_dib3000p_config);
+ if ((adap->fe_adap[0].fe) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
+ &adap->dev->i2c_adap,
+ DEFAULT_DIB3000MC_I2C_ADDRESS,
+ &mod3000p_dib3000p_config);
+ if ((adap->fe_adap[0].fe) != NULL) {
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
st->ops.pid_parse = dib3000mc_pid_parse;
@@ -309,15 +318,15 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap)
}
}
- tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
- if (dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
+ tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1);
+ if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
/* not found - use panasonic pll parameters */
- if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL)
+ if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL)
return -ENOMEM;
} else {
st->mt2060_present = 1;
/* set the correct parameters for the dib3000p */
- dib3000mc_set_config(adap->fe, &stk3000p_dib3000p_config);
+ dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config);
}
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index 04d91bdd3562..7270791f8340 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -31,11 +31,12 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap)
demod_cfg.demod_address = 0x8;
- if ((adap->fe = dvb_attach(dib3000mb_attach, &demod_cfg,
- &adap->dev->i2c_adap, &st->ops)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg,
+ &adap->dev->i2c_adap, &st->ops);
+ if ((adap->fe_adap[0].fe) == NULL)
return -ENODEV;
- adap->fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl;
return 0;
}
@@ -46,7 +47,7 @@ static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap)
st->tuner_addr = 0x61;
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap,
DVB_PLL_TUA6010XS);
return 0;
}
@@ -57,7 +58,7 @@ static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap)
st->tuner_addr = 0x60;
- dvb_attach(dvb_pll_attach, adap->fe, 0x60, &adap->dev->i2c_adap,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap,
DVB_PLL_TDA665X);
return 0;
}
@@ -78,16 +79,16 @@ static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap)
/* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */
msg[0].addr = msg[1].addr = st->tuner_addr = 0x60;
- if (adap->fe->ops.i2c_gate_ctrl)
- adap->fe->ops.i2c_gate_ctrl(adap->fe,1);
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) {
err("tuner i2c write failed.");
ret = -EREMOTEIO;
}
- if (adap->fe->ops.i2c_gate_ctrl)
- adap->fe->ops.i2c_gate_ctrl(adap->fe,0);
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
if (b2[0] == 0xfe) {
info("This device has the Thomson Cable onboard. Which is default.");
@@ -185,6 +186,8 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -205,6 +208,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -272,6 +276,8 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
.pid_filter_count = 16,
@@ -292,6 +298,7 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
},
},
@@ -338,6 +345,8 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -358,6 +367,7 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -398,6 +408,8 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
@@ -417,6 +429,7 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c
index c1d9094b61e5..9c165e2569d4 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -57,6 +57,8 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
@@ -76,6 +78,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index f6344cdd360f..f7184111aa64 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -137,11 +137,16 @@ static int digitv_frontend_attach(struct dvb_usb_adapter *adap)
{
struct digitv_state *st = adap->dev->priv;
- if ((adap->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &adap->dev->i2c_adap)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 0;
return 0;
}
- if ((adap->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &adap->dev->i2c_adap)) != NULL) {
+ adap->fe_adap[0].fe = dvb_attach(nxt6000_attach,
+ &digitv_nxt6000_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 1;
return 0;
}
@@ -152,11 +157,11 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap)
{
struct digitv_state *st = adap->dev->priv;
- if (!dvb_attach(dvb_pll_attach, adap->fe, 0x60, NULL, DVB_PLL_TDED4))
+ if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4))
return -ENODEV;
if (st->is_nxt6000)
- adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
+ adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
return 0;
}
@@ -292,6 +297,8 @@ static struct dvb_usb_device_properties digitv_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = digitv_frontend_attach,
.tuner_attach = digitv_tuner_attach,
@@ -306,6 +313,7 @@ static struct dvb_usb_device_properties digitv_properties = {
}
}
},
+ }},
}
},
.identify_state = digitv_identify_state,
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index ecd86eca2548..106dfd55ff9c 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -90,7 +90,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dtt200u_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = dtt200u_fe_attach(adap->dev);
return 0;
}
@@ -140,6 +140,8 @@ static struct dvb_usb_device_properties dtt200u_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -157,6 +159,7 @@ static struct dvb_usb_device_properties dtt200u_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -187,6 +190,8 @@ static struct dvb_usb_device_properties wt220u_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -204,6 +209,7 @@ static struct dvb_usb_device_properties wt220u_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -234,6 +240,8 @@ static struct dvb_usb_device_properties wt220u_fc_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -251,6 +259,7 @@ static struct dvb_usb_device_properties wt220u_fc_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
@@ -281,6 +290,8 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -298,6 +309,7 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
}
}
},
+ }},
}
},
.power_ctrl = dtt200u_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/dvb/dvb-usb/dtv5100.c
index 078ce92ca436..7373132163d2 100644
--- a/drivers/media/dvb/dvb-usb/dtv5100.c
+++ b/drivers/media/dvb/dvb-usb/dtv5100.c
@@ -115,13 +115,13 @@ static struct zl10353_config dtv5100_zl10353_config = {
static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
/* disable i2c gate, or it won't work... is this safe? */
- adap->fe->ops.i2c_gate_ctrl = NULL;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
return 0;
}
@@ -133,7 +133,7 @@ static struct qt1010_config dtv5100_qt1010_config = {
static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -180,6 +180,8 @@ static struct dvb_usb_device_properties dtv5100_properties = {
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dtv5100_frontend_attach,
.tuner_attach = dtv5100_tuner_attach,
@@ -193,6 +195,7 @@ static struct dvb_usb_device_properties dtv5100_properties = {
}
}
},
+ }},
} },
.i2c_algo = &dtv5100_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
index b3cb626ed56e..ba4a7517354f 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
@@ -17,15 +17,20 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
if (adap == NULL)
return -ENODEV;
+ if ((adap->active_fe < 0) ||
+ (adap->active_fe >= adap->num_frontends_initialized)) {
+ return -EINVAL;
+ }
+
newfeedcount = adap->feedcount + (onoff ? 1 : -1);
/* stop feed before setting a new pid if there will be no pid anymore */
if (newfeedcount == 0) {
deb_ts("stop feeding\n");
- usb_urb_kill(&adap->stream);
+ usb_urb_kill(&adap->fe_adap[adap->active_fe].stream);
- if (adap->props.streaming_ctrl != NULL) {
- ret = adap->props.streaming_ctrl(adap, 0);
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0);
if (ret < 0) {
err("error while stopping stream.");
return ret;
@@ -36,36 +41,37 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
adap->feedcount = newfeedcount;
/* activate the pid on the device specific pid_filter */
- deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",adap->pid_filtering ?
- "yes" : "no", dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ?
- "on" : "off");
- if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->pid_filtering &&
- adap->props.pid_filter != NULL)
- adap->props.pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid,onoff);
+ deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",
+ adap->fe_adap[adap->active_fe].pid_filtering ?
+ "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid,
+ dvbdmxfeed->index, onoff ? "on" : "off");
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->fe_adap[adap->active_fe].pid_filtering &&
+ adap->props.fe[adap->active_fe].pid_filter != NULL)
+ adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
/* start the feed if this was the first feed and there is still a feed
* for reception.
*/
if (adap->feedcount == onoff && adap->feedcount > 0) {
deb_ts("submitting all URBs\n");
- usb_urb_submit(&adap->stream);
+ usb_urb_submit(&adap->fe_adap[adap->active_fe].stream);
deb_ts("controlling pid parser\n");
- if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->props.caps &
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->props.fe[adap->active_fe].caps &
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
- adap->props.pid_filter_ctrl != NULL) {
- ret = adap->props.pid_filter_ctrl(adap,
- adap->pid_filtering);
+ adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap,
+ adap->fe_adap[adap->active_fe].pid_filtering);
if (ret < 0) {
err("could not handle pid_parser");
return ret;
}
}
deb_ts("start feeding\n");
- if (adap->props.streaming_ctrl != NULL) {
- ret = adap->props.streaming_ctrl(adap, 1);
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1);
if (ret < 0) {
err("error while enabling fifo.");
return ret;
@@ -90,6 +96,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
{
+ int i;
int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
adap->dev->owner, &adap->dev->udev->dev,
adapter_nums);
@@ -112,7 +119,12 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
adap->demux.priv = adap;
- adap->demux.feednum = adap->demux.filternum = adap->max_feed_count;
+ adap->demux.filternum = 0;
+ for (i = 0; i < adap->props.num_frontends; i++) {
+ if (adap->demux.filternum < adap->fe_adap[i].max_feed_count)
+ adap->demux.filternum = adap->fe_adap[i].max_feed_count;
+ }
+ adap->demux.feednum = adap->demux.filternum;
adap->demux.start_feed = dvb_usb_start_feed;
adap->demux.stop_feed = dvb_usb_stop_feed;
adap->demux.write_to_decoder = NULL;
@@ -156,14 +168,33 @@ int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap)
return 0;
}
+static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ int ret = (adap->props.frontend_ctrl) ?
+ adap->props.frontend_ctrl(fe, onoff) : 0;
+
+ if (ret < 0) {
+ err("frontend_ctrl request failed");
+ return ret;
+ }
+ if (onoff)
+ adap->active_fe = fe->id;
+
+ return 0;
+}
+
static int dvb_usb_fe_wakeup(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
dvb_usb_device_power_ctrl(adap->dev, 1);
- if (adap->fe_init)
- adap->fe_init(fe);
+ dvb_usb_set_active_fe(fe, 1);
+
+ if (adap->fe_adap[fe->id].fe_init)
+ adap->fe_adap[fe->id].fe_init(fe);
return 0;
}
@@ -172,45 +203,81 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
- if (adap->fe_sleep)
- adap->fe_sleep(fe);
+ if (adap->fe_adap[fe->id].fe_sleep)
+ adap->fe_adap[fe->id].fe_sleep(fe);
+
+ dvb_usb_set_active_fe(fe, 0);
return dvb_usb_device_power_ctrl(adap->dev, 0);
}
int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
{
- if (adap->props.frontend_attach == NULL) {
- err("strange: '%s' #%d doesn't want to attach a frontend.",adap->dev->desc->name, adap->id);
- return 0;
- }
+ int ret, i;
- /* re-assign sleep and wakeup functions */
- if (adap->props.frontend_attach(adap) == 0 && adap->fe != NULL) {
- adap->fe_init = adap->fe->ops.init; adap->fe->ops.init = dvb_usb_fe_wakeup;
- adap->fe_sleep = adap->fe->ops.sleep; adap->fe->ops.sleep = dvb_usb_fe_sleep;
+ /* register all given adapter frontends */
+ for (i = 0; i < adap->props.num_frontends; i++) {
- if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
- err("Frontend registration failed.");
- dvb_frontend_detach(adap->fe);
- adap->fe = NULL;
- return -ENODEV;
+ if (adap->props.fe[i].frontend_attach == NULL) {
+ err("strange: '%s' #%d,%d "
+ "doesn't want to attach a frontend.",
+ adap->dev->desc->name, adap->id, i);
+
+ return 0;
+ }
+
+ ret = adap->props.fe[i].frontend_attach(adap);
+ if (ret || adap->fe_adap[i].fe == NULL) {
+ /* only print error when there is no FE at all */
+ if (i == 0)
+ err("no frontend was attached by '%s'",
+ adap->dev->desc->name);
+
+ return 0;
+ }
+
+ adap->fe_adap[i].fe->id = i;
+
+ /* re-assign sleep and wakeup functions */
+ adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init;
+ adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup;
+ adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
+ adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
+
+ if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
+ err("Frontend %d registration failed.", i);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ adap->fe_adap[i].fe = NULL;
+ /* In error case, do not try register more FEs,
+ * still leaving already registered FEs alive. */
+ if (i == 0)
+ return -ENODEV;
+ else
+ return 0;
}
/* only attach the tuner if the demod is there */
- if (adap->props.tuner_attach != NULL)
- adap->props.tuner_attach(adap);
- } else
- err("no frontend was attached by '%s'",adap->dev->desc->name);
+ if (adap->props.fe[i].tuner_attach != NULL)
+ adap->props.fe[i].tuner_attach(adap);
+
+ adap->num_frontends_initialized++;
+ }
return 0;
}
int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
{
- if (adap->fe != NULL) {
- dvb_unregister_frontend(adap->fe);
- dvb_frontend_detach(adap->fe);
+ int i = adap->num_frontends_initialized - 1;
+
+ /* unregister all given adapter frontends */
+ for (; i >= 0; i--) {
+ if (adap->fe_adap[i].fe != NULL) {
+ dvb_unregister_frontend(adap->fe_adap[i].fe);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ }
}
+ adap->num_frontends_initialized = 0;
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 2a79b8fb3e8d..2d08c9b5128a 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -37,6 +37,7 @@
#define USB_VID_HAUPPAUGE 0x2040
#define USB_VID_HYPER_PALTEK 0x1025
#define USB_VID_INTEL 0x8086
+#define USB_VID_ITETECH 0x048d
#define USB_VID_KWORLD 0xeb2a
#define USB_VID_KWORLD_2 0x1b80
#define USB_VID_KYE 0x0458
@@ -126,6 +127,7 @@
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_INTEL_CE9500 0x9500
+#define USB_PID_ITETECH_IT9135 0x9135
#define USB_PID_KWORLD_399U 0xe399
#define USB_PID_KWORLD_399U_2 0xe400
#define USB_PID_KWORLD_395U 0xe396
@@ -136,6 +138,7 @@
#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_PC160_T 0xc161
#define USB_PID_KWORLD_UB383_T 0xe383
+#define USB_PID_KWORLD_UB499_2T_T09 0xe409
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
@@ -240,6 +243,9 @@
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
+#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
+#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
#define USB_PID_NEBULA_DIGITV 0x0201
#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
index 2e3ea0fa28e0..169196ec2d4e 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
@@ -29,7 +29,7 @@ MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID
static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
{
struct dvb_usb_adapter *adap;
- int ret, n;
+ int ret, n, o;
for (n = 0; n < d->props.num_adapters; n++) {
adap = &d->adapter[n];
@@ -38,31 +38,42 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties));
+ for (o = 0; o < adap->props.num_frontends; o++) {
+ struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o];
/* speed - when running at FULL speed we need a HW PID filter */
- if (d->udev->speed == USB_SPEED_FULL && !(adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
+ if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)");
return -ENODEV;
}
- if ((d->udev->speed == USB_SPEED_FULL && adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
- (adap->props.caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
- info("will use the device's hardware PID filter (table count: %d).", adap->props.pid_filter_count);
- adap->pid_filtering = 1;
- adap->max_feed_count = adap->props.pid_filter_count;
+ if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
+ (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
+ info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count);
+ adap->fe_adap[o].pid_filtering = 1;
+ adap->fe_adap[o].max_feed_count = props->pid_filter_count;
} else {
info("will pass the complete MPEG2 transport stream to the software demuxer.");
- adap->pid_filtering = 0;
- adap->max_feed_count = 255;
+ adap->fe_adap[o].pid_filtering = 0;
+ adap->fe_adap[o].max_feed_count = 255;
}
- if (!adap->pid_filtering &&
+ if (!adap->fe_adap[o].pid_filtering &&
dvb_usb_force_pid_filter_usage &&
- adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) {
+ props->caps & DVB_USB_ADAP_HAS_PID_FILTER) {
info("pid filter enabled by module option.");
- adap->pid_filtering = 1;
- adap->max_feed_count = adap->props.pid_filter_count;
+ adap->fe_adap[o].pid_filtering = 1;
+ adap->fe_adap[o].max_feed_count = props->pid_filter_count;
}
+ if (props->size_of_priv > 0) {
+ adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL);
+ if (adap->fe_adap[o].priv == NULL) {
+ err("no memory for priv for adapter %d fe %d.", n, o);
+ return -ENOMEM;
+ }
+ }
+ }
+
if (adap->props.size_of_priv > 0) {
adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL);
if (adap->priv == NULL) {
@@ -77,6 +88,10 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
return ret;
}
+ /* use exclusive FE lock if there is multiple shared FEs */
+ if (adap->fe_adap[1].fe)
+ adap->dvb_adap.mfe_shared = 1;
+
d->num_adapters_initialized++;
d->state |= DVB_USB_STATE_DVB;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
index bb46ba6a3573..53a5c30b51b2 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
@@ -82,16 +82,28 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer
int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
{
- adap->stream.udev = adap->dev->udev;
- if (adap->props.caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
- adap->stream.complete = dvb_usb_data_complete_204;
- else
- adap->stream.complete = dvb_usb_data_complete;
- adap->stream.user_priv = adap;
- return usb_urb_init(&adap->stream, &adap->props.stream);
+ int i, ret = 0;
+ for (i = 0; i < adap->props.num_frontends; i++) {
+
+ adap->fe_adap[i].stream.udev = adap->dev->udev;
+ if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
+ adap->fe_adap[i].stream.complete =
+ dvb_usb_data_complete_204;
+ else
+ adap->fe_adap[i].stream.complete = dvb_usb_data_complete;
+ adap->fe_adap[i].stream.user_priv = adap;
+ ret = usb_urb_init(&adap->fe_adap[i].stream,
+ &adap->props.fe[i].stream);
+ if (ret < 0)
+ break;
+ }
+ return ret;
}
int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap)
{
- return usb_urb_exit(&adap->stream);
+ int i;
+ for (i = 0; i < adap->props.num_frontends; i++)
+ usb_urb_exit(&adap->fe_adap[i].stream);
+ return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index 7d35d078342b..6d7d13f9ce68 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -124,6 +124,8 @@ struct usb_data_stream_properties {
* @caps: capabilities of the DVB USB device.
* @pid_filter_count: number of PID filter position in the optional hardware
* PID-filter.
+ * @num_frontends: number of frontends of the DVB USB adapter.
+ * @frontend_ctrl: called to power on/off active frontend.
* @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the
* device (not URB submitting/killing).
* @pid_filter_ctrl: called to en/disable the PID filter, if any.
@@ -134,7 +136,7 @@ struct usb_data_stream_properties {
* pll_desc and pll_init_buf of struct dvb_usb_device).
* @stream: configuration of the USB streaming
*/
-struct dvb_usb_adapter_properties {
+struct dvb_usb_adapter_fe_properties {
#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
@@ -152,9 +154,18 @@ struct dvb_usb_adapter_properties {
struct usb_data_stream_properties stream;
int size_of_priv;
+};
+
+#define MAX_NO_OF_FE_PER_ADAP 2
+struct dvb_usb_adapter_properties {
+ int size_of_priv;
+ int (*frontend_ctrl) (struct dvb_frontend *, int);
int (*fe_ioctl_override) (struct dvb_frontend *,
unsigned int, void *, unsigned int);
+
+ int num_frontends;
+ struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP];
};
/**
@@ -345,6 +356,20 @@ struct usb_data_stream {
*
* @stream: the usb data stream.
*/
+struct dvb_usb_fe_adapter {
+ struct dvb_frontend *fe;
+
+ int (*fe_init) (struct dvb_frontend *);
+ int (*fe_sleep) (struct dvb_frontend *);
+
+ struct usb_data_stream stream;
+
+ int pid_filtering;
+ int max_feed_count;
+
+ void *priv;
+};
+
struct dvb_usb_adapter {
struct dvb_usb_device *dev;
struct dvb_usb_adapter_properties props;
@@ -356,20 +381,16 @@ struct dvb_usb_adapter {
u8 id;
int feedcount;
- int pid_filtering;
/* dvb */
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
- struct dvb_frontend *fe;
- int max_feed_count;
-
- int (*fe_init) (struct dvb_frontend *);
- int (*fe_sleep) (struct dvb_frontend *);
- struct usb_data_stream stream;
+ struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP];
+ int active_fe;
+ int num_frontends_initialized;
void *priv;
};
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 058b2318abed..f103ec1fe82e 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -992,18 +992,18 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
struct dvb_tuner_ops *tuner_ops = NULL;
if (demod_probe & 4) {
- d->fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe != NULL) {
- if (dvb_attach(stb6100_attach, d->fe,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stb6100_attach, d->fe_adap[0].fe,
&dw2104a_stb6100_config,
&d->dev->i2c_adap)) {
- tuner_ops = &d->fe->ops.tuner_ops;
+ tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops;
tuner_ops->set_frequency = stb6100_set_freq;
tuner_ops->get_frequency = stb6100_get_freq;
tuner_ops->set_bandwidth = stb6100_set_bandw;
tuner_ops->get_bandwidth = stb6100_get_bandw;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached STV0900+STB6100!\n");
return 0;
}
@@ -1011,13 +1011,13 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
}
if (demod_probe & 2) {
- d->fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe != NULL) {
- if (dvb_attach(stv6110_attach, d->fe,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stv6110_attach, d->fe_adap[0].fe,
&dw2104_stv6110_config,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached STV0900+STV6110A!\n");
return 0;
}
@@ -1025,19 +1025,19 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
}
if (demod_probe & 1) {
- d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+ d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached cx24116!\n");
return 0;
}
}
- d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached DS3000!\n");
return 0;
}
@@ -1053,22 +1053,22 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
{
if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = NULL;*/
- d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
+ d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached si21xx!\n");
return 0;
}
}
if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
- d->fe = dvb_attach(stv0288_attach, &earda_config,
+ d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- if (dvb_attach(stb6000_attach, d->fe, 0x61,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0288!\n");
return 0;
}
@@ -1077,10 +1077,10 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
- d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
+ d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ if (d->fe_adap[0].fe != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0299!\n");
return 0;
}
@@ -1090,9 +1090,9 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
+ d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
&d->dev->i2c_adap, 0x48);
- if (d->fe != NULL) {
+ if (d->fe_adap[0].fe != NULL) {
info("Attached tda10023!\n");
return 0;
}
@@ -1101,12 +1101,12 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
static int zl100313_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(mt312_attach, &zl313_config,
+ d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- if (dvb_attach(zl10039_attach, d->fe, 0x60,
+ if (d->fe_adap[0].fe != NULL) {
+ if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60,
&d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached zl100313+zl10039!\n");
return 0;
}
@@ -1119,16 +1119,16 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
{
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(stv0288_attach, &earda_config,
+ d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- if (NULL == dvb_attach(stb6000_attach, d->fe, 0x61, &d->dev->i2c_adap))
+ if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap))
return -EIO;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1143,14 +1143,14 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- st->old_set_voltage = d->fe->ops.set_voltage;
- d->fe->ops.set_voltage = s660_set_voltage;
+ st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1163,12 +1163,12 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
{
u8 obuf[] = {7, 1};
- d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
+ d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
&d->dev->i2c_adap, 0);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
- d->fe->ops.set_voltage = dw210x_set_voltage;
+ d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
@@ -1204,9 +1204,9 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
err("command 0x51 transfer failed.");
- d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
+ d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe == NULL)
+ if (d->fe_adap[0].fe == NULL)
return -EIO;
info("Attached DS3000!\n");
@@ -1216,14 +1216,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
&adap->dev->i2c_adap, DVB_PLL_OPERA1);
return 0;
}
static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
&adap->dev->i2c_adap, DVB_PLL_TUA6034);
return 0;
@@ -1535,7 +1535,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
DW210X_READ_MSG);
if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) {
dw2102_properties.i2c_algo = &dw2102_i2c_algo;
- dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
+ dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach;
break;
} else {
/* check STV0288 frontend */
@@ -1591,6 +1591,8 @@ static struct dvb_usb_device_properties dw2102_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw2102_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1602,6 +1604,7 @@ static struct dvb_usb_device_properties dw2102_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 3,
@@ -1642,6 +1645,8 @@ static struct dvb_usb_device_properties dw2104_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw2104_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1653,6 +1658,7 @@ static struct dvb_usb_device_properties dw2104_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 2,
@@ -1689,6 +1695,8 @@ static struct dvb_usb_device_properties dw3101_properties = {
.read_mac_address = dw210x_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = dw3101_frontend_attach,
.tuner_attach = dw3101_tuner_attach,
.stream = {
@@ -1701,6 +1709,7 @@ static struct dvb_usb_device_properties dw3101_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
@@ -1733,6 +1742,8 @@ static struct dvb_usb_device_properties s6x0_properties = {
.read_mac_address = s6x0_read_mac_address,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = zl100313_frontend_attach,
.stream = {
.type = USB_BULK,
@@ -1744,6 +1755,7 @@ static struct dvb_usb_device_properties s6x0_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
@@ -1810,6 +1822,8 @@ static struct dvb_usb_device_properties su3000_properties = {
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = su3000_streaming_ctrl,
.frontend_attach = su3000_frontend_attach,
.stream = {
@@ -1822,6 +1836,7 @@ static struct dvb_usb_device_properties su3000_properties = {
}
}
}
+ }},
}
},
.num_device_descs = 3,
@@ -1855,7 +1870,7 @@ static int dw2102_probe(struct usb_interface *intf,
p1100->devices[0] = d1100;
p1100->rc.legacy.rc_map_table = rc_map_tbs_table;
p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table);
- p1100->adapter->frontend_attach = stv0288_frontend_attach;
+ p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach;
s660 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
if (!s660) {
@@ -1869,7 +1884,7 @@ static int dw2102_probe(struct usb_interface *intf,
s660->devices[0] = d660;
s660->devices[1] = d480_1;
s660->devices[2] = d480_2;
- s660->adapter->frontend_attach = ds3000_frontend_attach;
+ s660->adapter->fe[0].frontend_attach = ds3000_frontend_attach;
p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
if (!p7500) {
@@ -1883,7 +1898,7 @@ static int dw2102_probe(struct usb_interface *intf,
p7500->devices[0] = d7500;
p7500->rc.legacy.rc_map_table = rc_map_tbs_table;
p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table);
- p7500->adapter->frontend_attach = prof_7500_frontend_attach;
+ p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach;
if (0 == dvb_usb_device_init(intf, &dw2102_properties,
THIS_MODULE, NULL, adapter_nr) ||
diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c
index 1ba3e5dbee10..78442fe4aa5e 100644
--- a/drivers/media/dvb/dvb-usb/ec168.c
+++ b/drivers/media/dvb/dvb-usb/ec168.c
@@ -200,9 +200,9 @@ static struct ec100_config ec168_ec100_config = {
static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config,
+ adap->fe_adap[0].fe = dvb_attach(ec100_attach, &ec168_ec100_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -ENODEV;
return 0;
@@ -228,7 +228,7 @@ static struct mxl5005s_config ec168_mxl5003s_config = {
static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
deb_info("%s:\n", __func__);
- return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+ return dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
}
@@ -382,6 +382,8 @@ static struct dvb_usb_device_properties ec168_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = ec168_streaming_ctrl,
.frontend_attach = ec168_ec100_frontend_attach,
.tuner_attach = ec168_mxl5003s_tuner_attach,
@@ -395,6 +397,7 @@ static struct dvb_usb_device_properties ec168_properties = {
}
}
},
+ }},
}
},
diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c
index 76159aed9bb0..b092dc2137cd 100644
--- a/drivers/media/dvb/dvb-usb/friio.c
+++ b/drivers/media/dvb/dvb-usb/friio.c
@@ -403,8 +403,8 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap)
if (friio_initialize(adap->dev) < 0)
return -EIO;
- adap->fe = jdvbt90502_attach(adap->dev);
- if (adap->fe == NULL)
+ adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev);
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -473,6 +473,8 @@ static struct dvb_usb_device_properties friio_properties = {
/* caps:0 => no pid filter, 188B TS packet */
/* GL861 has a HW pid filter, but no info available. */
{
+ .num_frontends = 1,
+ .fe = {{
.caps = 0,
.frontend_attach = friio_frontend_attach,
@@ -490,6 +492,7 @@ static struct dvb_usb_device_properties friio_properties = {
}
}
},
+ }},
}
},
.i2c_algo = &gl861_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index 6f596ed41761..63681df244c4 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -103,9 +103,9 @@ static struct zl10353_config gl861_zl10353_config = {
static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
+ adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
&adap->dev->i2c_adap);
- if (adap->fe == NULL)
+ if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
@@ -118,7 +118,7 @@ static struct qt1010_config gl861_qt1010_config = {
static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
- adap->fe, &adap->dev->i2c_adap,
+ adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&gl861_qt1010_config) == NULL ? -ENODEV : 0;
}
@@ -167,6 +167,8 @@ static struct dvb_usb_device_properties gl861_properties = {
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = gl861_frontend_attach,
.tuner_attach = gl861_tuner_attach,
@@ -181,6 +183,7 @@ static struct dvb_usb_device_properties gl861_properties = {
}
}
},
+ }},
} },
.i2c_algo = &gl861_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index 60d11e57e7d0..5426267980c7 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -144,19 +144,25 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
+ /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */
+ if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8)
+ c->delivery_system = SYS_TURBO;
+
switch (c->delivery_system) {
case SYS_DVBS:
- /* Allow QPSK and 8PSK (even for DVB-S) */
- if (c->modulation != QPSK && c->modulation != PSK_8) {
+ if (c->modulation != QPSK) {
deb_fe("%s: unsupported modulation selected (%d)\n",
__func__, c->modulation);
return -EOPNOTSUPP;
}
c->fec_inner = FEC_AUTO;
break;
- case SYS_DVBS2:
+ case SYS_DVBS2: /* kept for backwards compatibility */
deb_fe("%s: DVB-S2 delivery system selected\n", __func__);
break;
+ case SYS_TURBO:
+ deb_fe("%s: Turbo-FEC delivery system selected\n", __func__);
+ break;
default:
deb_fe("%s: unsupported delivery system selected (%d)\n",
@@ -189,7 +195,10 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
default:
cmd[9] = 5; break;
}
- cmd[8] = ADV_MOD_DVB_QPSK;
+ if (c->delivery_system == SYS_TURBO)
+ cmd[8] = ADV_MOD_TURBO_QPSK;
+ else
+ cmd[8] = ADV_MOD_DVB_QPSK;
break;
case PSK_8: /* PSK_8 is for compatibility with DN */
cmd[8] = ADV_MOD_TURBO_8PSK;
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 1cb3d9a66e02..5f71284703d0 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -230,7 +230,7 @@ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe = gp8psk_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev);
return 0;
}
@@ -268,6 +268,8 @@ static struct dvb_usb_device_properties gp8psk_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = gp8psk_streaming_ctrl,
.frontend_attach = gp8psk_frontend_attach,
/* parameter for the MPEG2-data transfer */
@@ -281,6 +283,7 @@ static struct dvb_usb_device_properties gp8psk_properties = {
}
}
},
+ }},
}
},
.power_ctrl = gp8psk_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
new file mode 100644
index 000000000000..c46226187143
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -0,0 +1,702 @@
+/* DVB USB compliant linux driver for IT9137
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9137 (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/dvb/it9137.txt for firmware information
+ *
+ */
+#define DVB_USB_LOG_PREFIX "it913x"
+
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#include "dvb-usb.h"
+#include "it913x-fe.h"
+
+/* debug */
+static int dvb_usb_it913x_debug;
+#define l_dprintk(var, level, args...) do { \
+ if ((var >= level)) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \
+} while (0)
+
+#define deb_info(level, args...) l_dprintk(dvb_usb_it913x_debug, level, args)
+#define debug_data_snipet(level, name, p) \
+ deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+
+
+module_param_named(debug, dvb_usb_it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."
+ DVB_USB_DEBUG_STATUS);
+
+static int pid_filter;
+module_param_named(pid, pid_filter, int, 0644);
+MODULE_PARM_DESC(pid, "set default 0=on 1=off");
+
+int cmd_counter;
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct it913x_state {
+ u8 id;
+};
+
+struct ite_config {
+ u8 chip_ver;
+ u16 chip_type;
+ u32 firmware;
+ u8 tuner_id_0;
+ u8 tuner_id_1;
+ u8 dual_mode;
+};
+
+struct ite_config it913x_config;
+
+static int it913x_bulk_write(struct usb_device *dev,
+ u8 *snd, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
+ snd, len , &actual_l, 100);
+ return ret;
+}
+
+static int it913x_bulk_read(struct usb_device *dev,
+ u8 *rev, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
+ rev, len , &actual_l, 200);
+ return ret;
+}
+
+static u16 check_sum(u8 *p, u8 len)
+{
+ u16 sum = 0;
+ u8 i = 1;
+ while (i < len)
+ sum += (i++ & 1) ? (*p++) << 8 : *p++;
+ return ~sum;
+}
+
+static int it913x_io(struct usb_device *udev, u8 mode, u8 pro,
+ u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
+{
+ int ret = 0, i, buf_size = 1;
+ u8 *buff;
+ u8 rlen;
+ u16 chk_sum;
+
+ buff = kzalloc(256, GFP_KERNEL);
+ if (!buff) {
+ info("USB Buffer Failed");
+ return -ENOMEM;
+ }
+
+ buff[buf_size++] = pro;
+ buff[buf_size++] = cmd;
+ buff[buf_size++] = cmd_counter;
+
+ switch (mode) {
+ case READ_LONG:
+ case WRITE_LONG:
+ buff[buf_size++] = len;
+ buff[buf_size++] = 2;
+ buff[buf_size++] = (reg >> 24);
+ buff[buf_size++] = (reg >> 16) & 0xff;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_SHORT:
+ buff[buf_size++] = addr;
+ break;
+ case WRITE_SHORT:
+ buff[buf_size++] = len;
+ buff[buf_size++] = addr;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_DATA:
+ case WRITE_DATA:
+ break;
+ case WRITE_CMD:
+ mode = 7;
+ break;
+ default:
+ kfree(buff);
+ return -EINVAL;
+ }
+
+ if (mode & 1) {
+ for (i = 0; i < len ; i++)
+ buff[buf_size++] = data[i];
+ }
+ chk_sum = check_sum(&buff[1], buf_size);
+
+ buff[buf_size++] = chk_sum >> 8;
+ buff[0] = buf_size;
+ buff[buf_size++] = (chk_sum & 0xff);
+
+ ret = it913x_bulk_write(udev, buff, buf_size , 0x02);
+
+ ret |= it913x_bulk_read(udev, buff, (mode & 1) ?
+ 5 : len + 5 , 0x01);
+
+ rlen = (mode & 0x1) ? 0x1 : len;
+
+ if (mode & 1)
+ ret |= buff[2];
+ else
+ memcpy(data, &buff[3], rlen);
+
+ cmd_counter++;
+
+ kfree(buff);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_wr_reg(struct usb_device *udev, u8 pro, u32 reg , u8 data)
+{
+ int ret;
+ u8 b[1];
+ b[0] = data;
+ ret = it913x_io(udev, WRITE_LONG, pro,
+ CMD_DEMOD_WRITE, reg, 0, b, sizeof(b));
+
+ return ret;
+}
+
+static int it913x_read_reg(struct usb_device *udev, u32 reg)
+{
+ int ret;
+ u8 data[1];
+
+ ret = it913x_io(udev, READ_LONG, DEV_0,
+ CMD_DEMOD_READ, reg, 0, &data[0], 1);
+
+ return (ret < 0) ? ret : data[0];
+}
+
+static u32 it913x_query(struct usb_device *udev, u8 pro)
+{
+ int ret;
+ u8 data[4];
+ ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
+ 0x1222, 0, &data[0], 3);
+
+ it913x_config.chip_ver = data[0];
+ it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
+
+ info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
+ it913x_config.chip_type);
+
+ ret |= it913x_io(udev, READ_SHORT, pro,
+ CMD_QUERYINFO, 0, 0x1, &data[0], 4);
+
+ it913x_config.firmware = (data[0] << 24) + (data[1] << 16) +
+ (data[2] << 8) + data[3];
+
+ return (ret < 0) ? 0 : it913x_config.firmware;
+}
+
+static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "PID_C (%02x)", onoff);
+
+ if (!onoff)
+ ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+ return ret;
+}
+
+static int it913x_pid_filter(struct dvb_usb_adapter *adap,
+ int index, u16 pid, int onoff)
+{
+ struct usb_device *udev = adap->dev->udev;
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (pid_filter > 0)
+ return 0;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "PID_F (%02x)", onoff);
+ if (onoff) {
+ ret = it913x_wr_reg(udev, pro, PID_EN, 0x1);
+
+ ret |= it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
+
+ ret |= it913x_wr_reg(udev, pro, PID_MSB, (u8)(pid >> 8));
+
+ ret |= it913x_wr_reg(udev, pro, PID_INX_EN, (u8)onoff);
+
+ ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
+
+ }
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+ return 0;
+}
+
+
+static int it913x_return_status(struct usb_device *udev)
+{
+ u32 firm = 0;
+
+ firm = it913x_query(udev, DEV_0);
+ if (firm > 0)
+ info("Firmware Version %d", firm);
+
+ return (firm > 0) ? firm : 0;
+}
+
+static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ static u8 data[256];
+ int ret;
+ u32 reg;
+ u8 pro;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ debug_data_snipet(1, "Message out", msg[0].buf);
+ deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
+
+ pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0;
+ pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0;
+ memcpy(data, msg[0].buf, msg[0].len);
+ reg = (data[0] << 24) + (data[1] << 16) +
+ (data[2] << 8) + data[3];
+ if (num == 2) {
+ ret = it913x_io(d->udev, READ_LONG, pro,
+ CMD_DEMOD_READ, reg, 0, data, msg[1].len);
+ memcpy(msg[1].buf, data, msg[1].len);
+ } else
+ ret = it913x_io(d->udev, WRITE_LONG, pro, CMD_DEMOD_WRITE,
+ reg, 0, &data[4], msg[0].len - 4);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 it913x_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm it913x_i2c_algo = {
+ .master_xfer = it913x_i2c_xfer,
+ .functionality = it913x_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+#define IT913X_POLL 250
+static int it913x_rc_query(struct dvb_usb_device *d)
+{
+ u8 ibuf[4];
+ int ret;
+ u32 key;
+ /* Avoid conflict with frontends*/
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
+ 0, 0, &ibuf[0], sizeof(ibuf));
+
+ if ((ibuf[2] + ibuf[3]) == 0xff) {
+ key = ibuf[2];
+ key += ibuf[0] << 8;
+ deb_info(1, "INT Key =%08x", key);
+ if (d->rc_dev != NULL)
+ rc_keydown(d->rc_dev, key, 0);
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+static int it913x_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret = 0, firm_no;
+ u8 reg, remote;
+
+ firm_no = it913x_return_status(udev);
+
+ /* checnk for dual mode */
+ it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
+
+ /* TODO different remotes */
+ remote = it913x_read_reg(udev, 0x49ac); /* Remote */
+ if (remote == 0)
+ props->rc.core.rc_codes = NULL;
+
+ /* TODO at the moment tuner_id is always assigned to 0x38 */
+ it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
+
+ info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
+ , remote, it913x_config.tuner_id_0);
+
+ if (firm_no > 0) {
+ *cold = 0;
+ return 0;
+ }
+
+ if (it913x_config.dual_mode) {
+ it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0);
+ ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
+ msleep(50);
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0);
+ msleep(50);
+ reg = it913x_read_reg(udev, GPIOH1_O);
+ if (reg == 0) {
+ ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
+ ret |= it913x_return_status(udev);
+ if (ret != 0)
+ ret = it913x_wr_reg(udev, DEV_0,
+ GPIOH1_O, 0x0);
+ }
+ props->num_adapters = 2;
+ } else
+ props->num_adapters = 1;
+
+ reg = it913x_read_reg(udev, IO_MUX_POWER_CLK);
+
+ if (it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
+ ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1);
+ } else {
+ ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0);
+ }
+
+ *cold = 1;
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+ deb_info(1, "STM (%02x)", onoff);
+
+ if (!onoff)
+ ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+
+
+ mutex_unlock(&adap->dev->i2c_mutex);
+
+ return ret;
+}
+
+
+static int it913x_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int ret = 0, i;
+ u8 packet_size, dlen;
+ u8 *fw_data;
+
+ packet_size = 0x29;
+
+ ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100);
+
+ info("FRM Starting Firmware Download");
+ /* This uses scatter write firmware headers follow */
+ /* 03 XX 00 XX = chip number? */
+
+ for (i = 0; i < fw->size; i += packet_size) {
+ if (i > 0)
+ packet_size = 0x39;
+ fw_data = (u8 *)(fw->data + i);
+ dlen = ((i + packet_size) > fw->size)
+ ? (fw->size - i) : packet_size;
+ ret |= it913x_io(udev, WRITE_DATA, DEV_0,
+ CMD_SCATTER_WRITE, 0, 0, fw_data, dlen);
+ udelay(1000);
+ }
+
+ ret |= it913x_io(udev, WRITE_CMD, DEV_0,
+ CMD_BOOT, 0, 0, NULL, 0);
+
+ msleep(100);
+
+ if (ret < 0)
+ info("FRM Firmware Download Failed (%04x)" , ret);
+ else
+ info("FRM Firmware Download Completed - Resetting Device");
+
+ ret |= it913x_return_status(udev);
+
+ msleep(30);
+
+ ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
+
+ /* Tuner function */
+ if (it913x_config.dual_mode)
+ ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
+
+ ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0);
+ if (it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0);
+ ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0);
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_name(struct dvb_usb_adapter *adap)
+{
+ const char *desc = adap->dev->desc->name;
+ char *fe_name[] = {"_1", "_2", "_3", "_4"};
+ char *name = adap->fe_adap[0].fe->ops.info.name;
+
+ strlcpy(name, desc, 128);
+ strlcat(name, fe_name[adap->id], 128);
+
+ return 0;
+}
+
+static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct usb_device *udev = adap->dev->udev;
+ int ret = 0;
+ u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK);
+ u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
+ u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize;
+ u8 tuner_id, tuner_type;
+
+ if (adap->id == 0)
+ tuner_id = it913x_config.tuner_id_0;
+ else
+ tuner_id = it913x_config.tuner_id_1;
+
+ /* TODO we always use IT9137 possible references here*/
+ /* Documentation suggests don't care */
+ switch (tuner_id) {
+ case 0x51:
+ case 0x52:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ default:
+ case 0x38:
+ tuner_type = IT9137;
+ }
+
+ adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach,
+ &adap->dev->i2c_adap, adap_addr, adf, tuner_type);
+
+ if (adap->id == 0 && adap->fe_adap[0].fe) {
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x0f);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_NAK, 0x1b);
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x2f);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_LSB,
+ ep_size & 0xff);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
+ ret = it913x_wr_reg(udev, DEV_0, EP4_MAX_PKT, 0x80);
+ } else if (adap->id == 1 && adap->fe_adap[0].fe) {
+ ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x6f);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_LSB,
+ ep_size & 0xff);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
+ ret = it913x_wr_reg(udev, DEV_0, EP5_MAX_PKT, 0x80);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_EN, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_SERIAL, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1, TOP_HOSTB_SER_MODE, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, TSIS_ENABLE, 0x1);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0);
+ ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF_STOP_EN, 0x1);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0);
+ ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_STOP_EN, 0x0);
+ } else
+ return -ENODEV;
+
+ ret = it913x_name(adap);
+
+ return ret;
+}
+
+/* DVB USB Driver */
+static struct dvb_usb_device_properties it913x_properties;
+
+static int it913x_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ cmd_counter = 0;
+ if (0 == dvb_usb_device_init(intf, &it913x_properties,
+ THIS_MODULE, NULL, adapter_nr)) {
+ info("DEV registering device driver");
+ return 0;
+ }
+
+ info("DEV it913x Error");
+ return -ENODEV;
+
+}
+
+static struct usb_device_id it913x_table[] = {
+ { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) },
+ { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, it913x_table);
+
+static struct dvb_usb_device_properties it913x_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = it913x_download_firmware,
+ .firmware = "dvb-usb-it9137-01.fw",
+ .no_reconnect = 1,
+ .size_of_priv = sizeof(struct it913x_state),
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_NEED_PID_FILTERING|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .streaming_ctrl = it913x_streaming_ctrl,
+ .pid_filter_count = 31,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .frontend_attach = it913x_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x04,
+ .u = {/* Keep Low if PID filter on */
+ .bulk = {
+ .buffersize = 3584,
+
+ }
+ }
+ }
+ }},
+ },
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_NEED_PID_FILTERING|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .streaming_ctrl = it913x_streaming_ctrl,
+ .pid_filter_count = 31,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .frontend_attach = it913x_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x05,
+ .u = {
+ .bulk = {
+ .buffersize = 3584,
+
+ }
+ }
+ }
+ }},
+ }
+ },
+ .identify_state = it913x_identify_state,
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "it913x",
+ .rc_query = it913x_rc_query,
+ .rc_interval = IT913X_POLL,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_KWORLD_315U,
+ },
+ .i2c_algo = &it913x_i2c_algo,
+ .num_device_descs = 2,
+ .devices = {
+ { "Kworld UB499-2T T09(IT9137)",
+ { &it913x_table[0], NULL },
+ },
+ { "ITE 9135 Generic",
+ { &it913x_table[1], NULL },
+ },
+ }
+};
+
+static struct usb_driver it913x_driver = {
+ .name = "it913x",
+ .probe = it913x_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = it913x_table,
+};
+
+/* module stuff */
+static int __init it913x_module_init(void)
+{
+ int result = usb_register(&it913x_driver);
+ if (result) {
+ err("usb_register failed. Error number %d", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit it913x_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&it913x_driver);
+}
+
+module_init(it913x_module_init);
+module_exit(it913x_module_exit);
+
+MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
+MODULE_DESCRIPTION("it913x USB 2 Driver");
+MODULE_VERSION("1.07");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index 37b146961ae2..b9228240f5ce 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -162,7 +162,7 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
int ret = 0;
if (st->usb_buffer == NULL) {
- st->usb_buffer = kmalloc(512, GFP_KERNEL);
+ st->usb_buffer = kmalloc(64, GFP_KERNEL);
if (st->usb_buffer == NULL) {
info("MEM Error no memory");
return -ENOMEM;
@@ -175,8 +175,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
if (ret < 0)
return -EAGAIN;
- /* the read/write capped at 512 */
- memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen);
+ /* the read/write capped at 64 */
+ memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
@@ -186,8 +186,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
- ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ?
- 512 : rlen , 0x01);
+ ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
+ rlen : 64 , 0x01);
if (rlen > 0)
memcpy(rbuf, buff, rlen);
@@ -333,7 +333,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
if (lme_int->lme_urb == NULL)
return -ENOMEM;
- lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC,
+ lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 128, GFP_ATOMIC,
&lme_int->lme_urb->transfer_dma);
if (lme_int->buffer == NULL)
@@ -343,10 +343,10 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
adap->dev->udev,
usb_rcvintpipe(adap->dev->udev, 0xa),
lme_int->buffer,
- 4096,
+ 128,
lme2510_int_response,
adap,
- 11);
+ 8);
lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -580,7 +580,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct lme2510_state *st = d->priv;
- static u8 obuf[64], ibuf[512];
+ static u8 obuf[64], ibuf[64];
int i, read, read_o;
u16 len;
u8 gate = st->i2c_gate;
@@ -621,7 +621,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
len = msg[i].len+3;
}
- if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) {
+ if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
deb_info(1, "i2c transfer failed.");
return -EAGAIN;
}
@@ -941,7 +941,7 @@ static int lme_name(struct dvb_usb_adapter *adap)
const char *desc = adap->dev->desc->name;
char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
" SHARP:BS2F7HZ0194"};
- char *name = adap->fe->ops.info.name;
+ char *name = adap->fe_adap[0].fe->ops.info.name;
strlcpy(name, desc, 128);
strlcat(name, fe_name[st->tuner_config], 128);
@@ -958,10 +958,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
st->i2c_talk_onoff = 1;
st->i2c_gate = 4;
- adap->fe = dvb_attach(tda10086_attach, &tda10086_config,
+ adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("TUN Found Frontend TDA10086");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 4;
@@ -975,9 +975,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
}
st->i2c_gate = 4;
- adap->fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("FE Found Stv0299");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 5;
@@ -991,9 +991,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
}
st->i2c_gate = 5;
- adap->fe = dvb_attach(stv0288_attach, &lme_config,
+ adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
&adap->dev->i2c_adap);
- if (adap->fe) {
+ if (adap->fe_adap[0].fe) {
info("FE Found Stv0288");
st->i2c_tuner_gate_w = 4;
st->i2c_tuner_gate_r = 5;
@@ -1010,15 +1010,15 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
end: if (ret) {
- if (adap->fe) {
- dvb_frontend_detach(adap->fe);
- adap->fe = NULL;
+ if (adap->fe_adap[0].fe) {
+ dvb_frontend_detach(adap->fe_adap[0].fe);
+ adap->fe_adap[0].fe = NULL;
}
adap->dev->props.rc.core.rc_codes = NULL;
return -ENODEV;
}
- adap->fe->ops.set_voltage = dm04_lme2510_set_voltage;
+ adap->fe_adap[0].fe->ops.set_voltage = dm04_lme2510_set_voltage;
ret = lme_name(adap);
return ret;
}
@@ -1031,17 +1031,17 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
switch (st->tuner_config) {
case TUNER_LG:
- if (dvb_attach(tda826x_attach, adap->fe, 0xc0,
+ if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap, 1))
ret = st->tuner_config;
break;
case TUNER_S7395:
- if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner,
+ if (dvb_attach(ix2505v_attach , adap->fe_adap[0].fe, &lme_tuner,
&adap->dev->i2c_adap))
ret = st->tuner_config;
break;
case TUNER_S0194:
- if (dvb_attach(dvb_pll_attach , adap->fe, 0xc0,
+ if (dvb_attach(dvb_pll_attach , adap->fe_adap[0].fe, 0xc0,
&adap->dev->i2c_adap, DVB_PLL_OPERA1))
ret = st->tuner_config;
break;
@@ -1145,6 +1145,8 @@ static struct dvb_usb_device_properties lme2510_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER|
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1166,6 +1168,7 @@ static struct dvb_usb_device_properties lme2510_properties = {
}
}
}
+ }},
}
},
.rc.core = {
@@ -1193,6 +1196,8 @@ static struct dvb_usb_device_properties lme2510c_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER|
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1214,6 +1219,7 @@ static struct dvb_usb_device_properties lme2510c_properties = {
}
}
}
+ }},
}
},
.rc.core = {
@@ -1241,7 +1247,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d)
void *buffer = NULL;
if (adap != NULL) {
- lme2510_kill_urb(&adap->stream);
+ lme2510_kill_urb(&adap->fe_adap[0].stream);
adap->feedcount = 0;
}
@@ -1255,7 +1261,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d)
if (st->lme_urb != NULL) {
usb_kill_urb(st->lme_urb);
- usb_free_coherent(d->udev, 5000, st->buffer,
+ usb_free_coherent(d->udev, 128, st->buffer,
st->lme_urb->transfer_dma);
info("Interrupt Service Stopped");
}
@@ -1306,5 +1312,5 @@ module_exit(lme2510_module_exit);
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.88");
+MODULE_VERSION("1.90");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c
index 9456792f219b..a1e1287c949e 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/dvb/dvb-usb/m920x.c
@@ -86,12 +86,12 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq)
}
for (i = 0; i < d->props.num_adapters; i++)
- flags |= d->adapter[i].props.caps;
+ flags |= d->adapter[i].props.fe[0].caps;
/* Some devices(Dposh) might crash if we attempt touch at all. */
if (flags & DVB_USB_ADAP_HAS_PID_FILTER) {
for (i = 0; i < d->props.num_adapters; i++) {
- epi = d->adapter[i].props.stream.endpoint - 0x81;
+ epi = d->adapter[i].props.fe[0].stream.endpoint - 0x81;
if (epi < 0 || epi >= M9206_MAX_ADAPTERS) {
printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n");
@@ -292,7 +292,7 @@ static int m920x_update_filters(struct dvb_usb_adapter *adap)
struct m920x_state *m = adap->dev->priv;
int enabled = m->filtering_enabled[adap->id];
int i, ret = 0, filter = 0;
- int ep = adap->props.stream.endpoint;
+ int ep = adap->props.fe[0].stream.endpoint;
for (i = 0; i < M9206_MAX_FILTERS; i++)
if (m->filters[adap->id][i] == 8192)
@@ -501,9 +501,10 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(mt352_attach,
- &m920x_mt352_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach,
+ &m920x_mt352_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -513,9 +514,10 @@ static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(tda10046_attach,
- &m920x_tda10046_08_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(tda10046_attach,
+ &m920x_tda10046_08_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -525,9 +527,10 @@ static int m920x_tda10046_0b_frontend_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if ((adap->fe = dvb_attach(tda10046_attach,
- &m920x_tda10046_0b_config,
- &adap->dev->i2c_adap)) == NULL)
+ adap->fe_adap[0].fe = dvb_attach(tda10046_attach,
+ &m920x_tda10046_0b_config,
+ &adap->dev->i2c_adap);
+ if ((adap->fe_adap[0].fe) == NULL)
return -EIO;
return 0;
@@ -537,7 +540,7 @@ static int m920x_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL)
+ if (dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL)
return -ENODEV;
return 0;
@@ -547,7 +550,7 @@ static int m920x_tda8275_60_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL)
+ if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL)
return -ENODEV;
return 0;
@@ -557,7 +560,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap)
{
deb("%s\n",__func__);
- if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL)
+ if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL)
return -ENODEV;
return 0;
@@ -565,7 +568,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap)
static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(simple_tuner_attach, adap->fe,
+ dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
&adap->dev->i2c_adap, 0x61,
TUNER_PHILIPS_FMD1216ME_MK3);
return 0;
@@ -807,6 +810,9 @@ static struct dvb_usb_device_properties megasky_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -827,6 +833,7 @@ static struct dvb_usb_device_properties megasky_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -851,6 +858,9 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -871,6 +881,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -910,6 +921,9 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 2,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -929,7 +943,11 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.buffersize = 512,
}
}
+ }},
}},{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -949,6 +967,7 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = {
.buffersize = 512,
}
}
+ }},
},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -974,6 +993,8 @@ static struct dvb_usb_device_properties dposh_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
/* Hardware pid filters don't work with this device/firmware */
.frontend_attach = m920x_mt352_frontend_attach,
@@ -989,6 +1010,7 @@ static struct dvb_usb_device_properties dposh_properties = {
}
}
},
+ }},
}},
.i2c_algo = &m920x_i2c_algo,
@@ -1019,6 +1041,9 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = {
.identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
@@ -1041,6 +1066,7 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = {
}
}
},
+ }},
} },
.i2c_algo = &m920x_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
new file mode 100644
index 000000000000..d1f58371c711
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
@@ -0,0 +1,614 @@
+/*
+ * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "mxl111sf-demod.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_demod_debug;
+module_param_named(debug, mxl111sf_demod_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+ if (mxl111sf_demod_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_demod_state {
+ struct mxl111sf_state *mxl_state;
+
+ struct mxl111sf_demod_config *cfg;
+
+ struct dvb_frontend fe;
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
+ u8 addr, u8 *data)
+{
+ return (state->cfg->read_reg) ?
+ state->cfg->read_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
+ u8 addr, u8 data)
+{
+ return (state->cfg->write_reg) ?
+ state->cfg->write_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static
+int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ return (state->cfg->program_regs) ?
+ state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+ -EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+/* TPS */
+
+static
+int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
+ fe_code_rate_t *code_rate)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
+ /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch (val & V6_CODE_RATE_TPS_MASK) {
+ case 0:
+ *code_rate = FEC_1_2;
+ break;
+ case 1:
+ *code_rate = FEC_2_3;
+ break;
+ case 2:
+ *code_rate = FEC_3_4;
+ break;
+ case 3:
+ *code_rate = FEC_5_6;
+ break;
+ case 4:
+ *code_rate = FEC_7_8;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state,
+ fe_modulation_t *constellation)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
+ /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
+ case 0:
+ *constellation = QPSK;
+ break;
+ case 1:
+ *constellation = QAM_16;
+ break;
+ case 2:
+ *constellation = QAM_64;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
+ fe_transmit_mode_t *fft_mode)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
+ /* FFT Mode, 00:2K, 01:8K, 10:4K */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
+ case 0:
+ *fft_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ *fft_mode = TRANSMISSION_MODE_8K;
+ break;
+ case 2:
+ *fft_mode = TRANSMISSION_MODE_4K;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
+ fe_guard_interval_t *guard)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
+ /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_PARAM_GI_MASK) >> 4) {
+ case 0:
+ *guard = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ *guard = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ *guard = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ *guard = GUARD_INTERVAL_1_4;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
+ fe_hierarchy_t *hierarchy)
+{
+ u8 val;
+ int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
+ /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
+ case 0:
+ *hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ *hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ *hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ *hierarchy = HIERARCHY_4;
+ break;
+ }
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+/* LOCKS */
+
+static
+int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
+ int *sync_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *sync_lock = (val & SYNC_LOCK_MASK) >> 4;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
+ int *rs_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
+ int *tps_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
+fail:
+ return ret;
+}
+
+static
+int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
+ int *fec_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
+fail:
+ return ret;
+}
+
+#if 0
+static
+int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
+ int *cp_lock)
+{
+ u8 val = 0;
+ int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+ *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
+fail:
+ return ret;
+}
+#endif
+
+static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
+{
+ return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ int ret = 0;
+
+ struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
+ {0x00, 0xff, 0x01}, /* change page to 1 */
+ {0x40, 0xff, 0x05},
+ {0x40, 0xff, 0x01},
+ {0x41, 0xff, 0xca},
+ {0x41, 0xff, 0xc0},
+ {0x00, 0xff, 0x00}, /* change page to 0 */
+ {0, 0, 0}
+ };
+
+ mxl_dbg("()");
+
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe, param);
+ if (mxl_fail(ret))
+ goto fail;
+ msleep(50);
+ }
+ ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
+ mxl_fail(ret);
+ msleep(50);
+ ret = mxl1x1sf_demod_reset_irq_status(state);
+ mxl_fail(ret);
+ msleep(100);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+/* resets TS Packet error count */
+/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
+static
+int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
+{
+ struct mxl111sf_reg_ctrl_info reset_per_count[] = {
+ {0x20, 0x01, 0x01},
+ {0x20, 0x01, 0x00},
+ {0, 0, 0}
+ };
+ return mxl111sf_demod_program_regs(state, reset_per_count);
+}
+#endif
+
+/* returns TS Packet error count */
+/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
+static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ u32 fec_per_count, fec_per_scale;
+ u8 val;
+ int ret;
+
+ *ucblocks = 0;
+
+ /* FEC_PER_COUNT Register */
+ ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ fec_per_count = val;
+
+ /* FEC_PER_SCALE Register */
+ ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ val &= V6_FEC_PER_SCALE_MASK;
+ val *= 4;
+
+ fec_per_scale = 1 << val;
+
+ fec_per_count *= fec_per_scale;
+
+ *ucblocks = fec_per_count;
+fail:
+ return ret;
+}
+
+#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
+/* FIXME: leaving this enabled breaks the build on some architectures,
+ * and we shouldn't have any floating point math in the kernel, anyway.
+ *
+ * These macros need to be re-written, but it's harmless to simply
+ * return zero for now. */
+#define CALCULATE_BER(avg_errors, count) \
+ ((u32)(avg_errors * 4)/(count*64*188*8))
+#define CALCULATE_SNR(data) \
+ ((u32)((10 * (u32)data / 64) - 2.5))
+#else
+#define CALCULATE_BER(avg_errors, count) 0
+#define CALCULATE_SNR(data) 0
+#endif
+
+static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ u8 val1, val2, val3;
+ int ret;
+
+ *ber = 0;
+
+ ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
+ u16 *snr)
+{
+ u8 val1, val2;
+ int ret;
+
+ *snr = 0;
+
+ ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+ int ret = mxl111sf_demod_calc_snr(state, snr);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *snr /= 10; /* 0.1 dB */
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ int ret, locked, cr_lock, sync_lock, fec_lock;
+
+ *status = 0;
+
+ ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (locked)
+ *status |= FE_HAS_SIGNAL;
+ if (cr_lock)
+ *status |= FE_HAS_CARRIER;
+ if (sync_lock)
+ *status |= FE_HAS_SYNC;
+ if (fec_lock) /* false positives? */
+ *status |= FE_HAS_VITERBI;
+
+ if ((locked) && (cr_lock) && (sync_lock))
+ *status |= FE_HAS_LOCK;
+fail:
+ return ret;
+}
+
+static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ fe_modulation_t constellation;
+ u16 snr;
+
+ mxl111sf_demod_calc_snr(state, &snr);
+ mxl1x1sf_demod_get_tps_constellation(state, &constellation);
+
+ switch (constellation) {
+ case QPSK:
+ *signal_strength = (snr >= 1300) ?
+ min(65535, snr * 44) : snr * 38;
+ break;
+ case QAM_16:
+ *signal_strength = (snr >= 1500) ?
+ min(65535, snr * 38) : snr * 33;
+ break;
+ case QAM_64:
+ *signal_strength = (snr >= 2000) ?
+ min(65535, snr * 29) : snr * 25;
+ break;
+ default:
+ *signal_strength = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+ mxl_dbg("()");
+#if 0
+ p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
+#endif
+ if (fe->ops.tuner_ops.get_bandwidth)
+ fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth);
+ if (fe->ops.tuner_ops.get_frequency)
+ fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
+ mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP);
+ mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP);
+ mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation);
+ mxl1x1sf_demod_get_tps_guard_fft_mode(state,
+ &p->u.ofdm.transmission_mode);
+ mxl1x1sf_demod_get_tps_guard_interval(state,
+ &p->u.ofdm.guard_interval);
+ mxl1x1sf_demod_get_tps_hierarchy(state,
+ &p->u.ofdm.hierarchy_information);
+
+ return 0;
+}
+
+static
+int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void mxl111sf_demod_release(struct dvb_frontend *fe)
+{
+ struct mxl111sf_demod_state *state = fe->demodulator_priv;
+ mxl_dbg("()");
+ kfree(state);
+ fe->demodulator_priv = NULL;
+}
+
+static struct dvb_frontend_ops mxl111sf_demod_ops = {
+
+ .info = {
+ .name = "MaxLinear MxL111SF DVB-T demodulator",
+ .type = FE_OFDM,
+ .frequency_min = 177000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166666,
+ .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_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+ },
+ .release = mxl111sf_demod_release,
+#if 0
+ .init = mxl111sf_init,
+ .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl,
+#endif
+ .set_frontend = mxl111sf_demod_set_frontend,
+ .get_frontend = mxl111sf_demod_get_frontend,
+ .get_tune_settings = mxl111sf_demod_get_tune_settings,
+ .read_status = mxl111sf_demod_read_status,
+ .read_signal_strength = mxl111sf_demod_read_signal_strength,
+ .read_ber = mxl111sf_demod_read_ber,
+ .read_snr = mxl111sf_demod_read_snr,
+ .read_ucblocks = mxl111sf_demod_read_ucblocks,
+};
+
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg)
+{
+ struct mxl111sf_demod_state *state = NULL;
+
+ mxl_dbg("()");
+
+ state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->mxl_state = mxl_state;
+ state->cfg = cfg;
+
+ memcpy(&state->fe.ops, &mxl111sf_demod_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->fe.demodulator_priv = state;
+ return &state->fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
new file mode 100644
index 000000000000..432706ae5274
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
@@ -0,0 +1,55 @@
+/*
+ * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 __MXL111SF_DEMOD_H__
+#define __MXL111SF_DEMOD_H__
+
+#include "dvb_frontend.h"
+#include "mxl111sf.h"
+
+struct mxl111sf_demod_config {
+ int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
+ int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
+ int (*program_regs)(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+};
+
+#if defined(CONFIG_DVB_USB_MXL111SF) || \
+ (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
+extern
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg);
+#else
+static inline
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+ struct mxl111sf_demod_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_USB_MXL111SF */
+
+#endif /* __MXL111SF_DEMOD_H__ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
new file mode 100644
index 000000000000..e4121cb8f5ef
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
@@ -0,0 +1,763 @@
+/*
+ * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "mxl111sf-gpio.h"
+#include "mxl111sf-i2c.h"
+#include "mxl111sf.h"
+
+/* ------------------------------------------------------------------------- */
+
+#define MXL_GPIO_MUX_REG_0 0x84
+#define MXL_GPIO_MUX_REG_1 0x89
+#define MXL_GPIO_MUX_REG_2 0x82
+
+#define MXL_GPIO_DIR_INPUT 0
+#define MXL_GPIO_DIR_OUTPUT 1
+
+
+static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug_adv("(%d, %d)", pin, val);
+
+ if ((pin > 0) && (pin < 8)) {
+ ret = mxl111sf_read_reg(state, 0x19, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (pin - 1));
+ tmp |= (val << (pin - 1));
+ ret = mxl111sf_write_reg(state, 0x19, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ } else if (pin <= 10) {
+ if (pin == 0)
+ pin += 7;
+ ret = mxl111sf_read_reg(state, 0x30, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (pin - 3));
+ tmp |= (val << (pin - 3));
+ ret = mxl111sf_write_reg(state, 0x30, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ } else
+ ret = -EINVAL;
+fail:
+ return ret;
+}
+
+static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug("(0x%02x)", pin);
+
+ *val = 0;
+
+ switch (pin) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ ret = mxl111sf_read_reg(state, 0x23, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> (pin + 4)) & 0x01;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ ret = mxl111sf_read_reg(state, 0x2f, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> pin) & 0x01;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ ret = mxl111sf_read_reg(state, 0x22, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ *val = (tmp >> (pin - 3)) & 0x01;
+ break;
+ default:
+ return -EINVAL; /* invalid pin */
+ }
+fail:
+ return ret;
+}
+
+struct mxl_gpio_cfg {
+ u8 pin;
+ u8 dir;
+ u8 val;
+};
+
+static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state,
+ struct mxl_gpio_cfg *gpio_cfg)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir);
+
+ switch (gpio_cfg->pin) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (gpio_cfg->pin + 4));
+ tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4));
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << gpio_cfg->pin);
+ tmp |= (gpio_cfg->dir << gpio_cfg->pin);
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ tmp &= ~(1 << (gpio_cfg->pin - 3));
+ tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3));
+ ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+ break;
+ default:
+ return -EINVAL; /* invalid pin */
+ }
+
+ ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ?
+ mxl111sf_set_gpo_state(state,
+ gpio_cfg->pin, gpio_cfg->val) :
+ mxl111sf_get_gpi_state(state,
+ gpio_cfg->pin, &gpio_cfg->val);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state,
+ int gpio, int direction, int val)
+{
+ struct mxl_gpio_cfg gpio_config = {
+ .pin = gpio,
+ .dir = direction,
+ .val = val,
+ };
+
+ mxl_debug("(%d, %d, %d)", gpio, direction, val);
+
+ return mxl111sf_config_gpio_pins(state, &gpio_config);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */
+#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */
+#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */
+#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */
+#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */
+#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */
+#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */
+#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */
+#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */
+#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */
+#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */
+#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */
+#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */
+#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */
+#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */
+#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */
+#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */
+#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */
+#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */
+
+int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
+ enum mxl111sf_mux_config pin_mux_config)
+{
+ u8 r12, r15, r17, r18, r3D, r82, r84, r89;
+ int ret;
+
+ mxl_debug("(%d)", pin_mux_config);
+
+ ret = mxl111sf_read_reg(state, 0x17, &r17);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x18, &r18);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x12, &r12);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x15, &r15);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x82, &r82);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x84, &r84);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x89, &r89);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_read_reg(state, 0x3D, &r3D);
+ if (mxl_fail(ret))
+ goto fail;
+
+ switch (pin_mux_config) {
+ case PIN_MUX_TS_OUT_PARALLEL:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 1 */
+ r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 1 */
+ r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 1 */
+ r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0xF */
+ r84 |= 0xF0;
+ /* mdat_en_ctrl[7:4] = 0xF */
+ r89 |= 0xF0;
+ break;
+ case PIN_MUX_TS_OUT_SERIAL:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 1 */
+ r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 1 */
+ r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 1 */
+ r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0xF */
+ r84 |= 0xF0;
+ /* mdat_en_ctrl[7:4] = 0xF */
+ r89 |= 0xF0;
+ break;
+ case PIN_MUX_GPIO_MODE:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SERIAL_IN_MODE_0:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SERIAL_IN_MODE_1:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 1 */
+ r3D |= PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SPI_IN_MODE_1:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 1 */
+ r3D |= PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 1 */
+ r3D |= PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_SPI_IN_MODE_0:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 1 */
+ r18 |= PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 1 */
+ r3D |= PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_TS_PARALLEL_IN:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 1 */
+ r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_BT656_I2S_MODE:
+ /* mpeg_mode = 0 */
+ r17 &= ~PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 1 */
+ r12 |= PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 1 */
+ r15 |= PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ case PIN_MUX_DEFAULT:
+ default:
+ /* mpeg_mode = 1 */
+ r17 |= PIN_MUX_MPEG_MODE_MASK;
+ /* mpeg_par_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
+ /* mpeg_ser_en = 0 */
+ r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
+ /* mpg_in_mux = 0 */
+ r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
+ /* bt656_enable = 0 */
+ r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
+ /* i2s_enable = 0 */
+ r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
+ /* spi_mode = 0 */
+ r3D &= ~PIN_MUX_SPI_MODE_MASK;
+ /* mclk_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
+ /* mperr_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
+ /* mdval_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
+ /* mpsyn_en_ctrl = 0 */
+ r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
+ /* mdat_en_ctrl[3:0] = 0x0 */
+ r84 &= 0x0F;
+ /* mdat_en_ctrl[7:4] = 0x0 */
+ r89 &= 0x0F;
+ break;
+ }
+
+ ret = mxl111sf_write_reg(state, 0x17, r17);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x18, r18);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x12, r12);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x15, r15);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x82, r82);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x84, r84);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x89, r89);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x3D, r3D);
+ if (mxl_fail(ret))
+ goto fail;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val);
+}
+
+static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state)
+{
+ u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */
+ int i, ret;
+
+ mxl_debug("()");
+
+ for (i = 3; i < 8; i++) {
+ ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01);
+ if (mxl_fail(ret))
+ break;
+ }
+
+ return ret;
+}
+
+#define PCA9534_I2C_ADDR (0x40 >> 1)
+static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ u8 w[2] = { 1, 0 };
+ u8 r = 0;
+ struct i2c_msg msg[] = {
+ { .addr = PCA9534_I2C_ADDR,
+ .flags = 0, .buf = w, .len = 1 },
+ { .addr = PCA9534_I2C_ADDR,
+ .flags = I2C_M_RD, .buf = &r, .len = 1 },
+ };
+
+ mxl_debug("(%d, %d)", gpio, val);
+
+ /* read current GPIO levels from flip-flop */
+ i2c_transfer(&state->d->i2c_adap, msg, 2);
+
+ /* prepare write buffer with current GPIO levels */
+ msg[0].len = 2;
+#if 0
+ w[0] = 1;
+#endif
+ w[1] = r;
+
+ /* clear the desired GPIO */
+ w[1] &= ~(1 << gpio);
+
+ /* set the desired GPIO value */
+ w[1] |= ((val ? 1 : 0) << gpio);
+
+ /* write new GPIO levels to flip-flop */
+ i2c_transfer(&state->d->i2c_adap, &msg[0], 1);
+
+ return 0;
+}
+
+static int pca9534_init_port_expander(struct mxl111sf_state *state)
+{
+ u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */
+
+ struct i2c_msg msg = {
+ .addr = PCA9534_I2C_ADDR,
+ .flags = 0, .buf = w, .len = 2
+ };
+
+ mxl_debug("()");
+
+ i2c_transfer(&state->d->i2c_adap, &msg, 1);
+
+ /* configure all pins as outputs */
+ w[0] = 3;
+ w[1] = 0;
+
+ i2c_transfer(&state->d->i2c_adap, &msg, 1);
+
+ return 0;
+}
+
+int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val)
+{
+ mxl_debug("(%d, %d)", gpio, val);
+
+ switch (state->gpio_port_expander) {
+ default:
+ mxl_printk(KERN_ERR,
+ "gpio_port_expander undefined, assuming PCA9534");
+ /* fall-thru */
+ case mxl111sf_PCA9534:
+ return pca9534_set_gpio(state, gpio, val);
+ case mxl111sf_gpio_hw:
+ return mxl111sf_hw_set_gpio(state, gpio, val);
+ }
+}
+
+static int mxl111sf_probe_port_expander(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 w = 1;
+ u8 r = 0;
+ struct i2c_msg msg[] = {
+ { .flags = 0, .buf = &w, .len = 1 },
+ { .flags = I2C_M_RD, .buf = &r, .len = 1 },
+ };
+
+ mxl_debug("()");
+
+ msg[0].addr = 0x70 >> 1;
+ msg[1].addr = 0x70 >> 1;
+
+ /* read current GPIO levels from flip-flop */
+ ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
+ if (ret == 2) {
+ state->port_expander_addr = msg[0].addr;
+ state->gpio_port_expander = mxl111sf_PCA9534;
+ mxl_debug("found port expander at 0x%02x",
+ state->port_expander_addr);
+ return 0;
+ }
+
+ msg[0].addr = 0x40 >> 1;
+ msg[1].addr = 0x40 >> 1;
+
+ ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
+ if (ret == 2) {
+ state->port_expander_addr = msg[0].addr;
+ state->gpio_port_expander = mxl111sf_PCA9534;
+ mxl_debug("found port expander at 0x%02x",
+ state->port_expander_addr);
+ return 0;
+ }
+ state->port_expander_addr = 0xff;
+ state->gpio_port_expander = mxl111sf_gpio_hw;
+ mxl_debug("using hardware gpio");
+ return 0;
+}
+
+int mxl111sf_init_port_expander(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ if (0x00 == state->port_expander_addr)
+ mxl111sf_probe_port_expander(state);
+
+ switch (state->gpio_port_expander) {
+ default:
+ mxl_printk(KERN_ERR,
+ "gpio_port_expander undefined, assuming PCA9534");
+ /* fall-thru */
+ case mxl111sf_PCA9534:
+ return pca9534_init_port_expander(state);
+ case mxl111sf_gpio_hw:
+ return mxl111sf_hw_gpio_initialize(state);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode)
+{
+/* GPO:
+ * 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0
+ * 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0
+ * 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0
+ * 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0
+ * 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0
+ */
+ mxl_debug("(%d)", mode);
+
+ switch (mode) {
+ case MXL111SF_GPIO_MOD_MH:
+ mxl111sf_set_gpio(state, 4, 0);
+ mxl111sf_set_gpio(state, 5, 0);
+ msleep(50);
+ mxl111sf_set_gpio(state, 7, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 6, 1);
+ msleep(50);
+
+ mxl111sf_set_gpio(state, 3, 0);
+ break;
+ case MXL111SF_GPIO_MOD_ATSC:
+ mxl111sf_set_gpio(state, 6, 0);
+ mxl111sf_set_gpio(state, 7, 0);
+ msleep(50);
+ mxl111sf_set_gpio(state, 5, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 4, 1);
+ msleep(50);
+ mxl111sf_set_gpio(state, 3, 1);
+ break;
+ default: /* DVBT / STANDBY */
+ mxl111sf_init_port_expander(state);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
new file mode 100644
index 000000000000..0220f54299a5
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
@@ -0,0 +1,56 @@
+/*
+ * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 _DVB_USB_MXL111SF_GPIO_H_
+#define _DVB_USB_MXL111SF_GPIO_H_
+
+#include "mxl111sf.h"
+
+int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val);
+int mxl111sf_init_port_expander(struct mxl111sf_state *state);
+
+#define MXL111SF_GPIO_MOD_DVBT 0
+#define MXL111SF_GPIO_MOD_MH 1
+#define MXL111SF_GPIO_MOD_ATSC 2
+int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode);
+
+enum mxl111sf_mux_config {
+ PIN_MUX_DEFAULT = 0,
+ PIN_MUX_TS_OUT_PARALLEL,
+ PIN_MUX_TS_OUT_SERIAL,
+ PIN_MUX_GPIO_MODE,
+ PIN_MUX_TS_SERIAL_IN_MODE_0,
+ PIN_MUX_TS_SERIAL_IN_MODE_1,
+ PIN_MUX_TS_SPI_IN_MODE_0,
+ PIN_MUX_TS_SPI_IN_MODE_1,
+ PIN_MUX_TS_PARALLEL_IN,
+ PIN_MUX_BT656_I2S_MODE,
+};
+
+int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
+ enum mxl111sf_mux_config pin_mux_config);
+
+#endif /* _DVB_USB_MXL111SF_GPIO_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
new file mode 100644
index 000000000000..2e8c288258a9
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
@@ -0,0 +1,851 @@
+/*
+ * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "mxl111sf-i2c.h"
+#include "mxl111sf.h"
+
+/* SW-I2C ----------------------------------------------------------------- */
+
+#define SW_I2C_ADDR 0x1a
+#define SW_I2C_EN 0x02
+#define SW_SCL_OUT 0x04
+#define SW_SDA_OUT 0x08
+#define SW_SDA_IN 0x04
+
+#define SW_I2C_BUSY_ADDR 0x2f
+#define SW_I2C_BUSY 0x02
+
+static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state,
+ u8 byte)
+{
+ int i, ret;
+ u8 data = 0;
+
+ mxl_i2c("(0x%02x)", byte);
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ for (i = 0; i < 8; i++) {
+
+ data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | data);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+
+ /* last bit was 0 so we need to release SDA */
+ if (!(byte & 1)) {
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+
+ /* CLK high for ACK readback */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* drop the CLK after getting ACK, SDA will go high right away */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (data & SW_SDA_IN)
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state,
+ u8 *pbyte)
+{
+ int i, ret;
+ u8 byte = 0;
+ u8 data = 0;
+
+ mxl_i2c("()");
+
+ *pbyte = 0;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ for (i = 0; i < 8; i++) {
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN |
+ SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (data & SW_SDA_IN)
+ byte |= (0x80 >> i);
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+ }
+ *pbyte = byte;
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_start(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN); /* start */
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_stop(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN); /* stop */
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_SCL_OUT | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_ack(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 b = 0;
+
+ mxl_i2c("()");
+
+ ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* pull SDA low */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl111sf_i2c_nack(struct mxl111sf_state *state)
+{
+ int ret;
+
+ mxl_i2c("()");
+
+ /* SDA high to signal last byte read from slave */
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
+ 0x10 | SW_I2C_EN | SW_SDA_OUT);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state,
+ struct i2c_msg *msg)
+{
+ int i, ret;
+
+ mxl_i2c("()");
+
+ if (msg->flags & I2C_M_RD) {
+
+ ret = mxl111sf_i2c_start(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ (msg->addr << 1) | 0x01);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ for (i = 0; i < msg->len; i++) {
+ ret = mxl111sf_i2c_bitbang_recvbyte(state,
+ &msg->buf[i]);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ if (i < msg->len - 1)
+ mxl111sf_i2c_ack(state);
+ }
+
+ mxl111sf_i2c_nack(state);
+
+ ret = mxl111sf_i2c_stop(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ } else {
+
+ ret = mxl111sf_i2c_start(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ (msg->addr << 1) & 0xfe);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+
+ for (i = 0; i < msg->len; i++) {
+ ret = mxl111sf_i2c_bitbang_sendbyte(state,
+ msg->buf[i]);
+ if (mxl_fail(ret)) {
+ mxl111sf_i2c_stop(state);
+ goto fail;
+ }
+ }
+
+ /* FIXME: we only want to do this on the last transaction */
+ mxl111sf_i2c_stop(state);
+ }
+fail:
+ return ret;
+}
+
+/* HW-I2C ----------------------------------------------------------------- */
+
+#define USB_WRITE_I2C_CMD 0x99
+#define USB_READ_I2C_CMD 0xdd
+#define USB_END_I2C_CMD 0xfe
+
+#define USB_WRITE_I2C_CMD_LEN 26
+#define USB_READ_I2C_CMD_LEN 24
+
+#define I2C_MUX_REG 0x30
+#define I2C_CONTROL_REG 0x00
+#define I2C_SLAVE_ADDR_REG 0x08
+#define I2C_DATA_REG 0x0c
+#define I2C_INT_STATUS_REG 0x10
+
+static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
+ u8 index, u8 *wdata)
+{
+ int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ &wdata[1], 25, NULL, 0);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+static int mxl111sf_i2c_get_data(struct mxl111sf_state *state,
+ u8 index, u8 *wdata, u8 *rdata)
+{
+ int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ &wdata[1], 25, rdata, 24);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state)
+{
+ u8 status = 0;
+ u8 buf[26];
+
+ mxl_i2c_adv("()");
+
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ buf[2] = I2C_INT_STATUS_REG;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+
+ buf[5] = USB_END_I2C_CMD;
+
+ mxl111sf_i2c_get_data(state, 0, buf, buf);
+
+ if (buf[1] & 0x04)
+ status = 1;
+
+ return status;
+}
+
+static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state)
+{
+ u8 status = 0;
+ u8 buf[26];
+
+ mxl_i2c("()");
+
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+
+ buf[5] = I2C_INT_STATUS_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+ buf[8] = USB_END_I2C_CMD;
+
+ mxl111sf_i2c_get_data(state, 0, buf, buf);
+
+ if (0x08 == (buf[1] & 0x08))
+ status = 1;
+
+ if ((buf[5] & 0x02) == 0x02)
+ mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */
+
+ return status;
+}
+
+static int mxl111sf_i2c_readagain(struct mxl111sf_state *state,
+ u8 count, u8 *rbuf)
+{
+ u8 i2c_w_data[26];
+ u8 i2c_r_data[24];
+ u8 i = 0;
+ u8 fifo_status = 0;
+ int ret;
+ int status = 0;
+
+ mxl_i2c("read %d bytes", count);
+
+ while ((fifo_status == 0) && (i++ < 5))
+ fifo_status = mxl111sf_i2c_check_fifo(state);
+
+ i2c_w_data[0] = 0xDD;
+ i2c_w_data[1] = 0x00;
+
+ for (i = 2; i < 26; i++)
+ i2c_w_data[i] = 0xFE;
+
+ for (i = 0; i < count; i++) {
+ i2c_w_data[2+(i*3)] = 0x0C;
+ i2c_w_data[3+(i*3)] = 0x00;
+ i2c_w_data[4+(i*3)] = 0x00;
+ }
+
+ ret = mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data);
+
+ /* Check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("error!");
+ } else {
+ for (i = 0; i < count; i++) {
+ rbuf[i] = i2c_r_data[(i*3)+1];
+ mxl_i2c("%02x\t %02x",
+ i2c_r_data[(i*3)+1],
+ i2c_r_data[(i*3)+2]);
+ }
+
+ status = 1;
+ }
+
+ return status;
+}
+
+#define HWI2C400 1
+static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state,
+ struct i2c_msg *msg)
+{
+ int i, k, ret = 0;
+ u16 index = 0;
+ u8 buf[26];
+ u8 i2c_r_data[24];
+ u16 block_len;
+ u16 left_over_len;
+ u8 rd_status[8];
+ u8 ret_status;
+ u8 readbuff[26];
+
+ mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d",
+ msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0,
+ (!(msg->flags & I2C_M_RD)) ? msg->len : 0);
+
+ for (index = 0; index < 26; index++)
+ buf[index] = USB_END_I2C_CMD;
+
+ /* command to indicate data payload is destined for I2C interface */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* enable I2C interface */
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x80;
+ buf[4] = 0x00;
+
+ /* enable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x81;
+ buf[7] = 0x00;
+
+ /* set Timeout register on I2C interface */
+ buf[8] = 0x14;
+ buf[9] = 0xff;
+ buf[10] = 0x00;
+#if 0
+ /* enable Interrupts on I2C interface */
+ buf[8] = 0x24;
+ buf[9] = 0xF7;
+ buf[10] = 0x00;
+#endif
+ buf[11] = 0x24;
+ buf[12] = 0xF7;
+ buf[13] = 0x00;
+
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* write data on I2C bus */
+ if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) {
+ mxl_i2c("%d\t%02x", msg->len, msg->buf[0]);
+
+ /* control register on I2C interface to initialize I2C bus */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x5E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ /* I2C Slave device Address */
+ buf[5] = I2C_SLAVE_ADDR_REG;
+ buf[6] = (msg->addr);
+ buf[7] = 0x00;
+ buf[8] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for slave device status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* I2C interface can do I2C operations in block of 8 bytes of
+ I2C data. calculation to figure out number of blocks of i2c
+ data required to program */
+ block_len = (msg->len / 8);
+ left_over_len = (msg->len % 8);
+ index = 0;
+
+ mxl_i2c("block_len %d, left_over_len %d",
+ block_len, left_over_len);
+
+ for (index = 0; index < block_len; index++) {
+ for (i = 0; i < 8; i++) {
+ /* write data on I2C interface */
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = msg->buf[(index*8)+i];
+ buf[4+(i*3)] = 0x00;
+ }
+
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ }
+
+ if (left_over_len) {
+ for (k = 0; k < 26; k++)
+ buf[k] = USB_END_I2C_CMD;
+
+ buf[0] = 0x99;
+ buf[1] = 0x00;
+
+ for (i = 0; i < left_over_len; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = msg->buf[(index*8)+i];
+ mxl_i2c("index = %d %d data %d",
+ index, i, msg->buf[(index*8)+i]);
+ buf[4+(i*3)] = 0x00;
+ }
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK writing slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ }
+
+ /* issue I2C STOP after write */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x4E;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ }
+
+ /* read data from I2C bus */
+ if ((msg->flags & I2C_M_RD) && (msg->len > 0)) {
+ mxl_i2c("read buf len %d", msg->len);
+
+ /* command to indicate data payload is
+ destined for I2C interface */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xDF;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ /* I2C xfer length */
+ buf[5] = 0x14;
+ buf[6] = (msg->len & 0xFF);
+ buf[7] = 0;
+
+ /* I2C slave device Address */
+ buf[8] = I2C_SLAVE_ADDR_REG;
+ buf[9] = msg->addr;
+ buf[10] = 0x00;
+ buf[11] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* I2C interface can do I2C operations in block of 8 bytes of
+ I2C data. calculation to figure out number of blocks of
+ i2c data required to program */
+ block_len = ((msg->len) / 8);
+ left_over_len = ((msg->len) % 8);
+ index = 0;
+
+ mxl_i2c("block_len %d, left_over_len %d",
+ block_len, left_over_len);
+
+ /* command to read data from I2C interface */
+ buf[0] = USB_READ_I2C_CMD;
+ buf[1] = 0x00;
+
+ for (index = 0; index < block_len; index++) {
+ /* setup I2C read request packet on I2C interface */
+ for (i = 0; i < 8; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = 0x00;
+ buf[4+(i*3)] = 0x00;
+ }
+
+ ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* copy data from i2c data payload to read buffer */
+ for (i = 0; i < 8; i++) {
+ rd_status[i] = i2c_r_data[(i*3)+2];
+
+ if (rd_status[i] == 0x04) {
+ if (i < 7) {
+ mxl_i2c("i2c fifo empty!"
+ " @ %d", i);
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ /* read again */
+ ret_status =
+ mxl111sf_i2c_readagain(
+ state, 8-(i+1),
+ readbuff);
+ if (ret_status == 1) {
+ for (k = 0;
+ k < 8-(i+1);
+ k++) {
+
+ msg->buf[(index*8)+(k+i+1)] =
+ readbuff[k];
+ mxl_i2c("read data: %02x\t %02x",
+ msg->buf[(index*8)+(k+i)],
+ (index*8)+(k+i));
+ mxl_i2c("read data: %02x\t %02x",
+ msg->buf[(index*8)+(k+i+1)],
+ readbuff[k]);
+
+ }
+ goto stop_copy;
+ } else {
+ mxl_i2c("readagain "
+ "ERROR!");
+ }
+ } else {
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ }
+ } else {
+ msg->buf[(index*8)+i] =
+ i2c_r_data[(i*3)+1];
+ }
+ }
+stop_copy:
+ ;
+
+ }
+
+ if (left_over_len) {
+ for (k = 0; k < 26; k++)
+ buf[k] = USB_END_I2C_CMD;
+
+ buf[0] = 0xDD;
+ buf[1] = 0x00;
+
+ for (i = 0; i < left_over_len; i++) {
+ buf[2+(i*3)] = I2C_DATA_REG;
+ buf[3+(i*3)] = 0x00;
+ buf[4+(i*3)] = 0x00;
+ }
+ ret = mxl111sf_i2c_get_data(state, 0, buf,
+ i2c_r_data);
+
+ /* check for I2C NACK status */
+ if (mxl111sf_i2c_check_status(state) == 1) {
+ mxl_i2c("NACK reading slave address %02x",
+ msg->addr);
+
+ /* if NACK, stop I2C bus and exit */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+ ret = -EIO;
+ goto exit;
+ }
+
+ for (i = 0; i < left_over_len; i++) {
+ msg->buf[(block_len*8)+i] =
+ i2c_r_data[(i*3)+1];
+ mxl_i2c("read data: %02x\t %02x",
+ i2c_r_data[(i*3)+1],
+ i2c_r_data[(i*3)+2]);
+ }
+ }
+
+ /* indicate I2C interface to issue NACK
+ after next I2C read op */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* control register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0x17;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ buf[5] = USB_END_I2C_CMD;
+ ret = mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* control register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xC7;
+ buf[4] = (HWI2C400) ? 0x03 : 0x0D;
+
+ }
+exit:
+ /* STOP and disable I2C MUX */
+ buf[0] = USB_WRITE_I2C_CMD;
+ buf[1] = 0x00;
+
+ /* de-initilize I2C BUS */
+ buf[5] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* Control Register */
+ buf[2] = I2C_CONTROL_REG;
+ buf[3] = 0xDF;
+ buf[4] = 0x03;
+
+ /* disable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ /* de-initilize I2C BUS */
+ buf[8] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ /* disable I2C interface */
+ buf[2] = I2C_MUX_REG;
+ buf[3] = 0x81;
+ buf[4] = 0x00;
+
+ /* disable I2C interface */
+ buf[5] = I2C_MUX_REG;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ /* disable I2C interface */
+ buf[8] = I2C_MUX_REG;
+ buf[9] = 0x00;
+ buf[10] = 0x00;
+
+ buf[11] = USB_END_I2C_CMD;
+ mxl111sf_i2c_send_data(state, 0, buf);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct mxl111sf_state *state = d->priv;
+ int hwi2c = (state->chip_rev > MXL111SF_V6);
+ int i, ret;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ ret = (hwi2c) ?
+ mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) :
+ mxl111sf_i2c_sw_xfer_msg(state, &msg[i]);
+ if (mxl_fail(ret)) {
+ mxl_debug_adv("failed with error %d on i2c "
+ "transaction %d of %d, %sing %d bytes "
+ "to/from 0x%02x", ret, i+1, num,
+ (msg[i].flags & I2C_M_RD) ?
+ "read" : "writ",
+ msg[i].len, msg[i].addr);
+
+ break;
+ }
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return i == num ? num : -EREMOTEIO;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
new file mode 100644
index 000000000000..a57a45ffb9e4
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
@@ -0,0 +1,35 @@
+/*
+ * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 _DVB_USB_MXL111SF_I2C_H_
+#define _DVB_USB_MXL111SF_I2C_H_
+
+#include <linux/i2c.h>
+
+int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num);
+
+#endif /* _DVB_USB_MXL111SF_I2C_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c
new file mode 100644
index 000000000000..91dc1fc2825b
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c
@@ -0,0 +1,342 @@
+/*
+ * mxl111sf-phy.c - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "mxl111sf-phy.h"
+#include "mxl111sf-reg.h"
+
+int mxl111sf_init_tuner_demod(struct mxl111sf_state *state)
+{
+ struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = {
+ {0x07, 0xff, 0x0c},
+ {0x58, 0xff, 0x9d},
+ {0x09, 0xff, 0x00},
+ {0x06, 0xff, 0x06},
+ {0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */
+ {0x8d, 0x01, 0x01}, /* NEGATE_Q */
+ {0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */
+ {0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */
+ {0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */
+ {0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */
+ {0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */
+ {0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */
+ {0x88, 0xff, 0xf0}, /* INF_THD = 240 */
+ {0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */
+ {0x00, 0xff, 0x01}, /* Change to page 1 */
+ {0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */
+ {0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */
+ {0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */
+ {0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */
+ {0x00, 0xff, 0x00}, /* Change to page 0 */
+ {0, 0, 0}
+ };
+
+ mxl_debug("()");
+
+ return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default);
+}
+
+int mxl1x1sf_soft_reset(struct mxl111sf_state *state)
+{
+ int ret;
+ mxl_debug("()");
+
+ ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode)
+{
+ int ret;
+
+ mxl_debug("(%s)", MXL_SOC_MODE == mode ?
+ "MXL_SOC_MODE" : "MXL_TUNER_MODE");
+
+ /* set device mode */
+ ret = mxl111sf_write_reg(state, 0x03,
+ MXL_SOC_MODE == mode ? 0x01 : 0x00);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg_mask(state,
+ 0x7d, 0x40, MXL_SOC_MODE == mode ?
+ 0x00 : /* enable impulse noise filter,
+ INF_BYP = 0 */
+ 0x40); /* disable impulse noise filter,
+ INF_BYP = 1 */
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->device_mode = mode;
+fail:
+ return ret;
+}
+
+/* power up tuner */
+int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff)
+{
+ mxl_debug("(%d)", onoff);
+
+ return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00);
+}
+
+int mxl111sf_disable_656_port(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00);
+}
+
+int mxl111sf_enable_usb_output(struct mxl111sf_state *state)
+{
+ mxl_debug("()");
+
+ return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00);
+}
+
+/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */
+int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
+ unsigned int parallel_serial,
+ unsigned int msb_lsb_1st,
+ unsigned int clock_phase,
+ unsigned int mpeg_valid_pol,
+ unsigned int mpeg_sync_pol)
+{
+ int ret;
+ u8 mode, tmp;
+
+ mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st,
+ clock_phase, mpeg_valid_pol, mpeg_sync_pol);
+
+ /* Enable PIN MUX */
+ ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX);
+ mxl_fail(ret);
+
+ /* Configure MPEG Clock phase */
+ mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode);
+
+ if (clock_phase == TSIF_NORMAL)
+ mode &= ~V6_INVERTED_CLK_PHASE;
+ else
+ mode |= V6_INVERTED_CLK_PHASE;
+
+ ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode);
+ mxl_fail(ret);
+
+ /* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity
+ * Get current configuration */
+ ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode);
+ mxl_fail(ret);
+
+ /* Data Input mode */
+ if (parallel_serial == TSIF_INPUT_PARALLEL) {
+ /* Disable serial mode */
+ mode &= ~V6_MPEG_IN_DATA_SERIAL;
+
+ /* Enable Parallel mode */
+ mode |= V6_MPEG_IN_DATA_PARALLEL;
+ } else {
+ /* Disable Parallel mode */
+ mode &= ~V6_MPEG_IN_DATA_PARALLEL;
+
+ /* Enable Serial Mode */
+ mode |= V6_MPEG_IN_DATA_SERIAL;
+
+ /* If serial interface is chosen, configure
+ MSB or LSB order in transmission */
+ ret = mxl111sf_read_reg(state,
+ V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
+ &tmp);
+ mxl_fail(ret);
+
+ if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED)
+ tmp |= V6_MPEG_SER_MSB_FIRST;
+ else
+ tmp &= ~V6_MPEG_SER_MSB_FIRST;
+
+ ret = mxl111sf_write_reg(state,
+ V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
+ tmp);
+ mxl_fail(ret);
+ }
+
+ /* MPEG Sync polarity */
+ if (mpeg_sync_pol == TSIF_NORMAL)
+ mode &= ~V6_INVERTED_MPEG_SYNC;
+ else
+ mode |= V6_INVERTED_MPEG_SYNC;
+
+ /* MPEG Valid polarity */
+ if (mpeg_valid_pol == 0)
+ mode &= ~V6_INVERTED_MPEG_VALID;
+ else
+ mode |= V6_INVERTED_MPEG_VALID;
+
+ ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size)
+{
+ static struct mxl111sf_reg_ctrl_info init_i2s[] = {
+ {0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */
+ {0x15, 0x60, 0x60}, /* Enable I2S */
+ {0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB,
+ Inverted 656 Clock, I2S_SOFT_RESET,
+ 0 : Normal operation, 1 : Reset State */
+#if 0
+ {0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */
+#endif
+ {0x00, 0xff, 0x02}, /* Change to Control Page */
+ {0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */
+ {0x00, 0xff, 0x00},
+ {0, 0, 0}
+ };
+ int ret;
+
+ mxl_debug("(0x%02x)", sample_size);
+
+ ret = mxl111sf_ctrl_program_regs(state, init_i2s);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl111sf_disable_i2s_port(struct mxl111sf_state *state)
+{
+ static struct mxl111sf_reg_ctrl_info disable_i2s[] = {
+ {0x15, 0x40, 0x00},
+ {0, 0, 0}
+ };
+
+ mxl_debug("()");
+
+ return mxl111sf_ctrl_program_regs(state, disable_i2s);
+}
+
+int mxl111sf_config_i2s(struct mxl111sf_state *state,
+ u8 msb_start_pos, u8 data_width)
+{
+ int ret;
+ u8 tmp;
+
+ mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width);
+
+ ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ tmp &= 0xe0;
+ tmp |= msb_start_pos;
+ ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp);
+ if (mxl_fail(ret))
+ goto fail;
+
+ tmp &= 0xe0;
+ tmp |= data_width;
+ ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff)
+{
+ u8 val;
+ int ret;
+
+ mxl_debug("(%d)", onoff);
+
+ ret = mxl111sf_write_reg(state, 0x00, 0x02);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (onoff)
+ val |= 0x04;
+ else
+ val &= ~0x04;
+
+ ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_write_reg(state, 0x00, 0x00);
+ if (mxl_fail(ret))
+ goto fail;
+fail:
+ return ret;
+}
+
+int mxl111sf_idac_config(struct mxl111sf_state *state,
+ u8 control_mode, u8 current_setting,
+ u8 current_value, u8 hysteresis_value)
+{
+ int ret;
+ u8 val;
+ /* current value will be set for both automatic & manual IDAC control */
+ val = current_value;
+
+ if (control_mode == IDAC_MANUAL_CONTROL) {
+ /* enable manual control of IDAC */
+ val |= IDAC_MANUAL_CONTROL_BIT_MASK;
+
+ if (current_setting == IDAC_CURRENT_SINKING_ENABLE)
+ /* enable current sinking in manual mode */
+ val |= IDAC_CURRENT_SINKING_BIT_MASK;
+ else
+ /* disable current sinking in manual mode */
+ val &= ~IDAC_CURRENT_SINKING_BIT_MASK;
+ } else {
+ /* disable manual control of IDAC */
+ val &= ~IDAC_MANUAL_CONTROL_BIT_MASK;
+
+ /* set hysteresis value reg: 0x0B<5:0> */
+ ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG,
+ (hysteresis_value & 0x3F));
+ }
+
+ ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val);
+
+ return val;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h
new file mode 100644
index 000000000000..f0756071d347
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h
@@ -0,0 +1,53 @@
+/*
+ * mxl111sf-phy.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 _DVB_USB_MXL111SF_PHY_H_
+#define _DVB_USB_MXL111SF_PHY_H_
+
+#include "mxl111sf.h"
+
+int mxl1x1sf_soft_reset(struct mxl111sf_state *state);
+int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode);
+int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff);
+int mxl111sf_disable_656_port(struct mxl111sf_state *state);
+int mxl111sf_init_tuner_demod(struct mxl111sf_state *state);
+int mxl111sf_enable_usb_output(struct mxl111sf_state *state);
+int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
+ unsigned int parallel_serial,
+ unsigned int msb_lsb_1st,
+ unsigned int clock_phase,
+ unsigned int mpeg_valid_pol,
+ unsigned int mpeg_sync_pol);
+int mxl111sf_config_i2s(struct mxl111sf_state *state,
+ u8 msb_start_pos, u8 data_width);
+int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size);
+int mxl111sf_disable_i2s_port(struct mxl111sf_state *state);
+int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff);
+int mxl111sf_idac_config(struct mxl111sf_state *state,
+ u8 control_mode, u8 current_setting,
+ u8 current_value, u8 hysteresis_value);
+
+#endif /* _DVB_USB_MXL111SF_PHY_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h
new file mode 100644
index 000000000000..17831b0fb9db
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h
@@ -0,0 +1,179 @@
+/*
+ * mxl111sf-reg.h - driver for the MaxLinear MXL111SF
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 _DVB_USB_MXL111SF_REG_H_
+#define _DVB_USB_MXL111SF_REG_H_
+
+#define CHIP_ID_REG 0xFC
+#define TOP_CHIP_REV_ID_REG 0xFA
+
+#define V6_SNR_RB_LSB_REG 0x27
+#define V6_SNR_RB_MSB_REG 0x28
+
+#define V6_N_ACCUMULATE_REG 0x11
+#define V6_RS_AVG_ERRORS_LSB_REG 0x2C
+#define V6_RS_AVG_ERRORS_MSB_REG 0x2D
+
+#define V6_IRQ_STATUS_REG 0x24
+#define IRQ_MASK_FEC_LOCK 0x10
+
+#define V6_SYNC_LOCK_REG 0x28
+#define SYNC_LOCK_MASK 0x10
+
+#define V6_RS_LOCK_DET_REG 0x28
+#define RS_LOCK_DET_MASK 0x08
+
+#define V6_INITACQ_NODETECT_REG 0x20
+#define V6_FORCE_NFFT_CPSIZE_REG 0x20
+
+#define V6_CODE_RATE_TPS_REG 0x29
+#define V6_CODE_RATE_TPS_MASK 0x07
+
+
+#define V6_CP_LOCK_DET_REG 0x28
+#define V6_CP_LOCK_DET_MASK 0x04
+
+#define V6_TPS_HIERACHY_REG 0x29
+#define V6_TPS_HIERARCHY_INFO_MASK 0x40
+
+#define V6_MODORDER_TPS_REG 0x2A
+#define V6_PARAM_CONSTELLATION_MASK 0x30
+
+#define V6_MODE_TPS_REG 0x2A
+#define V6_PARAM_FFT_MODE_MASK 0x0C
+
+
+#define V6_CP_TPS_REG 0x29
+#define V6_PARAM_GI_MASK 0x30
+
+#define V6_TPS_LOCK_REG 0x2A
+#define V6_PARAM_TPS_LOCK_MASK 0x40
+
+#define V6_FEC_PER_COUNT_REG 0x2E
+#define V6_FEC_PER_SCALE_REG 0x2B
+#define V6_FEC_PER_SCALE_MASK 0x03
+#define V6_FEC_PER_CLR_REG 0x20
+#define V6_FEC_PER_CLR_MASK 0x01
+
+#define V6_PIN_MUX_MODE_REG 0x1B
+#define V6_ENABLE_PIN_MUX 0x1E
+
+#define V6_I2S_NUM_SAMPLES_REG 0x16
+
+#define V6_MPEG_IN_CLK_INV_REG 0x17
+#define V6_MPEG_IN_CTRL_REG 0x18
+
+#define V6_INVERTED_CLK_PHASE 0x20
+#define V6_MPEG_IN_DATA_PARALLEL 0x01
+#define V6_MPEG_IN_DATA_SERIAL 0x02
+
+#define V6_INVERTED_MPEG_SYNC 0x04
+#define V6_INVERTED_MPEG_VALID 0x08
+
+#define TSIF_INPUT_PARALLEL 0
+#define TSIF_INPUT_SERIAL 1
+#define TSIF_NORMAL 0
+
+#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19
+#define V6_MPEG_SER_MSB_FIRST 0x80
+#define MPEG_SER_MSB_FIRST_ENABLED 0x01
+
+#define V6_656_I2S_BUFF_STATUS_REG 0x2F
+#define V6_656_OVERFLOW_MASK_BIT 0x08
+#define V6_I2S_OVERFLOW_MASK_BIT 0x01
+
+#define V6_I2S_STREAM_START_BIT_REG 0x14
+#define V6_I2S_STREAM_END_BIT_REG 0x15
+#define I2S_RIGHT_JUSTIFIED 0
+#define I2S_LEFT_JUSTIFIED 1
+#define I2S_DATA_FORMAT 2
+
+#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09
+#define V6_ENABLE_LOOP_THRU 0x01
+
+#define TOTAL_NUM_IF_OUTPUT_FREQ 16
+
+#define TUNER_NORMAL_IF_SPECTRUM 0x0
+#define TUNER_INVERT_IF_SPECTRUM 0x10
+
+#define V6_TUNER_IF_SEL_REG 0x06
+#define V6_TUNER_IF_FCW_REG 0x3C
+#define V6_TUNER_IF_FCW_BYP_REG 0x3D
+#define V6_RF_LOCK_STATUS_REG 0x23
+
+#define NUM_DIG_TV_CHANNEL 1000
+
+#define V6_DIG_CLK_FREQ_SEL_REG 0x07
+#define V6_REF_SYNTH_INT_REG 0x5C
+#define V6_REF_SYNTH_REMAIN_REG 0x58
+#define V6_DIG_RFREFSELECT_REG 0x32
+#define V6_XTAL_CLK_OUT_GAIN_REG 0x31
+#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09
+#define V6_DIG_XTAL_ENABLE_REG 0x06
+#define V6_DIG_XTAL_BIAS_REG 0x66
+#define V6_XTAL_CAP_REG 0x08
+
+#define V6_GPO_CTRL_REG 0x18
+#define MXL_GPO_0 0x00
+#define MXL_GPO_1 0x01
+#define V6_GPO_0_MASK 0x10
+#define V6_GPO_1_MASK 0x20
+
+#define V6_111SF_GPO_CTRL_REG 0x19
+#define MXL_111SF_GPO_1 0x00
+#define MXL_111SF_GPO_2 0x01
+#define MXL_111SF_GPO_3 0x02
+#define MXL_111SF_GPO_4 0x03
+#define MXL_111SF_GPO_5 0x04
+#define MXL_111SF_GPO_6 0x05
+#define MXL_111SF_GPO_7 0x06
+
+#define MXL_111SF_GPO_0_MASK 0x01
+#define MXL_111SF_GPO_1_MASK 0x02
+#define MXL_111SF_GPO_2_MASK 0x04
+#define MXL_111SF_GPO_3_MASK 0x08
+#define MXL_111SF_GPO_4_MASK 0x10
+#define MXL_111SF_GPO_5_MASK 0x20
+#define MXL_111SF_GPO_6_MASK 0x40
+
+#define V6_ATSC_CONFIG_REG 0x0A
+
+#define MXL_MODE_REG 0x03
+#define START_TUNE_REG 0x1C
+
+#define V6_IDAC_HYSTERESIS_REG 0x0B
+#define V6_IDAC_SETTINGS_REG 0x0C
+#define IDAC_MANUAL_CONTROL 1
+#define IDAC_CURRENT_SINKING_ENABLE 1
+#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80
+#define IDAC_CURRENT_SINKING_BIT_MASK 0x40
+
+#define V8_SPI_MODE_REG 0xE9
+
+#define V6_DIG_RF_PWR_LSB_REG 0x46
+#define V6_DIG_RF_PWR_MSB_REG 0x47
+
+#endif /* _DVB_USB_MXL111SF_REG_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
new file mode 100644
index 000000000000..a6341058c4e7
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
@@ -0,0 +1,476 @@
+/*
+ * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "mxl111sf-tuner.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_tuner_debug;
+module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+ if (mxl111sf_tuner_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_tuner_state {
+ struct mxl111sf_state *mxl_state;
+
+ struct mxl111sf_tuner_config *cfg;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 *data)
+{
+ return (state->cfg->read_reg) ?
+ state->cfg->read_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 data)
+{
+ return (state->cfg->write_reg) ?
+ state->cfg->write_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ return (state->cfg->program_regs) ?
+ state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+ -EINVAL;
+}
+
+static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return (state->cfg->top_master_ctrl) ?
+ state->cfg->top_master_ctrl(state->mxl_state, onoff) :
+ -EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
+ {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
+ DIG_MODEINDEX, _A, _CSF, */
+ {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
+ {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
+ {0, 0, 0}
+};
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
+ u8 bw)
+{
+ u8 filt_bw;
+
+ /* set channel bandwidth */
+ switch (bw) {
+ case 0: /* ATSC */
+ filt_bw = 25;
+ break;
+ case 1: /* QAM */
+ filt_bw = 69;
+ break;
+ case 6:
+ filt_bw = 21;
+ break;
+ case 7:
+ filt_bw = 42;
+ break;
+ case 8:
+ filt_bw = 63;
+ break;
+ default:
+ err("%s: invalid bandwidth setting!", __func__);
+ return NULL;
+ }
+
+ /* calculate RF channel */
+ freq /= 1000000;
+
+ freq *= 64;
+#if 0
+ /* do round */
+ freq += 0.5;
+#endif
+ /* set bandwidth */
+ mxl_phy_tune_rf[0].data = filt_bw;
+
+ /* set RF */
+ mxl_phy_tune_rf[1].data = (freq & 0xff);
+ mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
+
+ /* start tune */
+ return mxl_phy_tune_rf;
+}
+
+static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
+{
+ int ret;
+ u8 ctrl;
+#if 0
+ u16 iffcw;
+ u32 if_freq;
+#endif
+ mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
+ state->cfg->invert_spectrum, state->cfg->if_freq);
+
+ /* set IF polarity */
+ ctrl = state->cfg->invert_spectrum;
+
+ ctrl |= state->cfg->if_freq;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ if_freq /= 1000000;
+
+ /* do round */
+ if_freq += 0.5;
+
+ if (MXL_IF_LO == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (108 * 4096));
+ } else if (MXL_IF_HI == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (216 * 4096));
+ } else {
+ ctrl = 0;
+ iffcw = 0;
+ }
+
+ ctrl |= (iffcw >> 8);
+#endif
+ ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ctrl &= 0xf0;
+ ctrl |= 0x90;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ ctrl = iffcw & 0x00ff;
+#endif
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
+ int ret;
+ u8 mxl_mode;
+
+ mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
+
+ /* stop tune */
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* check device mode */
+ ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* Fill out registers for channel tune */
+ reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
+ if (!reg_ctrl_array)
+ return -EINVAL;
+
+ ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
+ /* IF tuner mode only */
+ mxl1x1sf_tuner_top_master_ctrl(state, 0);
+ mxl1x1sf_tuner_top_master_ctrl(state, 1);
+ mxl1x1sf_tuner_set_if_output_freq(state);
+ }
+
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (state->cfg->ant_hunt)
+ state->cfg->ant_hunt(fe);
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
+ int *rf_synth_lock,
+ int *ref_synth_lock)
+{
+ int ret;
+ u8 data;
+
+ *rf_synth_lock = 0;
+ *ref_synth_lock = 0;
+
+ ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
+ *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
+fail:
+ return ret;
+}
+
+#if 0
+static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
+ onoff ? 1 : 0);
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+ u8 bw;
+
+ mxl_dbg("()");
+
+ if (fe->ops.info.type == FE_ATSC) {
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ bw = 0; /* ATSC */
+ break;
+ case QAM_64:
+ case QAM_256:
+ bw = 1; /* US CABLE */
+ break;
+ default:
+ err("%s: modulation not set!", __func__);
+ return -EINVAL;
+ }
+ } else if (fe->ops.info.type == FE_OFDM) {
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ bw = 6;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bw = 7;
+ break;
+ case BANDWIDTH_8_MHZ:
+ bw = 8;
+ break;
+ default:
+ err("%s: bandwidth not set!", __func__);
+ return -EINVAL;
+ }
+ } else {
+ err("%s: modulation type not supported!", __func__);
+ return -EINVAL;
+ }
+ ret = mxl1x1sf_tune_rf(fe, params->frequency, bw);
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->frequency = params->frequency;
+ state->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+ params->u.ofdm.bandwidth : 0;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+static int mxl111sf_tuner_init(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* wake from standby handled by usb driver */
+
+ return ret;
+}
+
+static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* enter standby mode handled by usb driver */
+
+ return ret;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int rf_locked, ref_locked, ret;
+
+ *status = 0;
+
+ ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
+ if (mxl_fail(ret))
+ goto fail;
+ mxl_info("%s%s", rf_locked ? "rf locked " : "",
+ ref_locked ? "ref locked" : "");
+
+ if ((rf_locked) || (ref_locked))
+ *status |= TUNER_STATUS_LOCKED;
+fail:
+ return ret;
+}
+
+static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ u8 val1, val2;
+ int ret;
+
+ *strength = 0;
+
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *strength = val1 | ((val2 & 0x07) << 8);
+fail:
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *frequency = state->frequency;
+ return 0;
+}
+
+static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *bandwidth = state->bandwidth;
+ return 0;
+}
+
+static int mxl111sf_tuner_release(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ mxl_dbg("()");
+ kfree(state);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
+ .info = {
+ .name = "MaxLinear MxL111SF",
+#if 0
+ .frequency_min = ,
+ .frequency_max = ,
+ .frequency_step = ,
+#endif
+ },
+#if 0
+ .init = mxl111sf_tuner_init,
+ .sleep = mxl111sf_tuner_sleep,
+#endif
+ .set_params = mxl111sf_tuner_set_params,
+ .get_status = mxl111sf_tuner_get_status,
+ .get_rf_strength = mxl111sf_get_rf_strength,
+ .get_frequency = mxl111sf_tuner_get_frequency,
+ .get_bandwidth = mxl111sf_tuner_get_bandwidth,
+ .release = mxl111sf_tuner_release,
+};
+
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state,
+ struct mxl111sf_tuner_config *cfg)
+{
+ struct mxl111sf_tuner_state *state = NULL;
+
+ mxl_dbg("()");
+
+ state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->mxl_state = mxl_state;
+ state->cfg = cfg;
+
+ memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+ return fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
new file mode 100644
index 000000000000..ff333960b184
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
@@ -0,0 +1,89 @@
+/*
+ * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 __MXL111SF_TUNER_H__
+#define __MXL111SF_TUNER_H__
+
+#include "dvb_frontend.h"
+
+#include "mxl111sf.h"
+
+enum mxl_if_freq {
+#if 0
+ MXL_IF_LO = 0x00, /* other IF < 9MHz */
+#endif
+ MXL_IF_4_0 = 0x01, /* 4.0 MHz */
+ MXL_IF_4_5 = 0x02, /* 4.5 MHz */
+ MXL_IF_4_57 = 0x03, /* 4.57 MHz */
+ MXL_IF_5_0 = 0x04, /* 5.0 MHz */
+ MXL_IF_5_38 = 0x05, /* 5.38 MHz */
+ MXL_IF_6_0 = 0x06, /* 6.0 MHz */
+ MXL_IF_6_28 = 0x07, /* 6.28 MHz */
+ MXL_IF_7_2 = 0x08, /* 7.2 MHz */
+ MXL_IF_35_25 = 0x09, /* 35.25 MHz */
+ MXL_IF_36 = 0x0a, /* 36 MHz */
+ MXL_IF_36_15 = 0x0b, /* 36.15 MHz */
+ MXL_IF_44 = 0x0c, /* 44 MHz */
+#if 0
+ MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */
+#endif
+};
+
+struct mxl111sf_tuner_config {
+ enum mxl_if_freq if_freq;
+ unsigned int invert_spectrum:1;
+
+ int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
+ int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
+ int (*program_regs)(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+ int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff);
+ int (*ant_hunt)(struct dvb_frontend *fe);
+};
+
+/* ------------------------------------------------------------------------ */
+
+#if defined(CONFIG_DVB_USB_MXL111SF) || \
+ (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
+extern
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state,
+ struct mxl111sf_tuner_config *cfg);
+#else
+static inline
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state
+ struct mxl111sf_tuner_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* __MXL111SF_TUNER_H__ */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
new file mode 100644
index 000000000000..b5c98da5d9e2
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/i2c.h>
+
+#include "mxl111sf.h"
+#include "mxl111sf-reg.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-i2c.h"
+#include "mxl111sf-gpio.h"
+
+#include "mxl111sf-demod.h"
+#include "mxl111sf-tuner.h"
+
+#include "lgdt3305.h"
+
+int dvb_usb_mxl111sf_debug;
+module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level "
+ "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
+
+int dvb_usb_mxl111sf_isoc;
+module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
+MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
+
+#define ANT_PATH_AUTO 0
+#define ANT_PATH_EXTERNAL 1
+#define ANT_PATH_INTERNAL 2
+
+int dvb_usb_mxl111sf_rfswitch =
+#if 0
+ ANT_PATH_AUTO;
+#else
+ ANT_PATH_EXTERNAL;
+#endif
+
+module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644);
+MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args)
+#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args)
+#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args)
+
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ int wo = (rbuf == NULL || rlen == 0); /* write-only */
+ int ret;
+ u8 sndbuf[1+wlen];
+
+ deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
+
+ memset(sndbuf, 0, 1+wlen);
+
+ sndbuf[0] = cmd;
+ memcpy(&sndbuf[1], wbuf, wlen);
+
+ ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) :
+ dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#define MXL_CMD_REG_READ 0xaa
+#define MXL_CMD_REG_WRITE 0x55
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
+{
+ u8 buf[2];
+ int ret;
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
+ if (mxl_fail(ret)) {
+ mxl_debug("error reading reg: 0x%02x", addr);
+ goto fail;
+ }
+
+ if (buf[0] == addr)
+ *data = buf[1];
+ else {
+ err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
+ addr, buf[0], buf[1]);
+ ret = -EINVAL;
+ }
+
+ deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
+fail:
+ return ret;
+}
+
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
+{
+ u8 buf[] = { addr, data };
+ int ret;
+
+ deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
+ if (mxl_fail(ret))
+ err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data)
+{
+ int ret;
+ u8 val;
+
+ if (mask != 0xff) {
+ ret = mxl111sf_read_reg(state, addr, &val);
+#if 1
+ /* dont know why this usually errors out on the first try */
+ if (mxl_fail(ret))
+ err("error writing addr: 0x%02x, mask: 0x%02x, "
+ "data: 0x%02x, retrying...", addr, mask, data);
+
+ ret = mxl111sf_read_reg(state, addr, &val);
+#endif
+ if (mxl_fail(ret))
+ goto fail;
+ }
+ val &= ~mask;
+ val |= data;
+
+ ret = mxl111sf_write_reg(state, addr, val);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ int i, ret = 0;
+
+ for (i = 0; ctrl_reg_info[i].addr |
+ ctrl_reg_info[i].mask |
+ ctrl_reg_info[i].data; i++) {
+
+ ret = mxl111sf_write_reg_mask(state,
+ ctrl_reg_info[i].addr,
+ ctrl_reg_info[i].mask,
+ ctrl_reg_info[i].data);
+ if (mxl_fail(ret)) {
+ err("failed on reg #%d (0x%02x)", i,
+ ctrl_reg_info[i].addr);
+ break;
+ }
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 id, ver;
+ char *mxl_chip, *mxl_rev;
+
+ if ((state->chip_id) && (state->chip_ver))
+ return 0;
+
+ ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_id = id;
+
+ ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_ver = ver;
+
+ switch (id) {
+ case 0x61:
+ mxl_chip = "MxL101SF";
+ break;
+ case 0x63:
+ mxl_chip = "MxL111SF";
+ break;
+ default:
+ mxl_chip = "UNKNOWN MxL1X1";
+ break;
+ }
+ switch (ver) {
+ case 0x36:
+ state->chip_rev = MXL111SF_V6;
+ mxl_rev = "v6";
+ break;
+ case 0x08:
+ state->chip_rev = MXL111SF_V8_100;
+ mxl_rev = "v8_100";
+ break;
+ case 0x18:
+ state->chip_rev = MXL111SF_V8_200;
+ mxl_rev = "v8_200";
+ break;
+ default:
+ state->chip_rev = 0;
+ mxl_rev = "UNKNOWN REVISION";
+ break;
+ }
+ info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
+fail:
+ return ret;
+}
+
+#define get_chip_info(state) \
+({ \
+ int ___ret; \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) { \
+ mxl_debug("failed to get chip info" \
+ " on first probe attempt"); \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) \
+ err("failed to get chip info during probe"); \
+ else \
+ mxl_debug("probe needed a retry " \
+ "in order to succeed."); \
+ } \
+ ___ret; \
+})
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ /* power control depends on which adapter is being woken:
+ * save this for init, instead, via mxl111sf_adap_fe_init */
+ return 0;
+}
+
+static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
+
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ mutex_lock(&state->fe_lock);
+
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ err = mxl1x1sf_soft_reset(state);
+ mxl_fail(err);
+ err = mxl111sf_init_tuner_demod(state);
+ mxl_fail(err);
+ err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+
+ mxl_fail(err);
+ mxl111sf_enable_usb_output(state);
+ mxl_fail(err);
+ mxl1x1sf_top_master_ctrl(state, 1);
+ mxl_fail(err);
+
+ if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
+ (state->chip_rev > MXL111SF_V6)) {
+ mxl111sf_config_pin_mux_modes(state,
+ PIN_MUX_TS_SPI_IN_MODE_1);
+ mxl_fail(err);
+ }
+ err = mxl111sf_init_port_expander(state);
+ if (!mxl_fail(err)) {
+ state->gpio_mode = adap_state->gpio_mode;
+ err = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ mxl_fail(err);
+#if 0
+ err = fe->ops.init(fe);
+#endif
+ msleep(100); /* add short delay after enabling
+ * the demod before touching it */
+ }
+
+ return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0;
+fail:
+ return -ENODEV;
+}
+
+static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
+
+ mutex_unlock(&state->fe_lock);
+
+ return err;
+fail:
+ return -ENODEV;
+}
+
+
+static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv;
+ int ret = 0;
+ u8 tmp;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ ret = mxl111sf_config_mpeg_in(state, 1, 1,
+ adap_state->ep6_clockphase,
+ 0, 0);
+ mxl_fail(ret);
+ } else {
+ ret = mxl111sf_disable_656_port(state);
+ mxl_fail(ret);
+ }
+
+ mxl111sf_read_reg(state, 0x12, &tmp);
+ tmp &= ~0x04;
+ mxl111sf_write_reg(state, 0x12, tmp);
+
+ return ret;
+}
+
+static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+ .i2c_addr = 0xb2 >> 1,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .qam_if_khz = 6000,
+ .vsb_if_khz = 6000,
+};
+
+static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe_adap[fe_id].fe = dvb_attach(lgdt3305_attach,
+ &hauppauge_lgdt3305_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct mxl111sf_demod_config mxl_demod_config = {
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+};
+
+static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_SOC_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* dont care if this fails */
+ mxl111sf_init_port_expander(state);
+
+ adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state,
+ &mxl_demod_config);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
+ int antpath)
+{
+ return mxl111sf_idac_config(state, 1, 1,
+ (antpath == ANT_PATH_INTERNAL) ?
+ 0x3f : 0x00, 0);
+}
+
+#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
+ err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
+ __func__, __LINE__, \
+ (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
+ pwr0, pwr1, pwr2, pwr3)
+
+#define ANT_HUNT_SLEEP 90
+#define ANT_EXT_TWEAK 0
+
+static int mxl111sf_ant_hunt(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+
+ int antctrl = dvb_usb_mxl111sf_rfswitch;
+
+ u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2;
+
+ /* FIXME: must force EXTERNAL for QAM - done elsewhere */
+ mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ?
+ ANT_PATH_EXTERNAL : antctrl);
+
+ if (antctrl == ANT_PATH_AUTO) {
+#if 0
+ msleep(ANT_HUNT_SLEEP);
+#endif
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2);
+
+ if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) {
+ /* return with EXTERNAL enabled */
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ } else {
+ /* return with INTERNAL enabled */
+ DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ }
+ }
+ return 0;
+}
+
+static struct mxl111sf_tuner_config mxl_tuner_config = {
+ .if_freq = MXL_IF_6_0, /* applies to external IF output, only */
+ .invert_spectrum = 0,
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+ .top_master_ctrl = mxl1x1sf_top_master_ctrl,
+ .ant_hunt = mxl111sf_ant_hunt,
+};
+
+static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+
+ deb_adv("%s()\n", __func__);
+
+ if (NULL != dvb_attach(mxl111sf_tuner_attach,
+ adap->fe_adap[fe_id].fe, state,
+ &mxl_tuner_config))
+ return 0;
+
+ return -EIO;
+}
+
+static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe,
+ unsigned int cmd, void *parg,
+ unsigned int stage)
+{
+ int err = 0;
+
+ switch (stage) {
+ case DVB_FE_IOCTL_PRE:
+
+ switch (cmd) {
+ case FE_READ_SIGNAL_STRENGTH:
+ err = fe->ops.tuner_ops.get_rf_strength(fe, parg);
+ /* If no error occurs, prevent dvb-core from handling
+ * this IOCTL, otherwise return the error */
+ if (0 == err)
+ err = 1;
+ break;
+ }
+ break;
+
+ case DVB_FE_IOCTL_POST:
+ /* no post-ioctl handling required */
+ break;
+ }
+ return err;
+};
+
+static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+struct i2c_algorithm mxl111sf_i2c_algo = {
+ .master_xfer = mxl111sf_i2c_xfer,
+ .functionality = mxl111sf_i2c_func,
+#ifdef NEED_ALGO_CONTROL
+ .algo_control = dummy_algo_control,
+#endif
+};
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
+
+static int mxl111sf_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct dvb_usb_device *d = NULL;
+
+ deb_adv("%s()\n", __func__);
+
+ if (((dvb_usb_mxl111sf_isoc) &&
+ (0 == dvb_usb_device_init(intf,
+ &mxl111sf_dvbt_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_isoc_properties,
+ THIS_MODULE, &d, adapter_nr))) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_dvbt_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) || 0) {
+
+ struct mxl111sf_state *state = d->priv;
+ static u8 eeprom[256];
+ struct i2c_client c;
+ int ret;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ err("failed to get chip info during probe");
+
+ mutex_init(&state->fe_lock);
+
+ if (state->chip_rev > MXL111SF_V6)
+ mxl111sf_config_pin_mux_modes(state,
+ PIN_MUX_TS_SPI_IN_MODE_1);
+
+ c.adapter = &d->i2c_adap;
+ c.addr = 0xa0 >> 1;
+
+ ret = tveeprom_read(&c, eeprom, sizeof(eeprom));
+ if (mxl_fail(ret))
+ return 0;
+ tveeprom_hauppauge_analog(&c, &state->tv,
+ (0x84 == eeprom[0xa0]) ?
+ eeprom + 0xa0 : eeprom + 0x80);
+#if 0
+ switch (state->tv.model) {
+ case 117001:
+ case 126001:
+ case 138001:
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
+ __func__, state->tv.model);
+ }
+#endif
+ return 0;
+ }
+ err("Your device is not yet supported by this driver. "
+ "See kernellabs.com for more info");
+ return -EINVAL;
+}
+
+static struct usb_device_id mxl111sf_table[] = {
+/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */
+/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */
+/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */
+/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */
+/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */
+/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */
+/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */
+ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, mxl111sf_table);
+
+
+#define MXL111SF_EP4_BULK_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
+ .stream = { \
+ .type = USB_BULK, \
+ .count = 5, \
+ .endpoint = 0x04, \
+ .u = { \
+ .bulk = { \
+ .buffersize = 8192, \
+ } \
+ } \
+ }
+
+/* FIXME: works for v6 but not v8 silicon */
+#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
+ .stream = { \
+ .type = USB_ISOC, \
+ .count = 5, \
+ .endpoint = 0x04, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = 96, \
+ /* FIXME: v6 SILICON: */ \
+ .framesize = 564, \
+ .interval = 1, \
+ } \
+ } \
+ }
+
+#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
+ .stream = { \
+ .type = USB_BULK, \
+ .count = 5, \
+ .endpoint = 0x06, \
+ .u = { \
+ .bulk = { \
+ .buffersize = 8192, \
+ } \
+ } \
+ }
+
+/* FIXME */
+#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \
+ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
+ .stream = { \
+ .type = USB_ISOC, \
+ .count = 5, \
+ .endpoint = 0x06, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = 24, \
+ .framesize = 3072, \
+ .interval = 1, \
+ } \
+ } \
+ }
+
+#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, \
+ .usb_ctrl = DEVICE_SPECIFIC, \
+ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \
+ EP6 BULK transfer (atsc/qam), \
+ use usb alt setting 2 for EP4 BULK transfer (dvb-t), \
+ EP6 ISOC transfer (atsc/qam), \
+ */ \
+ .power_ctrl = mxl111sf_power_ctrl, \
+ .i2c_algo = &mxl111sf_i2c_algo, \
+ .generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \
+ .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
+ .size_of_priv = sizeof(struct mxl111sf_state)
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ } },
+ },
+ },
+ .num_device_descs = 4,
+ .devices = {
+ { "Hauppauge 126xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[4], &mxl111sf_table[8],
+ NULL },
+ },
+ { "Hauppauge 117xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[15], &mxl111sf_table[18],
+ NULL },
+ },
+ { "Hauppauge 138xxx DVBT (bulk)",
+ { NULL },
+ { &mxl111sf_table[20], &mxl111sf_table[22],
+ &mxl111sf_table[24], &mxl111sf_table[26],
+ NULL },
+ },
+ { "Hauppauge 126xxx (tp-bulk)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ } },
+ },
+ },
+ .num_device_descs = 4,
+ .devices = {
+ { "Hauppauge 126xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[4], &mxl111sf_table[8],
+ NULL },
+ },
+ { "Hauppauge 117xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[15], &mxl111sf_table[18],
+ NULL },
+ },
+ { "Hauppauge 138xxx DVBT (isoc)",
+ { NULL },
+ { &mxl111sf_table[20], &mxl111sf_table[22],
+ &mxl111sf_table[24], &mxl111sf_table[26],
+ NULL },
+ },
+ { "Hauppauge 126xxx (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ },
+ {
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 6,
+ .devices = {
+ { "Hauppauge 126xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[1], &mxl111sf_table[5],
+ NULL },
+ },
+ { "Hauppauge 117xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[12],
+ NULL },
+ },
+ { "Hauppauge 126xxx ATSC+ (bulk)",
+ { NULL },
+ { &mxl111sf_table[0], &mxl111sf_table[3],
+ &mxl111sf_table[7], &mxl111sf_table[9],
+ &mxl111sf_table[10], NULL },
+ },
+ { "Hauppauge 117xxx ATSC+ (bulk)",
+ { NULL },
+ { &mxl111sf_table[11], &mxl111sf_table[14],
+ &mxl111sf_table[16], &mxl111sf_table[17],
+ &mxl111sf_table[32], &mxl111sf_table[33],
+ NULL },
+ },
+ { "Hauppauge Mercury (tp-bulk)",
+ { NULL },
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
+ &mxl111sf_table[27], NULL },
+ },
+ { "Hauppauge WinTV-Aero-M",
+ { NULL },
+ { &mxl111sf_table[29], &mxl111sf_table[31],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .size_of_priv = sizeof(struct mxl111sf_adap_state),
+
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 6,
+ .devices = {
+ { "Hauppauge 126xxx ATSC (isoc)",
+ { NULL },
+ { &mxl111sf_table[1], &mxl111sf_table[5],
+ NULL },
+ },
+ { "Hauppauge 117xxx ATSC (isoc)",
+ { NULL },
+ { &mxl111sf_table[12],
+ NULL },
+ },
+ { "Hauppauge 126xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[0], &mxl111sf_table[3],
+ &mxl111sf_table[7], &mxl111sf_table[9],
+ &mxl111sf_table[10], NULL },
+ },
+ { "Hauppauge 117xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[11], &mxl111sf_table[14],
+ &mxl111sf_table[16], &mxl111sf_table[17],
+ &mxl111sf_table[32], &mxl111sf_table[33],
+ NULL },
+ },
+ { "Hauppauge Mercury (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
+ &mxl111sf_table[27], NULL },
+ },
+ { "Hauppauge WinTV-Aero-M (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[29], &mxl111sf_table[31],
+ NULL },
+ },
+ }
+};
+
+static struct usb_driver mxl111sf_driver = {
+ .name = "dvb_usb_mxl111sf",
+ .probe = mxl111sf_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = mxl111sf_table,
+};
+
+static int __init mxl111sf_module_init(void)
+{
+ int result = usb_register(&mxl111sf_driver);
+ if (result) {
+ err("usb_register failed. Error number %d", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit mxl111sf_module_exit(void)
+{
+ usb_deregister(&mxl111sf_driver);
+}
+
+module_init(mxl111sf_module_init);
+module_exit(mxl111sf_module_exit);
+
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h
new file mode 100644
index 000000000000..364d89f826bd
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#ifndef _DVB_USB_MXL111SF_H_
+#define _DVB_USB_MXL111SF_H_
+
+#ifdef DVB_USB_LOG_PREFIX
+#undef DVB_USB_LOG_PREFIX
+#endif
+#define DVB_USB_LOG_PREFIX "mxl111sf"
+#include "dvb-usb.h"
+#include <media/tveeprom.h>
+
+#define MXL_EP1_REG_READ 1
+#define MXL_EP2_REG_WRITE 2
+#define MXL_EP3_INTERRUPT 3
+#define MXL_EP4_MPEG2 4
+#define MXL_EP5_I2S 5
+#define MXL_EP6_656 6
+#define MXL_EP6_MPEG2 6
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+enum mxl111sf_current_mode {
+ mxl_mode_dvbt = MXL_EP4_MPEG2,
+ mxl_mode_mh = MXL_EP5_I2S,
+ mxl_mode_atsc = MXL_EP6_MPEG2,
+};
+#endif
+
+enum mxl111sf_gpio_port_expander {
+ mxl111sf_gpio_hw,
+ mxl111sf_PCA9534,
+};
+
+struct mxl111sf_state {
+ struct dvb_usb_device *d;
+
+ enum mxl111sf_gpio_port_expander gpio_port_expander;
+ u8 port_expander_addr;
+
+ u8 chip_id;
+ u8 chip_ver;
+#define MXL111SF_V6 1
+#define MXL111SF_V8_100 2
+#define MXL111SF_V8_200 3
+ u8 chip_rev;
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+ enum mxl111sf_current_mode current_mode;
+#endif
+
+#define MXL_TUNER_MODE 0
+#define MXL_SOC_MODE 1
+#define MXL_DEV_MODE_MASK 0x01
+#if 1
+ int device_mode;
+#endif
+ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
+ EP5 BULK transfer (atsc-mh),
+ EP6 BULK transfer (atsc/qam),
+ use usb alt setting 2 for EP4 BULK transfer (dvb-t),
+ EP5 ISOC transfer (atsc-mh),
+ EP6 ISOC transfer (atsc/qam),
+ */
+ int alt_mode;
+ int gpio_mode;
+ struct tveeprom tv;
+
+ struct mutex fe_lock;
+};
+
+struct mxl111sf_adap_state {
+ int alt_mode;
+ int gpio_mode;
+ int device_mode;
+ int ep6_clockphase;
+ int (*fe_init)(struct dvb_frontend *);
+ int (*fe_sleep)(struct dvb_frontend *);
+};
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
+
+struct mxl111sf_reg_ctrl_info {
+ u8 addr;
+ u8 mask;
+ u8 data;
+};
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data);
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+
+/* needed for hardware i2c functions in mxl111sf-i2c.c:
+ * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
+
+#define mxl_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt "\n", __func__, ##arg)
+
+#define mxl_info(fmt, arg...) \
+ mxl_printk(KERN_INFO, fmt, ##arg)
+
+extern int dvb_usb_mxl111sf_debug;
+#define mxl_debug(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define MXL_I2C_DBG 0x04
+#define MXL_ADV_DBG 0x10
+#define mxl_debug_adv(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c_adv(fmt, arg...) \
+ if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
+ (MXL_I2C_DBG | MXL_ADV_DBG)) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* The following allows the mxl_fail() macro defined below to work
+ * in externel modules, such as mxl111sf-tuner.ko, even though
+ * dvb_usb_mxl111sf_debug is not defined within those modules */
+#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
+#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
+#else
+#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
+#endif
+
+#define mxl_fail(ret) \
+({ \
+ int __ret; \
+ __ret = (ret < 0); \
+ if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
+ mxl_printk(KERN_ERR, "error %d on line %d", \
+ ret, __LINE__); \
+ __ret; \
+})
+
+#endif /* _DVB_USB_MXL111SF_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index bc350e982b72..21384da6570e 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -166,6 +166,8 @@ static struct dvb_usb_device_properties nova_t_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
@@ -186,7 +188,7 @@ static struct dvb_usb_device_properties nova_t_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
index 2e4fab7215f5..98fd9a6092b7 100644
--- a/drivers/media/dvb/dvb-usb/opera1.c
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -263,10 +263,10 @@ static struct stv0299_config opera1_stv0299_config = {
static int opera1_frontend_attach(struct dvb_usb_adapter *d)
{
- if ((d->fe =
- dvb_attach(stv0299_attach, &opera1_stv0299_config,
- &d->dev->i2c_adap)) != NULL) {
- d->fe->ops.set_voltage = opera1_set_voltage;
+ d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config,
+ &d->dev->i2c_adap);
+ if ((d->fe_adap[0].fe) != NULL) {
+ d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage;
return 0;
}
info("not attached stv0299");
@@ -276,7 +276,7 @@ static int opera1_frontend_attach(struct dvb_usb_adapter *d)
static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(
- dvb_pll_attach, adap->fe, 0xc0>>1,
+ dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1,
&adap->dev->i2c_adap, DVB_PLL_OPERA1
);
return 0;
@@ -516,6 +516,8 @@ static struct dvb_usb_device_properties opera1_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = opera1_frontend_attach,
.streaming_ctrl = opera1_streaming_ctrl,
.tuner_attach = opera1_tuner_attach,
@@ -535,6 +537,7 @@ static struct dvb_usb_device_properties opera1_properties = {
}
}
},
+ }},
}
},
.num_device_descs = 1,
diff --git a/drivers/media/dvb/dvb-usb/pctv452e.c b/drivers/media/dvb/dvb-usb/pctv452e.c
new file mode 100644
index 000000000000..f9aec5cb6e77
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/pctv452e.c
@@ -0,0 +1,1079 @@
+/*
+ * PCTV 452e DVB driver
+ *
+ * Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net>
+ *
+ * TT connect S2-3650-CI Common Interface support, MAC readout
+ * Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at>
+ *
+ * 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.
+ */
+
+/* dvb usb framework */
+#define DVB_USB_LOG_PREFIX "pctv452e"
+#include "dvb-usb.h"
+
+/* Demodulator */
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+/* Tuner */
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+/* FE Power */
+#include "lnbp22.h"
+
+#include "dvb_ca_en50221.h"
+#include "ttpci-eeprom.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define ISOC_INTERFACE_ALTERNATIVE 3
+
+#define SYNC_BYTE_OUT 0xaa
+#define SYNC_BYTE_IN 0x55
+
+/* guessed: (copied from ttusb-budget) */
+#define PCTV_CMD_RESET 0x15
+/* command to poll IR receiver */
+#define PCTV_CMD_IR 0x1b
+/* command to send I2C */
+#define PCTV_CMD_I2C 0x31
+
+#define I2C_ADDR_STB0899 (0xd0 >> 1)
+#define I2C_ADDR_STB6100 (0xc0 >> 1)
+#define I2C_ADDR_LNBP22 (0x10 >> 1)
+#define I2C_ADDR_24C16 (0xa0 >> 1)
+#define I2C_ADDR_24C64 (0xa2 >> 1)
+
+
+/* pctv452e sends us this amount of data for each issued usb-command */
+#define PCTV_ANSWER_LEN 64
+/* Wait up to 1000ms for device */
+#define PCTV_TIMEOUT 1000
+
+
+#define PCTV_LED_GPIO STB0899_GPIO01
+#define PCTV_LED_GREEN 0x82
+#define PCTV_LED_ORANGE 0x02
+
+#define ci_dbg(format, arg...) \
+do { \
+ if (0) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": " format "\n" , ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
+
+static struct stb0899_postproc pctv45e_postproc[] = {
+ { PCTV_LED_GPIO, STB0899_GPIOPULLUP },
+ { 0, 0 }
+};
+
+/*
+ * stores all private variables for communication with the PCTV452e DVB-S2
+ */
+struct pctv452e_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+
+ u8 c; /* transaction counter, wraps around... */
+ u8 initialized; /* set to 1 if 0x15 has been sent */
+ u16 last_rc_key;
+};
+
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
+ unsigned int write_len, unsigned int read_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ unsigned int rlen;
+ int ret;
+
+ BUG_ON(NULL == data && 0 != (write_len | read_len));
+ BUG_ON(write_len > 64 - 4);
+ BUG_ON(read_len > 64 - 4);
+
+ id = state->c++;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = cmd;
+ buf[3] = write_len;
+
+ memcpy(buf + 4, data, write_len);
+
+ rlen = (read_len > 0) ? 64 : 0;
+ ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
+ buf, rlen, /* delay_ms */ 0);
+ if (0 != ret)
+ goto failed;
+
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ memcpy(data, buf + 4, read_len);
+
+ return 0;
+
+failed:
+ err("CI error %d; %02X %02X %02X -> %02X %02X %02X.",
+ ret, SYNC_BYTE_OUT, id, cmd, buf[0], buf[1], buf[2]);
+
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca,
+ u8 cmd, u8 *data, unsigned int write_len,
+ unsigned int read_len)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ u8 buf[3];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%s %04x -> %d 0x%02x",
+ __func__, address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%s %d 0x%04x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("%s 0x%02x -> %d 0x%02x",
+ __func__, address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%s %d 0x%02x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca,
+ int slot,
+ int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d %d", __func__, slot, enable);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ enable = !!enable;
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d", __func__, slot);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca,
+ int slot,
+ int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (0 != ret)
+ return ret;
+
+ if (1 == buf[0])
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+
+ return 0;
+
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state;
+
+ ci_dbg("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct pctv452e_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ /* Error ignored. */
+ tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ ci_dbg("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
+#define CMD_BUFFER_SIZE 0x28
+static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ const u8 *snd_buf, u8 snd_len,
+ u8 *rcv_buf, u8 rcv_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ int ret;
+
+ id = state->c++;
+
+ ret = -EINVAL;
+ if (snd_len > 64 - 7 || rcv_len > 64 - 7)
+ goto failed;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = PCTV_CMD_I2C;
+ buf[3] = snd_len + 3;
+ buf[4] = addr << 1;
+ buf[5] = snd_len;
+ buf[6] = rcv_len;
+
+ memcpy(buf + 7, snd_buf, snd_len);
+
+ ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
+ buf, /* rcv_len */ 64,
+ /* delay_ms */ 0);
+ if (ret < 0)
+ goto failed;
+
+ /* TT USB protocol error. */
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ /* I2C device didn't respond as expected. */
+ ret = -EREMOTEIO;
+ if (buf[5] < snd_len || buf[6] < rcv_len)
+ goto failed;
+
+ memcpy(rcv_buf, buf + 7, rcv_len);
+
+ return rcv_len;
+
+failed:
+ err("I2C error %d; %02X %02X %02X %02X %02X -> "
+ "%02X %02X %02X %02X %02X.",
+ ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
+ buf[0], buf[1], buf[4], buf[5], buf[6]);
+
+ return ret;
+}
+
+static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adapter);
+ int i;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
+ int ret;
+
+ if (msg[i].flags & I2C_M_RD) {
+ addr = msg[i].addr;
+ snd_buf = NULL;
+ snd_len = 0;
+ rcv_buf = msg[i].buf;
+ rcv_len = msg[i].len;
+ } else {
+ addr = msg[i].addr;
+ snd_buf = msg[i].buf;
+ snd_len = msg[i].len;
+ rcv_buf = NULL;
+ rcv_len = 0;
+ }
+
+ ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf,
+ rcv_len);
+ if (ret < rcv_len)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 pctv452e_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 };
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret;
+
+ info("%s: %d\n", __func__, i);
+
+ if (!i)
+ return 0;
+
+ if (state->initialized)
+ return 0;
+
+ /* hmm where shoud this should go? */
+ ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
+ if (ret != 0)
+ info("%s: Warning set interface returned: %d\n",
+ __func__, ret);
+
+ /* this is a one-time initialization, dont know where to put */
+ b0[1] = state->c++;
+ /* reset board */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ b0[1] = state->c++;
+ b0[4] = 1;
+ /* reset board (again?) */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ state->initialized = 1;
+
+ return 0;
+}
+
+static int pctv452e_rc_query(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b[CMD_BUFFER_SIZE];
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret, i;
+ u8 id = state->c++;
+
+ /* prepare command header */
+ b[0] = SYNC_BYTE_OUT;
+ b[1] = id;
+ b[2] = PCTV_CMD_IR;
+ b[3] = 0;
+
+ /* send ir request */
+ ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
+ if (ret != 0)
+ return ret;
+
+ if (debug > 3) {
+ info("%s: read: %2d: %02x %02x %02x: ", __func__,
+ ret, rx[0], rx[1], rx[2]);
+ for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
+ info(" %02x", rx[i+3]);
+
+ info("\n");
+ }
+
+ if ((rx[3] == 9) && (rx[12] & 0x01)) {
+ /* got a "press" event */
+ state->last_rc_key = (rx[7] << 8) | rx[6];
+ if (debug > 2)
+ info("%s: cmd=0x%02x sys=0x%02x\n",
+ __func__, rx[6], rx[7]);
+
+ rc_keydown(d->rc_dev, state->last_rc_key, 0);
+ } else if (state->last_rc_key) {
+ rc_keyup(d->rc_dev);
+ state->last_rc_key = 0;
+ }
+
+ return 0;
+}
+
+static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ const u8 mem_addr[] = { 0x1f, 0xcc };
+ u8 encoded_mac[20];
+ int ret;
+
+ ret = -EAGAIN;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ goto failed;
+
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16,
+ mem_addr + 1, /* snd_len */ 1,
+ encoded_mac, /* rcv_len */ 20);
+ if (-EREMOTEIO == ret)
+ /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a
+ byte write if /WC is low. */
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64,
+ mem_addr, 2,
+ encoded_mac, 20);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (20 != ret)
+ goto failed;
+
+ ret = ttpci_eeprom_decode_mac(mac, encoded_mac);
+ if (0 != ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ memset(mac, 0, 6);
+
+ return ret;
+}
+
+static const struct stb0899_s1_reg pctv452e_init_dev[] = {
+ { STB0899_DISCNTRL1, 0x26 },
+ { STB0899_DISCNTRL2, 0x80 },
+ { STB0899_DISRX_ST0, 0x04 },
+ { STB0899_DISRX_ST1, 0x20 },
+ { STB0899_DISPARITY, 0x00 },
+ { STB0899_DISFIFO, 0x00 },
+ { STB0899_DISF22, 0x99 },
+ { STB0899_DISF22RX, 0x85 }, /* 0xa8 */
+ { STB0899_ACRPRESC, 0x11 },
+ { STB0899_ACRDIV1, 0x0a },
+ { STB0899_ACRDIV2, 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG, 0x00 },
+ { STB0899_MODECFG, 0x00 }, /* Inversion */
+ { STB0899_IRQMSK_3, 0xf3 },
+ { STB0899_IRQMSK_2, 0xfc },
+ { STB0899_IRQMSK_1, 0xff },
+ { STB0899_IRQMSK_0, 0xff },
+ { STB0899_I2CCFG, 0x88 },
+ { STB0899_I2CRPT, 0x58 },
+ { STB0899_GPIO00CFG, 0x82 },
+ { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */
+ { STB0899_GPIO02CFG, 0x82 },
+ { STB0899_GPIO03CFG, 0x82 },
+ { STB0899_GPIO04CFG, 0x82 },
+ { STB0899_GPIO05CFG, 0x82 },
+ { STB0899_GPIO06CFG, 0x82 },
+ { STB0899_GPIO07CFG, 0x82 },
+ { STB0899_GPIO08CFG, 0x82 },
+ { STB0899_GPIO09CFG, 0x82 },
+ { STB0899_GPIO10CFG, 0x82 },
+ { STB0899_GPIO11CFG, 0x82 },
+ { STB0899_GPIO12CFG, 0x82 },
+ { STB0899_GPIO13CFG, 0x82 },
+ { STB0899_GPIO14CFG, 0x82 },
+ { STB0899_GPIO15CFG, 0x82 },
+ { STB0899_GPIO16CFG, 0x82 },
+ { STB0899_GPIO17CFG, 0x82 },
+ { STB0899_GPIO18CFG, 0x82 },
+ { STB0899_GPIO19CFG, 0x82 },
+ { STB0899_GPIO20CFG, 0x82 },
+ { STB0899_SDATCFG, 0xb8 },
+ { STB0899_SCLTCFG, 0xba },
+ { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */
+ { STB0899_GPIO22, 0x82 },
+ { STB0899_GPIO21, 0x91 },
+ { STB0899_DIRCLKCFG, 0x82 },
+ { STB0899_CLKOUT27CFG, 0x7e },
+ { STB0899_STDBYCFG, 0x82 },
+ { STB0899_CS0CFG, 0x82 },
+ { STB0899_CS1CFG, 0x82 },
+ { STB0899_DISEQCOCFG, 0x20 },
+ { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */
+ { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */
+ { STB0899_FILTCTRL, 0x00 },
+ { STB0899_SYSCTRL, 0x00 },
+ { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */
+ { STB0899_STOPCLK2, 0x00 },
+ { STB0899_INTBUFCTRL, 0x0a },
+ { STB0899_AGC2I1, 0x00 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_AGCIQIN, 0x00 },
+ { STB0899_TSTRES, 0x40 }, /* rjkm */
+ { 0xffff, 0xff },
+};
+
+static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = {
+ { STB0899_DEMOD, 0x00 },
+ { STB0899_RCOMPC, 0xc9 },
+ { STB0899_AGC1CN, 0x01 },
+ { STB0899_AGC1REF, 0x10 },
+ { STB0899_RTC, 0x23 },
+ { STB0899_TMGCFG, 0x4e },
+ { STB0899_AGC2REF, 0x34 },
+ { STB0899_TLSR, 0x84 },
+ { STB0899_CFD, 0xf7 },
+ { STB0899_ACLC, 0x87 },
+ { STB0899_BCLC, 0x94 },
+ { STB0899_EQON, 0x41 },
+ { STB0899_LDT, 0xf1 },
+ { STB0899_LDT2, 0xe3 },
+ { STB0899_EQUALREF, 0xb4 },
+ { STB0899_TMGRAMP, 0x10 },
+ { STB0899_TMGTHD, 0x30 },
+ { STB0899_IDCCOMP, 0xfd },
+ { STB0899_QDCCOMP, 0xff },
+ { STB0899_POWERI, 0x0c },
+ { STB0899_POWERQ, 0x0f },
+ { STB0899_RCOMP, 0x6c },
+ { STB0899_AGCIQIN, 0x80 },
+ { STB0899_AGC2I1, 0x06 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_TLIR, 0x30 },
+ { STB0899_RTF, 0x7f },
+ { STB0899_DSTATUS, 0x00 },
+ { STB0899_LDI, 0xbc },
+ { STB0899_CFRM, 0xea },
+ { STB0899_CFRL, 0x31 },
+ { STB0899_NIRM, 0x2b },
+ { STB0899_NIRL, 0x80 },
+ { STB0899_ISYMB, 0x1d },
+ { STB0899_QSYMB, 0xa6 },
+ { STB0899_SFRH, 0x2f },
+ { STB0899_SFRM, 0x68 },
+ { STB0899_SFRL, 0x40 },
+ { STB0899_SFRUPH, 0x2f },
+ { STB0899_SFRUPM, 0x68 },
+ { STB0899_SFRUPL, 0x40 },
+ { STB0899_EQUAI1, 0x02 },
+ { STB0899_EQUAQ1, 0xff },
+ { STB0899_EQUAI2, 0x04 },
+ { STB0899_EQUAQ2, 0x05 },
+ { STB0899_EQUAI3, 0x02 },
+ { STB0899_EQUAQ3, 0xfd },
+ { STB0899_EQUAI4, 0x03 },
+ { STB0899_EQUAQ4, 0x07 },
+ { STB0899_EQUAI5, 0x08 },
+ { STB0899_EQUAQ5, 0xf5 },
+ { STB0899_DSTATUS2, 0x00 },
+ { STB0899_VSTATUS, 0x00 },
+ { STB0899_VERROR, 0x86 },
+ { STB0899_IQSWAP, 0x2a },
+ { STB0899_ECNT1M, 0x00 },
+ { STB0899_ECNT1L, 0x00 },
+ { STB0899_ECNT2M, 0x00 },
+ { STB0899_ECNT2L, 0x00 },
+ { STB0899_ECNT3M, 0x0a },
+ { STB0899_ECNT3L, 0xad },
+ { STB0899_FECAUTO1, 0x06 },
+ { STB0899_FECM, 0x01 },
+ { STB0899_VTH12, 0xb0 },
+ { STB0899_VTH23, 0x7a },
+ { STB0899_VTH34, 0x58 },
+ { STB0899_VTH56, 0x38 },
+ { STB0899_VTH67, 0x34 },
+ { STB0899_VTH78, 0x24 },
+ { STB0899_PRVIT, 0xff },
+ { STB0899_VITSYNC, 0x19 },
+ { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC, 0x42 },
+ { STB0899_RSLLC, 0x41 },
+ { STB0899_TSLPL, 0x12 },
+ { STB0899_TSCFGH, 0x0c },
+ { STB0899_TSCFGM, 0x00 },
+ { STB0899_TSCFGL, 0x00 },
+ { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL, 0x00 },
+ { STB0899_TSINHDELH, 0x02 },
+ { STB0899_TSINHDELM, 0x00 },
+ { STB0899_TSINHDELL, 0x00 },
+ { STB0899_TSLLSTKM, 0x1b },
+ { STB0899_TSLLSTKL, 0xb3 },
+ { STB0899_TSULSTKM, 0x00 },
+ { STB0899_TSULSTKL, 0x00 },
+ { STB0899_PCKLENUL, 0xbc },
+ { STB0899_PCKLENLL, 0xcc },
+ { STB0899_RSPCKLEN, 0xbd },
+ { STB0899_TSSTATUS, 0x90 },
+ { STB0899_ERRCTRL1, 0xb6 },
+ { STB0899_ERRCTRL2, 0x95 },
+ { STB0899_ERRCTRL3, 0x8d },
+ { STB0899_DMONMSK1, 0x27 },
+ { STB0899_DMONMSK0, 0x03 },
+ { STB0899_DEMAPVIT, 0x5c },
+ { STB0899_PLPARM, 0x19 },
+ { STB0899_PDELCTRL, 0x48 },
+ { STB0899_PDELCTRL2, 0x00 },
+ { STB0899_BBHCTRL1, 0x00 },
+ { STB0899_BBHCTRL2, 0x00 },
+ { STB0899_HYSTTHRESH, 0x77 },
+ { STB0899_MATCSTM, 0x00 },
+ { STB0899_MATCSTL, 0x00 },
+ { STB0899_UPLCSTM, 0x00 },
+ { STB0899_UPLCSTL, 0x00 },
+ { STB0899_DFLCSTM, 0x00 },
+ { STB0899_DFLCSTL, 0x00 },
+ { STB0899_SYNCCST, 0x00 },
+ { STB0899_SYNCDCSTM, 0x00 },
+ { STB0899_SYNCDCSTL, 0x00 },
+ { STB0899_ISI_ENTRY, 0x00 },
+ { STB0899_ISI_BIT_EN, 0x00 },
+ { STB0899_MATSTRM, 0xf0 },
+ { STB0899_MATSTRL, 0x02 },
+ { STB0899_UPLSTRM, 0x45 },
+ { STB0899_UPLSTRL, 0x60 },
+ { STB0899_DFLSTRM, 0xe3 },
+ { STB0899_DFLSTRL, 0x00 },
+ { STB0899_SYNCSTR, 0x47 },
+ { STB0899_SYNCDSTRM, 0x05 },
+ { STB0899_SYNCDSTRL, 0x18 },
+ { STB0899_CFGPDELSTATUS1, 0x19 },
+ { STB0899_CFGPDELSTATUS2, 0x2b },
+ { STB0899_BBFERRORM, 0x00 },
+ { STB0899_BBFERRORL, 0x01 },
+ { STB0899_UPKTERRORM, 0x00 },
+ { STB0899_UPKTERRORL, 0x00 },
+ { 0xffff, 0xff },
+};
+
+static struct stb0899_config stb0899_config = {
+ .init_dev = pctv452e_init_dev,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = pctv452e_init_s1_demod,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .demod_address = I2C_ADDR_STB0899, /* I2C Address */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* ? */
+
+ .xtal_freq = 27000000, /* Assume Hz ? */
+ .inversion = IQ_SWAP_ON, /* ? */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .ts_output_mode = 0, /* Use parallel mode */
+ .clock_polarity = 0,
+ .data_clk_parity = 0,
+ .fec_mode = 0,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL,
+
+ /* helper for switching LED green/orange */
+ .postproc = pctv45e_postproc
+};
+
+static struct stb6100_config stb6100_config = {
+ .tuner_address = I2C_ADDR_STB6100,
+ .refclock = 27000000
+};
+
+
+static struct i2c_algorithm pctv452e_i2c_algo = {
+ .master_xfer = pctv452e_i2c_xfer,
+ .functionality = pctv452e_i2c_func
+};
+
+static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
+{
+ struct usb_device_id *id;
+
+ a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config,
+ &a->dev->i2c_adap);
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
+ &a->dev->i2c_adap)) == 0)
+ err("Cannot attach lnbp22\n");
+
+ id = a->dev->desc->warm_ids[0];
+ if (USB_VID_TECHNOTREND == id->idVendor
+ && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct)
+ /* Error ignored. */
+ tt3650_ci_init(a);
+
+ return 0;
+}
+
+static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
+{
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
+ &a->dev->i2c_adap) == 0) {
+ err("%s failed\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct usb_device_id pctv452e_usb_table[] = {
+ {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)},
+ {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)},
+ {USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, pctv452e_usb_table);
+
+static struct dvb_usb_device_properties pctv452e_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 4,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */
+
+ .num_device_descs = 1,
+ .devices = {
+ { .name = "PCTV HDTV USB",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[0], NULL }
+ },
+ { 0 },
+ }
+};
+
+static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+ .read_mac_address = pctv452e_read_mac_address,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_TT_1500,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "Technotrend TT Connect S2-3600",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[1], NULL }
+ },
+ { .name = "Technotrend TT Connect S2-3650-CI",
+ .cold_ids = { NULL, NULL },
+ .warm_ids = { &pctv452e_usb_table[2], NULL }
+ },
+ { 0 },
+ }
+};
+
+static void pctv452e_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int pctv452e_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ if (0 == dvb_usb_device_init(intf, &pctv452e_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties,
+ THIS_MODULE, NULL, adapter_nr))
+ return 0;
+
+ return -ENODEV;
+}
+
+static struct usb_driver pctv452e_usb_driver = {
+ .name = "pctv452e",
+ .probe = pctv452e_usb_probe,
+ .disconnect = pctv452e_usb_disconnect,
+ .id_table = pctv452e_usb_table,
+};
+
+static int __init pctv452e_usb_init(void)
+{
+ int ret = usb_register(&pctv452e_usb_driver);
+ if (ret)
+ err("%s: usb_register failed! Error %d", __FILE__, ret);
+
+ return ret;
+}
+
+static void __exit pctv452e_usb_exit(void)
+{
+ usb_deregister(&pctv452e_usb_driver);
+}
+
+module_init(pctv452e_usb_init);
+module_exit(pctv452e_usb_exit);
+
+MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>");
+MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>");
+MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>");
+MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c
index 473b95ed4d52..0998fe961950 100644
--- a/drivers/media/dvb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c
@@ -292,7 +292,7 @@ static void technisat_usb2_green_led_control(struct work_struct *work)
{
struct technisat_usb2_state *state =
container_of(work, struct technisat_usb2_state, green_led_work.work);
- struct dvb_frontend *fe = state->dev->adapter[0].fe;
+ struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe;
if (state->power_state == 0)
goto schedule;
@@ -505,14 +505,14 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
struct usb_device *udev = a->dev->udev;
int ret;
- a->fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
+ a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
&a->dev->i2c_adap, STV090x_DEMODULATOR_0);
- if (a->fe) {
+ if (a->fe_adap[0].fe) {
struct stv6110x_devctl *ctl;
ctl = dvb_attach(stv6110x_attach,
- a->fe,
+ a->fe_adap[0].fe,
&technisat_usb2_stv6110x_config,
&a->dev->i2c_adap);
@@ -532,8 +532,8 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
/* call the init function once to initialize
tuner's clock output divider and demod's
master clock */
- if (a->fe->ops.init)
- a->fe->ops.init(a->fe);
+ if (a->fe_adap[0].fe->ops.init)
+ a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe);
if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0)
return -EAGAIN;
@@ -548,20 +548,20 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
if (ret != 0)
err("could not set IF_CLK to external");
- a->fe->ops.set_voltage = technisat_usb2_set_voltage;
+ a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage;
/* if everything was successful assign a nice name to the frontend */
- strlcpy(a->fe->ops.info.name, a->dev->desc->name,
- sizeof(a->fe->ops.info.name));
+ strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name,
+ sizeof(a->fe_adap[0].fe->ops.info.name));
} else {
- dvb_frontend_detach(a->fe);
- a->fe = NULL;
+ dvb_frontend_detach(a->fe_adap[0].fe);
+ a->fe_adap[0].fe = NULL;
}
}
technisat_usb2_set_led_timer(a->dev, 1, 1);
- return a->fe == NULL ? -ENODEV : 0;
+ return a->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* Remote control */
@@ -697,6 +697,8 @@ static struct dvb_usb_device_properties technisat_usb2_devices = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = technisat_usb2_frontend_attach,
.stream = {
@@ -711,7 +713,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = {
}
}
},
-
+ }},
.size_of_priv = 0,
},
},
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
index 0d4709ff9cbb..ea4eab8b3965 100644
--- a/drivers/media/dvb/dvb-usb/ttusb2.c
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -30,18 +30,43 @@
#include "tda826x.h"
#include "tda10086.h"
#include "tda1002x.h"
+#include "tda10048.h"
#include "tda827x.h"
#include "lnbp21.h"
+/* CA */
+#include "dvb_ca_en50221.h"
/* debug */
static int dvb_usb_ttusb2_debug;
#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args)
module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
+static int dvb_usb_ttusb2_debug_ci;
+module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644);
+MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+#define ci_dbg(format, arg...) \
+do { \
+ if (dvb_usb_ttusb2_debug_ci) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": %s " format "\n" , __func__, ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
struct ttusb2_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
u8 id;
u16 last_rc_key;
};
@@ -78,11 +103,260 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
return 0;
}
+/* ci */
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ int ret;
+ u8 rx[60];/* (64 -4) */
+ ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len);
+ if (!ret)
+ memcpy(data, rx, read_len);
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+ u8 buf[3];
+ int ret = 0;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%d 0x%04x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%d 0x%02x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d %d", slot, enable);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d", slot);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ msleep(1100);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (ret)
+ return ret;
+
+ if (1 == buf[0]) {
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ return 0;
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct ttusb2_state *state;
+
+ ci_dbg("");
+
+ if (NULL == d)
+ return;
+
+ state = d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ ci_dbg("");
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
static u8 obuf[60], ibuf[60];
- int i,read;
+ int i, write_read, read;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
@@ -91,28 +365,35 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num
warn("more than 2 i2c messages at a time is not handled yet. TODO.");
for (i = 0; i < num; i++) {
- read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ read = msg[i].flags & I2C_M_RD;
- obuf[0] = (msg[i].addr << 1) | read;
- obuf[1] = msg[i].len;
+ obuf[0] = (msg[i].addr << 1) | (write_read | read);
+ if (read)
+ obuf[1] = 0;
+ else
+ obuf[1] = msg[i].len;
/* read request */
- if (read)
+ if (write_read)
obuf[2] = msg[i+1].len;
+ else if (read)
+ obuf[2] = msg[i].len;
else
obuf[2] = 0;
- memcpy(&obuf[3],msg[i].buf,msg[i].len);
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) {
err("i2c transfer failed.");
break;
}
- if (read) {
- memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len);
+ if (write_read) {
+ memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len);
i++;
- }
+ } else if (read)
+ memcpy(msg[i].buf, &ibuf[3], msg[i].len);
}
mutex_unlock(&d->i2c_mutex);
@@ -190,12 +471,31 @@ static struct tda10023_config tda10023_config = {
.deltaf = 0xa511,
};
+static struct tda10048_config tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_4000,
+ .dtv7_if_freq_khz = TDA10048_IF_4500,
+ .dtv8_if_freq_khz = TDA10048_IF_5000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .no_firmware = 1,
+ .set_pll = true ,
+ .pll_m = 5,
+ .pll_n = 3,
+ .pll_p = 0,
+};
+
+static struct tda827x_config tda827x_config = {
+ .config = 0,
+};
+
static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev,0,3) < 0)
err("set interface to alts=3 failed");
- if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
+ if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
deb_info("TDA10086 attach failed\n");
return -ENODEV;
}
@@ -203,20 +503,57 @@ static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable);
+}
+
static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev, 0, 3) < 0)
err("set interface to alts=3 failed");
- if ((adap->fe = dvb_attach(tda10023_attach, &tda10023_config, &adap->dev->i2c_adap, 0x48)) == NULL) {
- deb_info("TDA10023 attach failed\n");
- return -ENODEV;
+
+ if (adap->fe_adap[0].fe == NULL) {
+ /* FE 0 DVB-C */
+ adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
+ &tda10023_config, &adap->dev->i2c_adap, 0x48);
+
+ if (adap->fe_adap[0].fe == NULL) {
+ deb_info("TDA10023 attach failed\n");
+ return -ENODEV;
+ }
+ tt3650_ci_init(adap);
+ } else {
+ adap->fe_adap[1].fe = dvb_attach(tda10048_attach,
+ &tda10048_config, &adap->dev->i2c_adap);
+
+ if (adap->fe_adap[1].fe == NULL) {
+ deb_info("TDA10048 attach failed\n");
+ return -ENODEV;
+ }
+
+ /* tuner is behind TDA10023 I2C-gate */
+ adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl;
+
}
+
return 0;
}
static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
{
- if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) {
+ struct dvb_frontend *fe;
+
+ /* MFE: select correct FE to attach tuner since that's called twice */
+ if (adap->fe_adap[1].fe == NULL)
+ fe = adap->fe_adap[0].fe;
+ else
+ fe = adap->fe_adap[1].fe;
+
+ /* attach tuner */
+ if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) {
printk(KERN_ERR "%s: No tda827x found!\n", __func__);
return -ENODEV;
}
@@ -225,12 +562,12 @@ static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap)
{
- if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
+ if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
deb_info("TDA8263 attach failed\n");
return -ENODEV;
}
- if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
+ if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
deb_info("LNBP21 attach failed\n");
return -ENODEV;
}
@@ -242,6 +579,14 @@ static struct dvb_usb_device_properties ttusb2_properties;
static struct dvb_usb_device_properties ttusb2_properties_s2400;
static struct dvb_usb_device_properties ttusb2_properties_ct3650;
+static void ttusb2_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
static int ttusb2_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -277,6 +622,8 @@ static struct dvb_usb_device_properties ttusb2_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
.frontend_attach = ttusb2_frontend_tda10086_attach,
@@ -295,6 +642,7 @@ static struct dvb_usb_device_properties ttusb2_properties = {
}
}
}
+ }},
}
},
@@ -329,6 +677,8 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10086_attach,
@@ -347,6 +697,7 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
}
}
}
+ }},
}
},
@@ -383,6 +734,27 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 2,
+ .fe = {{
+ .streaming_ctrl = NULL,
+
+ .frontend_attach = ttusb2_frontend_tda10023_attach,
+ .tuner_attach = ttusb2_tuner_tda827x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }, {
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10023_attach,
@@ -401,6 +773,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
}
}
}
+ }},
},
},
@@ -422,7 +795,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
static struct usb_driver ttusb2_driver = {
.name = "dvb_usb_ttusb2",
.probe = ttusb2_probe,
- .disconnect = dvb_usb_device_exit,
+ .disconnect = ttusb2_usb_disconnect,
.id_table = ttusb2_table,
};
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index 118aab1a3e54..463673a5c2b2 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -60,14 +60,14 @@ static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap)
umt_config.demod_init = umt_mt352_demod_init;
umt_config.demod_address = 0xf;
- adap->fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
return 0;
}
static int umt_tuner_attach (struct dvb_usb_adapter *adap)
{
- dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_TUA6034);
+ dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034);
return 0;
}
@@ -100,6 +100,8 @@ static struct dvb_usb_device_properties umt_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.frontend_attach = umt_mt352_frontend_attach,
.tuner_attach = umt_tuner_attach,
@@ -115,7 +117,7 @@ static struct dvb_usb_device_properties umt_properties = {
}
}
},
-
+ }},
.size_of_priv = sizeof(struct dibusb_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c
index 86d68933b6b4..d62ee0f5a165 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -148,7 +148,7 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!.\n");
for (j = 0; j < i; j++)
- usb_free_urb(stream->urb_list[i]);
+ usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
usb_fill_bulk_urb( stream->urb_list[i], stream->udev,
@@ -181,7 +181,7 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream)
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!\n");
for (j = 0; j < i; j++)
- usb_free_urb(stream->urb_list[i]);
+ usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index 54355f84a98f..45e31f224814 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -320,7 +320,7 @@ static int vp702x_frontend_attach(struct dvb_usb_adapter *adap)
vp702x_init_pid_filter(adap);
- adap->fe = vp702x_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev);
vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
return 0;
@@ -383,6 +383,8 @@ static struct dvb_usb_device_properties vp702x_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
.streaming_ctrl = vp702x_streaming_ctrl,
@@ -399,6 +401,7 @@ static struct dvb_usb_device_properties vp702x_properties = {
}
}
},
+ }},
.size_of_priv = sizeof(struct vp702x_adapter_state),
}
},
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index 536c16c943bd..90873af5682e 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -214,7 +214,7 @@ static int vp7045_frontend_attach(struct dvb_usb_adapter *adap)
/* Dump the EEPROM */
/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */
- adap->fe = vp7045_fe_attach(adap->dev);
+ adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev);
return 0;
}
@@ -245,6 +245,8 @@ static struct dvb_usb_device_properties vp7045_properties = {
.num_adapters = 1,
.adapter = {
{
+ .num_frontends = 1,
+ .fe = {{
.frontend_attach = vp7045_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
@@ -257,6 +259,7 @@ static struct dvb_usb_device_properties vp7045_properties = {
}
}
},
+ }},
}
},
.power_ctrl = vp7045_power_ctrl,
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 32e08e351525..4a2d2e6c91ab 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -236,6 +236,13 @@ config DVB_MB86A16
A DVB-S/DSS Direct Conversion reveiver.
Say Y when you want to support this frontend.
+config DVB_TDA10071
+ tristate "NXP TDA10071"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -600,6 +607,16 @@ config DVB_LNBP21
help
An SEC control chips.
+config DVB_LNBP22
+ tristate "LNBP22 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ LNB power supply and control voltage
+ regulator chip with step-up converter
+ and I2C interface.
+ Say Y when you want to support this chip.
+
config DVB_ISL6405
tristate "ISL6405 SEC controller"
depends on DVB_CORE && I2C
@@ -621,6 +638,11 @@ config DVB_ISL6423
help
A SEC controller chip from Intersil
+config DVB_A8293
+ tristate "Allegro A8293"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+
config DVB_LGS8GL5
tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
depends on DVB_CORE && I2C
@@ -661,6 +683,14 @@ config DVB_IX2505V
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_IT913X_FE
+ tristate "it913x frontend and it9137 tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module.
+ Say Y when you want to support this frontend.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 6a6ba053ead4..f639f6781551 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -2,8 +2,8 @@
# Makefile for the kernel DVB frontend device drivers.
#
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/common/tuners/
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
@@ -52,6 +52,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
+obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
obj-$(CONFIG_DVB_ISL6405) += isl6405.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
@@ -91,4 +92,7 @@ obj-$(CONFIG_DVB_STV0367) += stv0367.o
obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
obj-$(CONFIG_DVB_DRXK) += drxk.o
obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
+obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
+obj-$(CONFIG_DVB_A8293) += a8293.o
+obj-$(CONFIG_DVB_TDA10071) += tda10071.o
diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c
new file mode 100644
index 000000000000..bb56497e940a
--- /dev/null
+++ b/drivers/media/dvb/frontends/a8293.c
@@ -0,0 +1,184 @@
+/*
+ * Allegro A8293 SEC driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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 "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;
+ u8 reg[2];
+};
+
+static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd)
+{
+ int ret;
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ if (rd)
+ msg[0].flags = I2C_M_RD;
+ else
+ msg[0].flags = 0;
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c failed=%d rd=%d", ret, rd);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+static int a8293_wr(struct a8293_priv *priv, u8 *val, int len)
+{
+ return a8293_i2c(priv, val, len, 0);
+}
+
+static int a8293_rd(struct a8293_priv *priv, u8 *val, int len)
+{
+ return a8293_i2c(priv, val, len, 1);
+}
+
+static int a8293_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t fe_sec_voltage)
+{
+ struct a8293_priv *priv = fe->sec_priv;
+ int ret;
+
+ dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_OFF:
+ /* ENB=0 */
+ priv->reg[0] = 0x10;
+ break;
+ case SEC_VOLTAGE_13:
+ /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/
+ priv->reg[0] = 0x31;
+ break;
+ case SEC_VOLTAGE_18:
+ /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/
+ priv->reg[0] = 0x38;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ };
+
+ ret = a8293_wr(priv, &priv->reg[0], 1);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __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);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg)
+{
+ int ret;
+ struct a8293_priv *priv = NULL;
+ u8 buf[2];
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ priv->cfg = cfg;
+ fe->sec_priv = priv;
+
+ /* check if the SEC is there */
+ ret = a8293_rd(priv, buf, 2);
+ if (ret)
+ goto err;
+
+ /* ENB=0 */
+ priv->reg[0] = 0x10;
+ ret = a8293_wr(priv, &priv->reg[1], 1);
+ if (ret)
+ goto err;
+
+ /* TMODE=0, TGATE=1 */
+ priv->reg[1] = 0x82;
+ ret = a8293_wr(priv, &priv->reg[1], 1);
+ if (ret)
+ goto err;
+
+ info("Allegro A8293 SEC attached.");
+
+ fe->ops.release_sec = a8293_release_sec;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = a8293_set_voltage;
+
+ return fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(a8293_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Allegro A8293 SEC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212_priv.h b/drivers/media/dvb/frontends/a8293.h
index 9adff9356b73..ed29e5504f76 100644
--- a/drivers/media/common/tuners/tda18212_priv.h
+++ b/drivers/media/dvb/frontends/a8293.h
@@ -1,5 +1,5 @@
/*
- * NXP TDA18212HN silicon tuner driver
+ * Allegro A8293 SEC driver
*
* Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
*
@@ -18,27 +18,24 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef TDA18212_PRIV_H
-#define TDA18212_PRIV_H
+#ifndef A8293_H
+#define A8293_H
-#include "tda18212.h"
-
-#define LOG_PREFIX "tda18212"
-
-#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 tda18212_priv {
- struct tda18212_config *cfg;
- struct i2c_adapter *i2c;
+struct a8293_config {
+ u8 i2c_addr;
};
+#if defined(CONFIG_DVB_A8293) || \
+ (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE))
+extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg);
+#else
+static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct a8293_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
#endif
+
+#endif /* A8293_H */
diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h
index 2906582dc94c..03cab7b547fb 100644
--- a/drivers/media/dvb/frontends/cxd2820r.h
+++ b/drivers/media/dvb/frontends/cxd2820r.h
@@ -93,9 +93,6 @@ extern struct dvb_frontend *cxd2820r_attach(
struct i2c_adapter *i2c,
struct dvb_frontend *fe
);
-extern struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-);
#else
static inline struct dvb_frontend *cxd2820r_attach(
const struct cxd2820r_config *config,
@@ -106,12 +103,6 @@ static inline struct dvb_frontend *cxd2820r_attach(
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
-static inline struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-)
-{
- return NULL;
-}
#endif
diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c
index 3c07d400731d..b85f5011e344 100644
--- a/drivers/media/dvb/frontends/cxd2820r_c.c
+++ b/drivers/media/dvb/frontends/cxd2820r_c.c
@@ -335,4 +335,3 @@ int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
index d416e85589e1..036480f967b7 100644
--- a/drivers/media/dvb/frontends/cxd2820r_core.c
+++ b/drivers/media/dvb/frontends/cxd2820r_core.c
@@ -727,72 +727,22 @@ static void cxd2820r_release(struct dvb_frontend *fe)
struct cxd2820r_priv *priv = fe->demodulator_priv;
dbg("%s", __func__);
- if (fe->ops.info.type == FE_OFDM) {
- i2c_del_adapter(&priv->tuner_i2c_adapter);
+ if (fe->ops.info.type == FE_OFDM)
kfree(priv);
- }
return;
}
-static u32 cxd2820r_tuner_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static int cxd2820r_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg msg[], int num)
-{
- struct cxd2820r_priv *priv = i2c_get_adapdata(i2c_adap);
- int ret;
- u8 *obuf = kmalloc(msg[0].len + 2, GFP_KERNEL);
- struct i2c_msg msg2[2] = {
- {
- .addr = priv->cfg.i2c_address,
- .flags = 0,
- .len = msg[0].len + 2,
- .buf = obuf,
- }, {
- .addr = priv->cfg.i2c_address,
- .flags = I2C_M_RD,
- .len = msg[1].len,
- .buf = msg[1].buf,
- }
- };
-
- if (!obuf)
- return -ENOMEM;
-
- obuf[0] = 0x09;
- obuf[1] = (msg[0].addr << 1);
- if (num == 2) { /* I2C read */
- obuf[1] = (msg[0].addr << 1) | I2C_M_RD; /* I2C RD flag */
- msg2[0].len = msg[0].len + 2 - 1; /* '-1' maybe HW bug ? */
- }
- memcpy(&obuf[2], msg[0].buf, msg[0].len);
-
- ret = i2c_transfer(priv->i2c, msg2, num);
- if (ret < 0)
- warn("tuner i2c failed ret:%d", ret);
-
- kfree(obuf);
-
- return ret;
-}
-
-static struct i2c_algorithm cxd2820r_tuner_i2c_algo = {
- .master_xfer = cxd2820r_tuner_i2c_xfer,
- .functionality = cxd2820r_tuner_i2c_func,
-};
-
-struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
- return &priv->tuner_i2c_adapter;
+ dbg("%s: %d", __func__, enable);
+
+ /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */
+ return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1);
}
-EXPORT_SYMBOL(cxd2820r_get_tuner_i2c_adapter);
-static struct dvb_frontend_ops cxd2820r_ops[2];
+static const struct dvb_frontend_ops cxd2820r_ops[2];
struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
struct i2c_adapter *i2c, struct dvb_frontend *fe)
@@ -831,18 +781,6 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
priv->fe[0].demodulator_priv = priv;
priv->fe[1].demodulator_priv = priv;
- /* create tuner i2c adapter */
- strlcpy(priv->tuner_i2c_adapter.name,
- "CXD2820R tuner I2C adapter",
- sizeof(priv->tuner_i2c_adapter.name));
- priv->tuner_i2c_adapter.algo = &cxd2820r_tuner_i2c_algo;
- priv->tuner_i2c_adapter.algo_data = NULL;
- i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
- if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
- err("tuner I2C bus could not be initialized");
- goto error;
- }
-
return &priv->fe[0];
} else {
@@ -858,7 +796,7 @@ error:
}
EXPORT_SYMBOL(cxd2820r_attach);
-static struct dvb_frontend_ops cxd2820r_ops[2] = {
+static const struct dvb_frontend_ops cxd2820r_ops[2] = {
{
/* DVB-T/T2 */
.info = {
@@ -883,6 +821,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = {
.sleep = cxd2820r_sleep,
.get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
.get_frontend = cxd2820r_get_frontend,
@@ -911,6 +850,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = {
.sleep = cxd2820r_sleep,
.get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
.set_frontend = cxd2820r_set_frontend,
.get_frontend = cxd2820r_get_frontend,
diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h
index 0c0ebc9d5c4a..95539134efdb 100644
--- a/drivers/media/dvb/frontends/cxd2820r_priv.h
+++ b/drivers/media/dvb/frontends/cxd2820r_priv.h
@@ -50,7 +50,6 @@ struct cxd2820r_priv {
struct i2c_adapter *i2c;
struct dvb_frontend fe[2];
struct cxd2820r_config cfg;
- struct i2c_adapter tuner_i2c_adapter;
struct mutex fe_lock; /* FE lock */
int active_fe:2; /* FE lock, -1=NONE, 0=DVB-T/T2, 1=DVB-C */
diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c
index 6582564c930c..a04f9c810101 100644
--- a/drivers/media/dvb/frontends/cxd2820r_t.c
+++ b/drivers/media/dvb/frontends/cxd2820r_t.c
@@ -446,4 +446,3 @@ int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c
index c47b35c8acf1..6548588309f7 100644
--- a/drivers/media/dvb/frontends/cxd2820r_t2.c
+++ b/drivers/media/dvb/frontends/cxd2820r_t2.c
@@ -420,4 +420,3 @@ int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
return 0;
}
-
diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c
index 1d47d4da7d4c..dc1cb17a6ea7 100644
--- a/drivers/media/dvb/frontends/dib0070.c
+++ b/drivers/media/dvb/frontends/dib0070.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -78,10 +79,18 @@ struct dib0070_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[3];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
-static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
+static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
@@ -96,13 +105,23 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
printk(KERN_WARNING "DiB0070 I2C read failed\n");
- return 0;
- }
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
state->i2c_write_buffer[0] = reg;
state->i2c_write_buffer[1] = val >> 8;
state->i2c_write_buffer[2] = val & 0xff;
@@ -115,9 +134,12 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0070 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
#define HARD_RESET(state) do { \
@@ -734,6 +756,7 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
state->cfg = cfg;
state->i2c = i2c;
state->fe = fe;
+ mutex_init(&state->i2c_buffer_lock);
fe->tuner_priv = state;
if (dib0070_reset(fe) != 0)
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index c9c935ae41e4..b174d1c78583 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -196,6 +197,7 @@ struct dib0090_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[3];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
struct dib0090_fw_state {
@@ -208,10 +210,18 @@ struct dib0090_fw_state {
struct i2c_msg msg;
u8 i2c_write_buffer[2];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
@@ -226,14 +236,24 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
printk(KERN_WARNING "DiB0090 I2C read failed\n");
- return 0;
- }
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = reg & 0xff;
state->i2c_write_buffer[1] = val >> 8;
state->i2c_write_buffer[2] = val & 0xff;
@@ -246,13 +266,23 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg;
memset(&state->msg, 0, sizeof(struct i2c_msg));
@@ -262,13 +292,24 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C read failed\n");
- return 0;
- }
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = val >> 8;
state->i2c_write_buffer[1] = val & 0xff;
@@ -279,9 +320,12 @@ static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
printk(KERN_WARNING "DiB0090 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0)
@@ -2440,6 +2484,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
st->config = config;
st->i2c = i2c;
st->fe = fe;
+ mutex_init(&st->i2c_buffer_lock);
fe->tuner_priv = st;
if (config->wbd == NULL)
@@ -2471,6 +2516,7 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada
st->config = config;
st->i2c = i2c;
st->fe = fe;
+ mutex_init(&st->i2c_buffer_lock);
fe->tuner_priv = st;
if (dib0090_fw_reset_digital(fe, st->config) != 0)
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
index 79cb1c20df24..dbb76d75c932 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_frontend.h"
@@ -55,6 +56,7 @@ struct dib7000m_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib7000m_power_mode {
@@ -69,6 +71,13 @@ enum dib7000m_power_mode {
static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) | 0x80;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -85,11 +94,21 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
dprintk("i2c read error on %d",reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -101,7 +120,10 @@ static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf)
{
@@ -1385,6 +1407,7 @@ struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));
+ mutex_init(&st->i2c_buffer_lock);
st->timf_default = cfg->bw->timf;
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index a64a538ba364..ce8534ff142e 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dvb_math.h"
#include "dvb_frontend.h"
@@ -68,6 +69,7 @@ struct dib7000p_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib7000p_power_mode {
@@ -81,6 +83,13 @@ static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff);
static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg >> 8;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -97,11 +106,20 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -113,7 +131,10 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
}
static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf)
@@ -1577,8 +1598,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
return -ENOMEM;
rx = kzalloc(2*sizeof(u8), GFP_KERNEL);
if (!rx) {
- goto rx_memory_error;
ret = -ENOMEM;
+ goto rx_memory_error;
}
msg[0].buf = tx;
@@ -1646,6 +1667,7 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
return -ENOMEM;
dpst->i2c_adap = i2c;
+ mutex_init(&dpst->i2c_buffer_lock);
for (k = no_of_demods - 1; k >= 0; k--) {
dpst->cfg = cfg[k];
@@ -2324,6 +2346,7 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+ mutex_init(&st->i2c_buffer_lock);
dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
@@ -2333,8 +2356,9 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
st->version = dib7000p_read_word(st, 897);
/* FIXME: make sure the dev.parent field is initialized, or else
- request_firmware() will hit an OOPS (this should be moved somewhere
- more common) */
+ request_firmware() will hit an OOPS (this should be moved somewhere
+ more common) */
+ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
/* FIXME: make sure the dev.parent field is initialized, or else
request_firmware() will hit an OOPS (this should be moved somewhere
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 7d2ea112ae2b..fe284d5292f5 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -10,6 +10,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
+
#include "dvb_math.h"
#include "dvb_frontend.h"
@@ -37,6 +39,7 @@ struct i2c_device {
u8 addr;
u8 *i2c_write_buffer;
u8 *i2c_read_buffer;
+ struct mutex *i2c_buffer_lock;
};
struct dib8000_state {
@@ -77,6 +80,7 @@ struct dib8000_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[4];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
enum dib8000_power_mode {
@@ -86,24 +90,39 @@ enum dib8000_power_mode {
static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg)
{
+ u16 ret;
struct i2c_msg msg[2] = {
- {.addr = i2c->addr >> 1, .flags = 0,
- .buf = i2c->i2c_write_buffer, .len = 2},
- {.addr = i2c->addr >> 1, .flags = I2C_M_RD,
- .buf = i2c->i2c_read_buffer, .len = 2},
+ {.addr = i2c->addr >> 1, .flags = 0, .len = 2},
+ {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2},
};
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
+ msg[0].buf = i2c->i2c_write_buffer;
msg[0].buf[0] = reg >> 8;
msg[0].buf[1] = reg & 0xff;
+ msg[1].buf = i2c->i2c_read_buffer;
if (i2c_transfer(i2c->adap, msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (msg[1].buf[0] << 8) | msg[1].buf[1];
+ ret = (msg[1].buf[0] << 8) | msg[1].buf[1];
+ mutex_unlock(i2c->i2c_buffer_lock);
+ return ret;
}
static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
state->i2c_write_buffer[0] = reg >> 8;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -120,7 +139,10 @@ static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
@@ -135,22 +157,35 @@ static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
{
- struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0,
- .buf = i2c->i2c_write_buffer, .len = 4};
+ struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4};
int ret = 0;
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
+ msg.buf = i2c->i2c_write_buffer;
msg.buf[0] = (reg >> 8) & 0xff;
msg.buf[1] = reg & 0xff;
msg.buf[2] = (val >> 8) & 0xff;
msg.buf[3] = val & 0xff;
ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+ mutex_unlock(i2c->i2c_buffer_lock);
return ret;
}
static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
state->i2c_write_buffer[1] = reg & 0xff;
state->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -162,7 +197,11 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
state->msg[0].buf = state->i2c_write_buffer;
state->msg[0].len = 4;
- return i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
}
static const s16 coeff_2k_sb_1seg_dqpsk[8] = {
@@ -2434,8 +2473,15 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau
if (!client.i2c_read_buffer) {
dprintk("%s: not enough memory", __func__);
ret = -ENOMEM;
- goto error_memory;
+ goto error_memory_read;
+ }
+ client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (!client.i2c_buffer_lock) {
+ dprintk("%s: not enough memory", __func__);
+ ret = -ENOMEM;
+ goto error_memory_lock;
}
+ mutex_init(client.i2c_buffer_lock);
for (k = no_of_demods - 1; k >= 0; k--) {
/* designated i2c address */
@@ -2476,8 +2522,10 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau
}
error:
+ kfree(client.i2c_buffer_lock);
+error_memory_lock:
kfree(client.i2c_read_buffer);
-error_memory:
+error_memory_read:
kfree(client.i2c_write_buffer);
return ret;
@@ -2581,6 +2629,8 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s
state->i2c.addr = i2c_addr;
state->i2c.i2c_write_buffer = state->i2c_write_buffer;
state->i2c.i2c_read_buffer = state->i2c_read_buffer;
+ mutex_init(&state->i2c_buffer_lock);
+ state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock;
state->gpio_val = cfg->gpio_val;
state->gpio_dir = cfg->gpio_dir;
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
index a0855883b5ce..660f80661ed4 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -38,6 +38,15 @@ struct i2c_device {
#define DibInitLock(lock) mutex_init(lock)
#define DibFreeLock(lock)
+struct dib9000_pid_ctrl {
+#define DIB9000_PID_FILTER_CTRL 0
+#define DIB9000_PID_FILTER 1
+ u8 cmd;
+ u8 id;
+ u16 pid;
+ u8 onoff;
+};
+
struct dib9000_state {
struct i2c_device i2c;
@@ -99,6 +108,10 @@ struct dib9000_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[255];
u8 i2c_read_buffer[255];
+ DIB_LOCK demod_lock;
+ u8 get_frontend_internal;
+ struct dib9000_pid_ctrl pid_ctrl[10];
+ s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */
};
static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1167,8 +1180,8 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_p
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- goto error;
ret = -EIO;
+ goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION,
@@ -1743,19 +1756,56 @@ EXPORT_SYMBOL(dib9000_set_gpio);
int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
- u16 val = dib9000_read_word(state, 294 + 1) & 0xffef;
+ u16 val;
+ int ret;
+
+ if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) {
+ /* postpone the pid filtering cmd */
+ dprintk("pid filter cmd postpone");
+ state->pid_ctrl_index++;
+ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL;
+ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
+ return 0;
+ }
+
+ DibAcquireLock(&state->demod_lock);
+
+ val = dib9000_read_word(state, 294 + 1) & 0xffef;
val |= (onoff & 0x1) << 4;
dprintk("PID filter enabled %d", onoff);
- return dib9000_write_word(state, 294 + 1, val);
+ ret = dib9000_write_word(state, 294 + 1, val);
+ DibReleaseLock(&state->demod_lock);
+ return ret;
+
}
EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl);
int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (state->pid_ctrl_index != -2) {
+ /* postpone the pid filtering cmd */
+ dprintk("pid filter postpone");
+ if (state->pid_ctrl_index < 9) {
+ state->pid_ctrl_index++;
+ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER;
+ state->pid_ctrl[state->pid_ctrl_index].id = id;
+ state->pid_ctrl[state->pid_ctrl_index].pid = pid;
+ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
+ } else
+ dprintk("can not add any more pid ctrl cmd");
+ return 0;
+ }
+
+ DibAcquireLock(&state->demod_lock);
dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
- return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0);
+ ret = dib9000_write_word(state, 300 + 1 + id,
+ onoff ? (1 << 13) | pid : 0);
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
EXPORT_SYMBOL(dib9000_fw_pid_filter);
@@ -1778,6 +1828,7 @@ static void dib9000_release(struct dvb_frontend *demod)
DibFreeLock(&state->platform.risc.mbx_lock);
DibFreeLock(&state->platform.risc.mem_lock);
DibFreeLock(&state->platform.risc.mem_mbx_lock);
+ DibFreeLock(&state->demod_lock);
dibx000_exit_i2c_master(&st->i2c_master);
i2c_del_adapter(&st->tuner_adap);
@@ -1795,14 +1846,19 @@ static int dib9000_sleep(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
- int ret;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
if (ret < 0)
- return ret;
+ goto error;
}
- return dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
+ ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
@@ -1816,7 +1872,10 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend, sub_index_frontend;
fe_status_t stat;
- int ret;
+ int ret = 0;
+
+ if (state->get_frontend_internal == 0)
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
@@ -1846,14 +1905,15 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
state->fe[index_frontend]->dtv_property_cache.rolloff;
}
}
- return 0;
+ ret = 0;
+ goto return_value;
}
}
/* get the channel from master chip */
ret = dib9000_fw_get_channel(fe, fep);
if (ret != 0)
- return ret;
+ goto return_value;
/* synchronize the cache with the other frontends */
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
@@ -1866,8 +1926,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP;
state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff;
}
+ ret = 0;
- return 0;
+return_value:
+ if (state->get_frontend_internal == 0)
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
@@ -1912,6 +1976,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
dprintk("dib9000: must specify bandwidth ");
return 0;
}
+
+ state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
+ DibAcquireLock(&state->demod_lock);
+
fe->dtv_property_cache.delivery_system = SYS_DVBT;
/* set the master status */
@@ -1974,13 +2042,18 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
/* check the tune result */
if (exit_condition == 1) { /* tune failed */
dprintk("tune failed");
+ DibReleaseLock(&state->demod_lock);
+ /* tune failed; put all the pid filtering cmd to junk */
+ state->pid_ctrl_index = -1;
return 0;
}
dprintk("tune success on frontend%i", index_frontend_success);
/* synchronize all the channel cache */
+ state->get_frontend_internal = 1;
dib9000_get_frontend(state->fe[0], fep);
+ state->get_frontend_internal = 0;
/* retune the other frontends with the found channel */
channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
@@ -2025,6 +2098,28 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
/* turn off the diversity for the last frontend */
dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0);
+ DibReleaseLock(&state->demod_lock);
+ if (state->pid_ctrl_index >= 0) {
+ u8 index_pid_filter_cmd;
+ u8 pid_ctrl_index = state->pid_ctrl_index;
+
+ state->pid_ctrl_index = -2;
+ for (index_pid_filter_cmd = 0;
+ index_pid_filter_cmd <= pid_ctrl_index;
+ index_pid_filter_cmd++) {
+ if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL)
+ dib9000_fw_pid_filter_ctrl(state->fe[0],
+ state->pid_ctrl[index_pid_filter_cmd].onoff);
+ else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER)
+ dib9000_fw_pid_filter(state->fe[0],
+ state->pid_ctrl[index_pid_filter_cmd].id,
+ state->pid_ctrl[index_pid_filter_cmd].pid,
+ state->pid_ctrl[index_pid_filter_cmd].onoff);
+ }
+ }
+ /* do not postpone any more the pid filtering */
+ state->pid_ctrl_index = -2;
+
return 0;
}
@@ -2041,6 +2136,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
+ DibAcquireLock(&state->demod_lock);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
@@ -2059,6 +2155,8 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if ((lock & 0x0008) || (lock_slave & 0x0008))
*stat |= FE_HAS_LOCK;
+ DibReleaseLock(&state->demod_lock);
+
return 0;
}
@@ -2066,10 +2164,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR,
state->i2c_read_buffer, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2077,7 +2180,10 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
c = (u16 *)state->i2c_read_buffer;
*ber = c[10] << 16 | c[11];
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
@@ -2086,7 +2192,9 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
u8 index_frontend;
u16 *c = (u16 *)state->i2c_read_buffer;
u16 val;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
*strength = 0;
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
@@ -2097,8 +2205,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
}
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2107,7 +2217,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength = 65535;
else
*strength += val;
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
static u32 dib9000_get_snr(struct dvb_frontend *fe)
@@ -2151,6 +2264,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u8 index_frontend;
u32 snr_master;
+ DibAcquireLock(&state->demod_lock);
snr_master = dib9000_get_snr(fe);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
snr_master += dib9000_get_snr(state->fe[index_frontend]);
@@ -2161,6 +2275,8 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
} else
*snr = 0;
+ DibReleaseLock(&state->demod_lock);
+
return 0;
}
@@ -2168,15 +2284,22 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c = (u16 *)state->i2c_read_buffer;
+ int ret = 0;
+ DibAcquireLock(&state->demod_lock);
DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ ret = -EIO;
+ goto error;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
*unc = c[12];
- return 0;
+
+error:
+ DibReleaseLock(&state->demod_lock);
+ return ret;
}
int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr)
@@ -2322,6 +2445,10 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c
DibInitLock(&st->platform.risc.mbx_lock);
DibInitLock(&st->platform.risc.mem_lock);
DibInitLock(&st->platform.risc.mem_mbx_lock);
+ DibInitLock(&st->demod_lock);
+ st->get_frontend_internal = 0;
+
+ st->pid_ctrl_index = -2;
st->fe[0] = fe;
fe->demodulator_priv = st;
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index dc5d17a67579..774d507b66cc 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -1,4 +1,5 @@
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include "dibx000_common.h"
@@ -10,6 +11,13 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
{
+ int ret;
+
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
mst->i2c_write_buffer[0] = (reg >> 8) & 0xff;
mst->i2c_write_buffer[1] = reg & 0xff;
mst->i2c_write_buffer[2] = (val >> 8) & 0xff;
@@ -21,11 +29,21 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
mst->msg[0].buf = mst->i2c_write_buffer;
mst->msg[0].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0;
+ ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0;
+ mutex_unlock(&mst->i2c_buffer_lock);
+
+ return ret;
}
static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
mst->i2c_write_buffer[0] = reg >> 8;
mst->i2c_write_buffer[1] = reg & 0xff;
@@ -42,7 +60,10 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2)
dprintk("i2c read error on %d", reg);
- return (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1];
+ ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1];
+ mutex_unlock(&mst->i2c_buffer_lock);
+
+ return ret;
}
static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst)
@@ -257,6 +278,7 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int ret;
if (num > 32) {
dprintk("%s: too much I2C message to be transmitted (%i).\
@@ -264,10 +286,15 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
return -ENOMEM;
}
- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
-
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+
+ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
+
/* open the gate */
dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1);
mst->msg[0].addr = mst->i2c_addr;
@@ -282,7 +309,11 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
mst->msg[num + 1].buf = &mst->i2c_write_buffer[4];
mst->msg[num + 1].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO;
+ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ?
+ num : -EIO);
+
+ mutex_unlock(&mst->i2c_buffer_lock);
+ return ret;
}
static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = {
@@ -294,6 +325,7 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int ret;
if (num > 32) {
dprintk("%s: too much I2C message to be transmitted (%i).\
@@ -301,10 +333,14 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
return -ENOMEM;
}
- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
-
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
+
/* open the gate */
dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1);
mst->msg[0].addr = mst->i2c_addr;
@@ -319,7 +355,10 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
mst->msg[num + 1].buf = &mst->i2c_write_buffer[4];
mst->msg[num + 1].len = 4;
- return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO;
+ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ?
+ num : -EIO);
+ mutex_unlock(&mst->i2c_buffer_lock);
+ return ret;
}
static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
@@ -390,8 +429,18 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
- u8 tx[4];
- struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 };
+ int ret;
+
+ mutex_init(&mst->i2c_buffer_lock);
+ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return -EINVAL;
+ }
+ memset(mst->msg, 0, sizeof(struct i2c_msg));
+ mst->msg[0].addr = i2c_addr >> 1;
+ mst->msg[0].flags = 0;
+ mst->msg[0].buf = mst->i2c_write_buffer;
+ mst->msg[0].len = 4;
mst->device_rev = device_rev;
mst->i2c_adap = i2c_adap;
@@ -431,9 +480,12 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
"DiBX000: could not initialize the master i2c_adapter\n");
/* initialize the i2c-master by closing the gate */
- dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
+ dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0);
+
+ ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1);
+ mutex_unlock(&mst->i2c_buffer_lock);
- return i2c_transfer(i2c_adap, &m, 1) == 1;
+ return ret;
}
EXPORT_SYMBOL(dibx000_init_i2c_master);
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index f031165c0459..5e011474be43 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -33,6 +33,7 @@ struct dibx000_i2c_master {
struct i2c_msg msg[34];
u8 i2c_write_buffer[8];
u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
};
extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst,
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c
index 2238bf0be959..88e46f4cdbb2 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb/frontends/drxd_hard.c
@@ -889,10 +889,15 @@ static int ReadIFAgc(struct drxd_state *state, u32 * pValue)
u32 R2 = state->if_agc_cfg.R2;
u32 R3 = state->if_agc_cfg.R3;
- u32 Vmax = (3300 * R2) / (R1 + R2);
- u32 Rpar = (R2 * R3) / (R3 + R2);
- u32 Vmin = (3300 * Rpar) / (R1 + Rpar);
- u32 Vout = Vmin + ((Vmax - Vmin) * Value) / 1024;
+ u32 Vmax, Rpar, Vmin, Vout;
+
+ if (R2 == 0 && (R1 == 0 || R3 == 0))
+ return 0;
+
+ Vmax = (3300 * R2) / (R1 + R2);
+ Rpar = (R2 * R3) / (R3 + R2);
+ Vmin = (3300 * Rpar) / (R1 + Rpar);
+ Vout = Vmin + ((Vmax - Vmin) * Value) / 1024;
*pValue = Vout;
}
@@ -926,16 +931,15 @@ static int DownloadMicrocode(struct drxd_state *state,
const u8 *pMCImage, u32 Length)
{
u8 *pSrc;
- u16 Flags;
u32 Address;
u16 nBlocks;
u16 BlockSize;
- u16 BlockCRC;
u32 offset = 0;
int i, status = 0;
pSrc = (u8 *) pMCImage;
- Flags = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using Flags */
+ /* Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
nBlocks = (pSrc[0] << 8) | pSrc[1];
@@ -952,11 +956,13 @@ static int DownloadMicrocode(struct drxd_state *state,
pSrc += sizeof(u16);
offset += sizeof(u16);
- Flags = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using Flags */
+ /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
- BlockCRC = (pSrc[0] << 8) | pSrc[1];
+ /* We're not using BlockCRC */
+ /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
offset += sizeof(u16);
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
index 41b083820dae..f6431ef827dc 100644
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -6211,6 +6211,14 @@ static int drxk_set_parameters(struct dvb_frontend *fe,
u32 IF;
dprintk(1, "\n");
+
+ 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");
+ return -EINVAL;
+ }
+
+
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (fe->ops.tuner_ops.set_params)
@@ -6218,7 +6226,7 @@ static int drxk_set_parameters(struct dvb_frontend *fe,
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
state->param = *p;
- fe->ops.tuner_ops.get_frequency(fe, &IF);
+ fe->ops.tuner_ops.get_if_frequency(fe, &IF);
Start(state, 0, IF);
/* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h
new file mode 100644
index 000000000000..1c6fb4b66255
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe-priv.h
@@ -0,0 +1,336 @@
+
+struct it913xset { u32 pro;
+ u32 address;
+ u8 reg[15];
+ u8 count;
+};
+
+struct adctable { u32 adcFrequency;
+ u32 bandwidth;
+ u32 coeff_1_2048;
+ u32 coeff_1_4096;
+ u32 coeff_1_8191;
+ u32 coeff_1_8192;
+ u32 coeff_1_8193;
+ u32 coeff_2_2k;
+ u32 coeff_2_4k;
+ u32 coeff_2_8k;
+ u16 bfsfcw_fftinx_ratio;
+ u16 fftinx_bfsfcw_ratio;
+};
+
+/* clock and coeff tables only table 3 is used with IT9137*/
+/* TODO other tables relate AF9035 may be removed */
+static struct adctable tab1[] = {
+ { 20156250, BANDWIDTH_6_MHZ,
+ 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a,
+ 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c },
+ { 20156250, BANDWIDTH_7_MHZ,
+ 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007,
+ 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 },
+ { 20156250, BANDWIDTH_8_MHZ,
+ 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3,
+ 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 },
+ { 20156250, BANDWIDTH_5_MHZ,
+ 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e,
+ 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 }
+};
+
+static struct adctable tab2[] = {
+ { 20187500, BANDWIDTH_6_MHZ,
+ 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426,
+ 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c },
+ { 20187500, BANDWIDTH_7_MHZ,
+ 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81,
+ 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 },
+ { 20187500, BANDWIDTH_8_MHZ,
+ 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd,
+ 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 },
+ { 20187500, BANDWIDTH_5_MHZ,
+ 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca,
+ 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 }
+
+};
+
+static struct adctable tab3[] = {
+ { 20250000, BANDWIDTH_6_MHZ,
+ 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1,
+ 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b },
+ { 20250000, BANDWIDTH_7_MHZ,
+ 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36,
+ 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 },
+ { 20250000, BANDWIDTH_8_MHZ,
+ 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab,
+ 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce },
+ { 20250000, BANDWIDTH_5_MHZ,
+ 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b,
+ 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 }
+
+};
+
+static struct adctable tab4[] = {
+ { 20583333, BANDWIDTH_6_MHZ,
+ 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12,
+ 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 },
+ { 20583333, BANDWIDTH_7_MHZ,
+ 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf,
+ 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e },
+ { 20583333, BANDWIDTH_8_MHZ,
+ 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d,
+ 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 },
+ { 20583333, BANDWIDTH_5_MHZ,
+ 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64,
+ 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c }
+
+};
+
+static struct adctable tab5[] = {
+ { 20416667, BANDWIDTH_6_MHZ,
+ 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a,
+ 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 },
+ { 20416667, BANDWIDTH_7_MHZ,
+ 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e,
+ 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 },
+ { 20416667, BANDWIDTH_8_MHZ,
+ 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2,
+ 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb },
+ { 20416667, BANDWIDTH_5_MHZ,
+ 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865,
+ 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f }
+
+};
+
+static struct adctable tab6[] = {
+ { 20480000, BANDWIDTH_6_MHZ,
+ 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c,
+ 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 },
+ { 20480000, BANDWIDTH_7_MHZ,
+ 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0,
+ 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 },
+ { 20480000, BANDWIDTH_8_MHZ,
+ 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25,
+ 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 },
+ { 20480000, BANDWIDTH_5_MHZ,
+ 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7,
+ 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e }
+};
+
+static struct adctable tab7[] = {
+ { 20500000, BANDWIDTH_6_MHZ,
+ 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c,
+ 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 },
+ { 20500000, BANDWIDTH_7_MHZ,
+ 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce,
+ 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 },
+ { 20500000, BANDWIDTH_8_MHZ,
+ 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210,
+ 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 },
+ { 20500000, BANDWIDTH_5_MHZ,
+ 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a,
+ 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d }
+
+};
+
+static struct adctable tab8[] = {
+ { 20625000, BANDWIDTH_6_MHZ,
+ 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de,
+ 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 },
+ { 20625000, BANDWIDTH_7_MHZ,
+ 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8,
+ 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d },
+ { 20625000, BANDWIDTH_8_MHZ,
+ 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2,
+ 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 },
+ { 20625000, BANDWIDTH_5_MHZ,
+ 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3,
+ 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c }
+
+};
+
+struct table {
+ u32 xtal;
+ struct adctable *table;
+};
+
+static struct table fe_clockTable[] = {
+ {12000000, tab3}, /* FPGA */
+ {16384000, tab6}, /* 16.38MHz */
+ {20480000, tab6}, /* 20.48MHz */
+ {36000000, tab3}, /* 36.00MHz */
+ {30000000, tab1}, /* 30.00MHz */
+ {26000000, tab4}, /* 26.00MHz */
+ {28000000, tab5}, /* 28.00MHz */
+ {32000000, tab7}, /* 32.00MHz */
+ {34000000, tab2}, /* 34.00MHz */
+ {24000000, tab1}, /* 24.00MHz */
+ {22000000, tab8}, /* 22.00MHz */
+ {12000000, tab3} /* 12.00MHz */
+};
+
+/* fe get */
+fe_code_rate_t fe_code[] = {
+ FEC_1_2,
+ FEC_2_3,
+ FEC_3_4,
+ FEC_5_6,
+ FEC_7_8,
+ FEC_NONE,
+};
+
+fe_guard_interval_t fe_gi[] = {
+ GUARD_INTERVAL_1_32,
+ GUARD_INTERVAL_1_16,
+ GUARD_INTERVAL_1_8,
+ GUARD_INTERVAL_1_4,
+};
+
+fe_hierarchy_t fe_hi[] = {
+ HIERARCHY_NONE,
+ HIERARCHY_1,
+ HIERARCHY_2,
+ HIERARCHY_4,
+};
+
+fe_transmit_mode_t fe_mode[] = {
+ TRANSMISSION_MODE_2K,
+ TRANSMISSION_MODE_8K,
+ TRANSMISSION_MODE_4K,
+};
+
+fe_modulation_t fe_con[] = {
+ QPSK,
+ QAM_16,
+ QAM_64,
+};
+
+/* Standard demodulator functions */
+static struct it913xset set_solo_fe[] = {
+ {PRO_LINK, DVBT_INTEN, {0x04}, 0x01},
+ {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01},
+ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01},
+ {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01},
+ {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01},
+ {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01},
+ {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01},
+ {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01},
+ {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01},
+ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
+ {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+
+static struct it913xset init_1[] = {
+ {PRO_LINK, LOCK3_OUT, {0x01}, 0x01},
+ {PRO_LINK, PADMISCDRSR, {0x01}, 0x01},
+ {PRO_LINK, PADMISCDR2, {0x00}, 0x01},
+ {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */
+ {PRO_LINK, PADMISCDR8, {0x00}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
+};
+
+/* ---------IT9137 0x38 tuner init---------- */
+static struct it913xset it9137_set[] = {
+ {PRO_DMOD, 0x0043, {0x00}, 0x01},
+ {PRO_DMOD, 0x0046, {0x38}, 0x01},
+ {PRO_DMOD, 0x0051, {0x01}, 0x01},
+ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0x0068, {0x0a}, 0x01},
+ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
+ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05},
+ {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02},
+ {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8,
+ 0xd0, 0xc3, 0x01 }, 0x0a},
+ {PRO_DMOD, 0x008e, {0x01}, 0x01},
+ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
+ {PRO_DMOD, 0x0099, {0x01}, 0x01},
+ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
+ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
+ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
+ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
+ {PRO_DMOD, 0x00b0, {0x01}, 0x01},
+ {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02},
+ {PRO_DMOD, 0x00b6, {0x14}, 0x01},
+ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03},
+ {PRO_DMOD, 0x00c4, {0x00}, 0x01},
+ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
+ {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03},
+ {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03},
+ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
+ {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77,
+ 0x00, 0x02, 0xc8, 0x05, 0x7b }, 0x0c},
+ {PRO_DMOD, 0x0109, {0x02}, 0x01},
+ {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02},
+ {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0xbc, 0xa0}, 0x04},
+ {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03},
+ {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02},
+ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
+ {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05},
+ {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8}, 0x04},
+ {PRO_DMOD, 0xf130, {0x04}, 0x01},
+ {PRO_DMOD, 0xf132, {0x04}, 0x01},
+ {PRO_DMOD, 0xf144, {0x1a}, 0x01},
+ {PRO_DMOD, 0xf146, {0x00}, 0x01},
+ {PRO_DMOD, 0xf14a, {0x01}, 0x01},
+ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0xf14f, {0x04}, 0x01},
+ {PRO_DMOD, 0xf158, {0x7f}, 0x01},
+ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
+ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
+ {PRO_DMOD, 0xf163, {0x05}, 0x01},
+ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
+ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
+ {PRO_DMOD, 0xf183, {0x01}, 0x01},
+ {PRO_DMOD, 0xf19d, {0x40}, 0x01},
+ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
+ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
+ {PRO_DMOD, 0xf204, {0x10}, 0x01},
+ {PRO_DMOD, 0xf214, {0x00}, 0x01},
+ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
+ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
+ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
+ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
+ {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
+ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
+ {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02},
+ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
+ {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
+ {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
+ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
+ 0x1f }, 0x08},
+ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
+ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
+ {PRO_DMOD, 0xf78b, {0x01}, 0x01},
+ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
+ {PRO_DMOD, 0xf905, {0x01}, 0x01},
+ {PRO_DMOD, 0xfb06, {0x03}, 0x01},
+ {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
+ {PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
+ {PRO_LINK, GPIOH5_ON, {0x01}, 0x01},
+ {PRO_LINK, GPIOH5_O, {0x00}, 0x01},
+ {PRO_LINK, GPIOH5_O, {0x01}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+static struct it913xset it9137_tuner_off[] = {
+ {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */
+ {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */
+ {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04},
+ {PRO_DMOD, 0xec3f, {0x01}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
+
+static struct it913xset set_it9137_template[] = {
+ {PRO_DMOD, 0xee06, {0x00}, 0x01},
+ {PRO_DMOD, 0xec56, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4c, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4d, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4e, {0x00}, 0x01},
+ {PRO_DMOD, 0xec4f, {0x00}, 0x01},
+ {PRO_DMOD, 0xec50, {0x00}, 0x01},
+ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
+};
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
new file mode 100644
index 000000000000..d4bd24eb4700
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe.c
@@ -0,0 +1,839 @@
+/*
+ * Driver for it913x-fe Frontend
+ *
+ * with support for on chip it9137 integral tuner
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9137 Copyright (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "dvb_frontend.h"
+#include "it913x-fe.h"
+#include "it913x-fe-priv.h"
+
+static int it913x_debug;
+
+module_param_named(debug, it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & it913x_debug) \
+ printk(KERN_DEBUG "it913x-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define debug_data_snipet(level, name, p) \
+ dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+
+struct it913x_fe_state {
+ struct dvb_frontend frontend;
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+ u32 frequency;
+ u8 adf;
+ u32 crystalFrequency;
+ u32 adcFrequency;
+ u8 tuner_type;
+ struct adctable *table;
+ fe_status_t it913x_status;
+ u16 tun_xtal;
+ u8 tun_fdiv;
+ u8 tun_clk_mode;
+ u32 tun_fn_min;
+};
+
+static int it913x_read_reg(struct it913x_fe_state *state,
+ u32 reg, u8 *data, u8 count)
+{
+ int ret;
+ u8 pro = PRO_DMOD; /* All reads from demodulator */
+ u8 b[4];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = sizeof(b) },
+ { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD,
+ .buf = data, .len = count }
+ };
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+
+ ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+ return ret;
+}
+
+static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg)
+{
+ int ret;
+ u8 b[1];
+ ret = it913x_read_reg(state, reg, &b[0], sizeof(b));
+ return (ret < 0) ? -ENODEV : b[0];
+}
+
+static int it913x_write(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u8 buf[], u8 count)
+{
+ u8 b[256];
+ struct i2c_msg msg[1] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = count + 4 }
+ };
+ int ret;
+
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+ memcpy(&b[4], buf, count);
+
+ ret = i2c_transfer(state->i2c_adap, msg, 1);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int it913x_write_reg(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u32 data)
+{
+ int ret;
+ u8 b[4];
+ u8 s;
+
+ b[0] = data >> 24;
+ b[1] = (data >> 16) & 0xff;
+ b[2] = (data >> 8) & 0xff;
+ b[3] = data & 0xff;
+ /* expand write as needed */
+ if (data < 0x100)
+ s = 3;
+ else if (data < 0x1000)
+ s = 2;
+ else if (data < 0x100000)
+ s = 1;
+ else
+ s = 0;
+
+ ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s);
+
+ return ret;
+}
+
+static int it913x_fe_script_loader(struct it913x_fe_state *state,
+ struct it913xset *loadscript)
+{
+ int ret, i;
+ if (loadscript == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 1000; ++i) {
+ if (loadscript[i].pro == 0xff)
+ break;
+ ret = it913x_write(state, loadscript[i].pro,
+ loadscript[i].address,
+ loadscript[i].reg, loadscript[i].count);
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int it913x_init_tuner(struct it913x_fe_state *state)
+{
+ int ret, i, reg;
+ u8 val, nv_val;
+ u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
+ u8 b[2];
+
+ reg = it913x_read_reg_u8(state, 0xec86);
+ switch (reg) {
+ case 0:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 2000;
+ state->tun_fdiv = 3;
+ val = 16;
+ break;
+ case -ENODEV:
+ return -ENODEV;
+ case 1:
+ default:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 640;
+ state->tun_fdiv = 1;
+ val = 6;
+ break;
+ }
+
+ reg = it913x_read_reg_u8(state, 0xed03);
+
+ if (reg < 0)
+ return -ENODEV;
+ else if (reg < sizeof(nv))
+ nv_val = nv[reg];
+ else
+ nv_val = 2;
+
+ for (i = 0; i < 50; i++) {
+ ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b));
+ reg = (b[1] << 8) + b[0];
+ if (reg > 0)
+ break;
+ if (ret < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+ state->tun_fn_min = state->tun_xtal * reg;
+ state->tun_fn_min /= (state->tun_fdiv * nv_val);
+ deb_info("Tuner fn_min %d", state->tun_fn_min);
+
+ for (i = 0; i < 50; i++) {
+ reg = it913x_read_reg_u8(state, 0xec82);
+ if (reg > 0)
+ break;
+ if (reg < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+
+ return it913x_write_reg(state, PRO_DMOD, 0xed81, val);
+}
+
+static int it9137_set_tuner(struct it913x_fe_state *state,
+ enum fe_bandwidth bandwidth, u32 frequency_m)
+{
+ struct it913xset *set_tuner = set_it9137_template;
+ int ret, reg;
+ u32 frequency = frequency_m / 1000;
+ u32 freq, temp_f, tmp;
+ u16 iqik_m_cal;
+ u16 n_div;
+ u8 n;
+ u8 l_band;
+ u8 lna_band;
+ u8 bw;
+
+ deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth);
+
+ if (frequency >= 51000 && frequency <= 440000) {
+ l_band = 0;
+ lna_band = 0;
+ } else if (frequency > 440000 && frequency <= 484000) {
+ l_band = 1;
+ lna_band = 1;
+ } else if (frequency > 484000 && frequency <= 533000) {
+ l_band = 1;
+ lna_band = 2;
+ } else if (frequency > 533000 && frequency <= 587000) {
+ l_band = 1;
+ lna_band = 3;
+ } else if (frequency > 587000 && frequency <= 645000) {
+ l_band = 1;
+ lna_band = 4;
+ } else if (frequency > 645000 && frequency <= 710000) {
+ l_band = 1;
+ lna_band = 5;
+ } else if (frequency > 710000 && frequency <= 782000) {
+ l_band = 1;
+ lna_band = 6;
+ } else if (frequency > 782000 && frequency <= 860000) {
+ l_band = 1;
+ lna_band = 7;
+ } else if (frequency > 1450000 && frequency <= 1492000) {
+ l_band = 1;
+ lna_band = 0;
+ } else if (frequency > 1660000 && frequency <= 1685000) {
+ l_band = 1;
+ lna_band = 1;
+ } else
+ return -EINVAL;
+ set_tuner[0].reg[0] = lna_band;
+
+ if (bandwidth == BANDWIDTH_5_MHZ)
+ bw = 0;
+ else if (bandwidth == BANDWIDTH_6_MHZ)
+ bw = 2;
+ else if (bandwidth == BANDWIDTH_7_MHZ)
+ bw = 4;
+ else if (bandwidth == BANDWIDTH_8_MHZ)
+ bw = 6;
+ else
+ bw = 6;
+
+ set_tuner[1].reg[0] = bw;
+ set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
+
+ if (frequency > 53000 && frequency <= 74000) {
+ n_div = 48;
+ n = 0;
+ } else if (frequency > 74000 && frequency <= 111000) {
+ n_div = 32;
+ n = 1;
+ } else if (frequency > 111000 && frequency <= 148000) {
+ n_div = 24;
+ n = 2;
+ } else if (frequency > 148000 && frequency <= 222000) {
+ n_div = 16;
+ n = 3;
+ } else if (frequency > 222000 && frequency <= 296000) {
+ n_div = 12;
+ n = 4;
+ } else if (frequency > 296000 && frequency <= 445000) {
+ n_div = 8;
+ n = 5;
+ } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
+ n_div = 6;
+ n = 6;
+ } else if (frequency > state->tun_fn_min && frequency <= 950000) {
+ n_div = 4;
+ n = 7;
+ } else if (frequency > 1450000 && frequency <= 1680000) {
+ n_div = 2;
+ n = 0;
+ } else
+ return -EINVAL;
+
+ reg = it913x_read_reg_u8(state, 0xed81);
+ iqik_m_cal = (u16)reg * n_div;
+
+ if (reg < 0x20) {
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = (iqik_m_cal * 9) >> 5;
+ else
+ iqik_m_cal >>= 1;
+ } else {
+ iqik_m_cal = 0x40 - iqik_m_cal;
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
+ else
+ iqik_m_cal = ~(iqik_m_cal >> 1);
+ }
+
+ temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
+ freq = temp_f / state->tun_xtal;
+ tmp = freq * state->tun_xtal;
+
+ if ((temp_f - tmp) >= (state->tun_xtal >> 1))
+ freq++;
+
+ freq += (u32) n << 13;
+ /* Frequency OMEGA_IQIK_M_CAL_MID*/
+ temp_f = freq + (u32)iqik_m_cal;
+
+ set_tuner[3].reg[0] = temp_f & 0xff;
+ set_tuner[4].reg[0] = (temp_f >> 8) & 0xff;
+
+ deb_info("High Frequency = %04x", temp_f);
+
+ /* Lower frequency */
+ set_tuner[5].reg[0] = freq & 0xff;
+ set_tuner[6].reg[0] = (freq >> 8) & 0xff;
+
+ deb_info("low Frequency = %04x", freq);
+
+ ret = it913x_fe_script_loader(state, set_tuner);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_select_bw(struct it913x_fe_state *state,
+ enum fe_bandwidth bandwidth, u32 adcFrequency)
+{
+ int ret, i;
+ u8 buffer[256];
+ u32 coeff[8];
+ u16 bfsfcw_fftinx_ratio;
+ u16 fftinx_bfsfcw_ratio;
+ u8 count;
+ u8 bw;
+ u8 adcmultiplier;
+
+ deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency);
+
+ if (bandwidth == BANDWIDTH_5_MHZ)
+ bw = 3;
+ else if (bandwidth == BANDWIDTH_6_MHZ)
+ bw = 0;
+ else if (bandwidth == BANDWIDTH_7_MHZ)
+ bw = 1;
+ else if (bandwidth == BANDWIDTH_8_MHZ)
+ bw = 2;
+ else
+ bw = 2;
+
+ ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw);
+
+ if (state->table == NULL)
+ return -EINVAL;
+
+ /* In write order */
+ coeff[0] = state->table[bw].coeff_1_2048;
+ coeff[1] = state->table[bw].coeff_2_2k;
+ coeff[2] = state->table[bw].coeff_1_8191;
+ coeff[3] = state->table[bw].coeff_1_8192;
+ coeff[4] = state->table[bw].coeff_1_8193;
+ coeff[5] = state->table[bw].coeff_2_8k;
+ coeff[6] = state->table[bw].coeff_1_4096;
+ coeff[7] = state->table[bw].coeff_2_4k;
+ bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio;
+ fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio;
+
+ /* ADC multiplier */
+ ret = it913x_read_reg_u8(state, ADC_X_2);
+ if (ret < 0)
+ return -EINVAL;
+
+ adcmultiplier = ret;
+
+ count = 0;
+
+ /* Build Buffer for COEFF Registers */
+ for (i = 0; i < 8; i++) {
+ if (adcmultiplier == 1)
+ coeff[i] /= 2;
+ buffer[count++] = (coeff[i] >> 24) & 0x3;
+ buffer[count++] = (coeff[i] >> 16) & 0xff;
+ buffer[count++] = (coeff[i] >> 8) & 0xff;
+ buffer[count++] = coeff[i] & 0xff;
+ }
+
+ /* bfsfcw_fftinx_ratio register 0x21-0x22 */
+ buffer[count++] = bfsfcw_fftinx_ratio & 0xff;
+ buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff;
+ /* fftinx_bfsfcw_ratio register 0x23-0x24 */
+ buffer[count++] = fftinx_bfsfcw_ratio & 0xff;
+ buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff;
+ /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/
+ ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count);
+
+ for (i = 0; i < 42; i += 8)
+ debug_data_snipet(0x1, "Buffer", &buffer[i]);
+
+ return ret;
+}
+
+
+
+static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret, i;
+ fe_status_t old_status = state->it913x_status;
+ *status = 0;
+
+ if (state->it913x_status == 0) {
+ ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (ret == 0x1) {
+ *status |= FE_HAS_SIGNAL;
+ for (i = 0; i < 40; i++) {
+ ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK);
+ if (ret == 0x1)
+ break;
+ msleep(25);
+ }
+ if (ret == 0x1)
+ *status |= FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC;
+ state->it913x_status = *status;
+ }
+ }
+
+ if (state->it913x_status & FE_HAS_SYNC) {
+ ret = it913x_read_reg_u8(state, TPSD_LOCK);
+ if (ret == 0x1)
+ *status |= FE_HAS_LOCK
+ | state->it913x_status;
+ else
+ state->it913x_status = 0;
+ if (old_status != state->it913x_status)
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret);
+ }
+
+ return 0;
+}
+
+static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
+ /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/
+ if (state->it913x_status & FE_HAS_SIGNAL)
+ ret = (ret * 0xff) / 0x64;
+ else
+ ret = 0x0;
+ ret |= ret << 0x8;
+ *strength = ret;
+ return 0;
+}
+
+static int it913x_fe_read_snr(struct dvb_frontend *fe, u16* snr)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = it913x_read_reg_u8(state, SIGNAL_QUALITY);
+ ret = (ret * 0xff) / 0x64;
+ ret |= (ret << 0x8);
+ *snr = ~ret;
+ return 0;
+}
+
+static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int it913x_fe_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret;
+ u8 reg[8];
+
+ ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
+
+ if (reg[3] < 3)
+ p->u.ofdm.constellation = fe_con[reg[3]];
+
+ if (reg[0] < 3)
+ p->u.ofdm.transmission_mode = fe_mode[reg[0]];
+
+ if (reg[1] < 4)
+ p->u.ofdm.guard_interval = fe_gi[reg[1]];
+
+ if (reg[2] < 4)
+ p->u.ofdm.hierarchy_information = fe_hi[reg[2]];
+
+ p->u.ofdm.code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
+ p->u.ofdm.code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
+
+ return 0;
+}
+
+static int it913x_fe_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret, i;
+ u8 empty_ch, last_ch;
+
+ state->it913x_status = 0;
+
+ /* Set bw*/
+ ret = it913x_fe_select_bw(state, p->u.ofdm.bandwidth,
+ state->adcFrequency);
+
+ /* Training Mode Off */
+ ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
+
+ /* Clear Empty Channel */
+ ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
+
+ /* Clear bits */
+ ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
+ /* LED on */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+ /* Select Band*/
+ if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
+ i = 0;
+ else if ((p->frequency >= 350000000) && (p->frequency <= 900000000))
+ i = 1;
+ else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000))
+ i = 2;
+ else
+ return -EOPNOTSUPP;
+
+ ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
+
+ deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
+ switch (state->tuner_type) {
+ case IT9137: /* Tuner type 0x38 */
+ ret = it9137_set_tuner(state,
+ p->u.ofdm.bandwidth, p->frequency);
+ break;
+ default:
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ break;
+ }
+ /* LED off */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+ /* Trigger ofsm */
+ ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+ last_ch = 2;
+ for (i = 0; i < 40; ++i) {
+ empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (last_ch == 1 && empty_ch == 1)
+ break;
+ if (last_ch == 2 && empty_ch == 2)
+ return 0;
+ last_ch = empty_ch;
+ msleep(25);
+ }
+ for (i = 0; i < 40; ++i) {
+ if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1)
+ break;
+ msleep(25);
+ }
+
+ state->frequency = p->frequency;
+ return 0;
+}
+
+static int it913x_fe_suspend(struct it913x_fe_state *state)
+{
+ int ret, i;
+ u8 b;
+
+ ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+
+ for (i = 0; i < 128; i++) {
+ ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1);
+ if (ret < 0)
+ return -ENODEV;
+ if (b == 0)
+ break;
+
+ }
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8);
+ /* Turn LED off */
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+
+ ret |= it913x_fe_script_loader(state, it9137_tuner_off);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+/* Power sequence */
+/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */
+/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */
+
+static int it913x_fe_sleep(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ return it913x_fe_suspend(state);
+}
+
+static u32 compute_div(u32 a, u32 b, u32 x)
+{
+ u32 res = 0;
+ u32 c = 0;
+ u32 i = 0;
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ res += 1;
+ a -= b;
+ }
+ a <<= 1;
+ res <<= 1;
+ }
+
+ res = (c << x) + res;
+
+ return res;
+}
+
+static int it913x_fe_start(struct it913x_fe_state *state)
+{
+ struct it913xset *set_fe;
+ struct it913xset *set_mode;
+ int ret;
+ u8 adf = (state->adf & 0xf);
+ u32 adc, xtal;
+ u8 b[4];
+
+ ret = it913x_init_tuner(state);
+
+ if (adf < 12) {
+ state->crystalFrequency = fe_clockTable[adf].xtal ;
+ state->table = fe_clockTable[adf].table;
+ state->adcFrequency = state->table->adcFrequency;
+
+ adc = compute_div(state->adcFrequency, 1000000ul, 19ul);
+ xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul);
+
+ } else
+ return -EINVAL;
+
+ deb_info("Xtal Freq :%d Adc Freq :%d Adc %08x Xtal %08x",
+ state->crystalFrequency, state->adcFrequency, adc, xtal);
+
+ /* Set LED indicator on GPIOH3 */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01);
+
+ b[0] = xtal & 0xff;
+ b[1] = (xtal >> 8) & 0xff;
+ b[2] = (xtal >> 16) & 0xff;
+ b[3] = (xtal >> 24);
+ ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4);
+
+ b[0] = adc & 0xff;
+ b[1] = (adc >> 8) & 0xff;
+ b[2] = (adc >> 16) & 0xff;
+ ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3);
+
+ switch (state->tuner_type) {
+ case IT9137: /* Tuner type 0x38 */
+ set_fe = it9137_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set the demod */
+ ret = it913x_fe_script_loader(state, set_fe);
+ /* Always solo frontend */
+ set_mode = set_solo_fe;
+ ret |= it913x_fe_script_loader(state, set_mode);
+
+ ret |= it913x_fe_suspend(state);
+ return 0;
+}
+
+static int it913x_fe_init(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = 0;
+ /* Power Up Tuner - common all versions */
+ ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0);
+
+ ret |= it913x_fe_script_loader(state, init_1);
+
+ switch (state->tuner_type) {
+ case IT9137:
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static void it913x_fe_release(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops;
+
+struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr, u8 adf, u8 type)
+{
+ struct it913x_fe_state *state = NULL;
+ int ret;
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->i2c_adap = i2c_adap;
+ state->i2c_addr = i2c_addr;
+ state->adf = adf;
+ state->tuner_type = type;
+
+ ret = it913x_fe_start(state);
+ if (ret < 0)
+ goto error;
+
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(it913x_fe_attach);
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
+
+ .info = {
+ .name = "it913x-fe DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 51000000,
+ .frequency_max = 1680000000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = it913x_fe_release,
+
+ .init = it913x_fe_init,
+ .sleep = it913x_fe_sleep,
+
+ .set_frontend = it913x_fe_set_frontend,
+ .get_frontend = it913x_fe_get_frontend,
+
+ .read_status = it913x_fe_read_status,
+ .read_signal_strength = it913x_fe_read_signal_strength,
+ .read_snr = it913x_fe_read_snr,
+ .read_ber = it913x_fe_read_ber,
+ .read_ucblocks = it913x_fe_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_VERSION("1.07");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h
new file mode 100644
index 000000000000..9d97f32e690b
--- /dev/null
+++ b/drivers/media/dvb/frontends/it913x-fe.h
@@ -0,0 +1,196 @@
+/*
+ * Driver for it913x Frontend
+ *
+ *
+ * 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 IT913X_FE_H
+#define IT913X_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \
+defined(MODULE))
+extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr, u8 adf, u8 type);
+#else
+static inline struct dvb_frontend *it913x_fe_attach(
+ struct i2c_adapter *i2c_adap, u8 i2c_addr, u8 adf, u8 type)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_IT913X_FE */
+#define I2C_BASE_ADDR 0x10
+#define DEV_0 0x0
+#define DEV_1 0x10
+#define PRO_LINK 0x0
+#define PRO_DMOD 0x1
+#define DEV_0_DMOD (PRO_DMOD << 0x7)
+#define DEV_1_DMOD (DEV_0_DMOD | DEV_1)
+#define CHIP2_I2C_ADDR 0x3a
+
+#define AFE_MEM0 0xfb24
+
+#define MP2_SW_RST 0xf99d
+#define MP2IF2_SW_RST 0xf9a4
+
+#define PADODPU 0xd827
+#define THIRDODPU 0xd828
+#define AGC_O_D 0xd829
+
+#define EP0_TX_EN 0xdd11
+#define EP0_TX_NAK 0xdd13
+#define EP4_TX_LEN_LSB 0xdd88
+#define EP4_TX_LEN_MSB 0xdd89
+#define EP4_MAX_PKT 0xdd0c
+#define EP5_TX_LEN_LSB 0xdd8a
+#define EP5_TX_LEN_MSB 0xdd8b
+#define EP5_MAX_PKT 0xdd0d
+
+#define IO_MUX_POWER_CLK 0xd800
+#define CLK_O_EN 0xd81a
+#define I2C_CLK 0xf103
+#define I2C_CLK_100 0x7
+#define I2C_CLK_400 0x1a
+
+#define D_TPSD_LOCK 0xf5a9
+#define MP2IF2_EN 0xf9a3
+#define MP2IF_SERIAL 0xf985
+#define TSIS_ENABLE 0xf9cd
+#define MP2IF2_HALF_PSB 0xf9a5
+#define MP2IF_STOP_EN 0xf9b5
+#define MPEG_FULL_SPEED 0xf990
+#define TOP_HOSTB_SER_MODE 0xd91c
+
+#define PID_RST 0xf992
+#define PID_EN 0xf993
+#define PID_INX_EN 0xf994
+#define PID_INX 0xf995
+#define PID_LSB 0xf996
+#define PID_MSB 0xf997
+
+#define MP2IF_MPEG_PAR_MODE 0xf986
+#define DCA_UPPER_CHIP 0xf731
+#define DCA_LOWER_CHIP 0xf732
+#define DCA_PLATCH 0xf730
+#define DCA_FPGA_LATCH 0xf778
+#define DCA_STAND_ALONE 0xf73c
+#define DCA_ENABLE 0xf776
+
+#define DVBT_INTEN 0xf41f
+#define DVBT_ENABLE 0xf41a
+#define HOSTB_DCA_LOWER 0xd91f
+#define HOSTB_MPEG_PAR_MODE 0xd91b
+#define HOSTB_MPEG_SER_MODE 0xd91c
+#define HOSTB_MPEG_SER_DO7 0xd91d
+#define HOSTB_DCA_UPPER 0xd91e
+#define PADMISCDR2 0xd830
+#define PADMISCDR4 0xd831
+#define PADMISCDR8 0xd832
+#define PADMISCDRSR 0xd833
+#define LOCK3_OUT 0xd8fd
+
+#define GPIOH1_O 0xd8af
+#define GPIOH1_EN 0xd8b0
+#define GPIOH1_ON 0xd8b1
+#define GPIOH3_O 0xd8b3
+#define GPIOH3_EN 0xd8b4
+#define GPIOH3_ON 0xd8b5
+#define GPIOH5_O 0xd8bb
+#define GPIOH5_EN 0xd8bc
+#define GPIOH5_ON 0xd8bd
+
+#define AFE_MEM0 0xfb24
+
+#define REG_TPSD_TX_MODE 0xf900
+#define REG_TPSD_GI 0xf901
+#define REG_TPSD_HIER 0xf902
+#define REG_TPSD_CONST 0xf903
+#define REG_BW 0xf904
+#define REG_PRIV 0xf905
+#define REG_TPSD_HP_CODE 0xf906
+#define REG_TPSD_LP_CODE 0xf907
+
+#define MP2IF_SYNC_LK 0xf999
+#define ADC_FREQ 0xf1cd
+
+#define TRIGGER_OFSM 0x0000
+/* COEFF Registers start at 0x0001 to 0x0020 */
+#define COEFF_1_2048 0x0001
+#define XTAL_CLK 0x0025
+#define BFS_FCW 0x0029
+#define TPSD_LOCK 0x003c
+#define TRAINING_MODE 0x0040
+#define ADC_X_2 0x0045
+#define TUNER_ID 0x0046
+#define EMPTY_CHANNEL_STATUS 0x0047
+#define SIGNAL_LEVEL 0x0048
+#define SIGNAL_QUALITY 0x0049
+#define EST_SIGNAL_LEVEL 0x004a
+#define FREE_BAND 0x004b
+#define SUSPEND_FLAG 0x004c
+/* Build in tuners */
+#define IT9137 0x38
+
+enum {
+ CMD_DEMOD_READ = 0,
+ CMD_DEMOD_WRITE,
+ CMD_TUNER_READ,
+ CMD_TUNER_WRITE,
+ CMD_REG_EEPROM_READ,
+ CMD_REG_EEPROM_WRITE,
+ CMD_DATA_READ,
+ CMD_VAR_READ = 8,
+ CMD_VAR_WRITE,
+ CMD_PLATFORM_GET,
+ CMD_PLATFORM_SET,
+ CMD_IP_CACHE,
+ CMD_IP_ADD,
+ CMD_IP_REMOVE,
+ CMD_PID_ADD,
+ CMD_PID_REMOVE,
+ CMD_SIPSI_GET,
+ CMD_SIPSI_MPE_RESET,
+ CMD_H_PID_ADD = 0x15,
+ CMD_H_PID_REMOVE,
+ CMD_ABORT,
+ CMD_IR_GET,
+ CMD_IR_SET,
+ CMD_FW_DOWNLOAD = 0x21,
+ CMD_QUERYINFO,
+ CMD_BOOT,
+ CMD_FW_DOWNLOAD_BEGIN,
+ CMD_FW_DOWNLOAD_END,
+ CMD_RUN_CODE,
+ CMD_SCATTER_READ = 0x28,
+ CMD_SCATTER_WRITE,
+ CMD_GENERIC_READ,
+ CMD_GENERIC_WRITE
+};
+
+enum {
+ READ_LONG,
+ WRITE_LONG,
+ READ_SHORT,
+ WRITE_SHORT,
+ READ_DATA,
+ WRITE_DATA,
+ WRITE_CMD,
+};
+
+#endif /* IT913X_FE_H */
diff --git a/drivers/media/dvb/frontends/lnbp22.c b/drivers/media/dvb/frontends/lnbp22.c
new file mode 100644
index 000000000000..84ad0390a4a1
--- /dev/null
+++ b/drivers/media/dvb/frontends/lnbp22.c
@@ -0,0 +1,148 @@
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21 driver
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org
+ */
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "lnbp22.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+
+#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg)
+
+struct lnbp22 {
+ u8 config[4];
+ struct i2c_adapter *i2c;
+};
+
+static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage,
+ SEC_VOLTAGE_18, SEC_VOLTAGE_13);
+
+ lnbp22->config[3] = 0x60; /* Power down */
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ lnbp22->config[3] |= LNBP22_EN;
+ break;
+ case SEC_VOLTAGE_18:
+ lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]);
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d\n", __func__, (int)arg);
+ if (arg)
+ lnbp22->config[3] |= LNBP22_LLC;
+ else
+ lnbp22->config[3] &= ~LNBP22_LLC;
+
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static void lnbp22_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s\n", __func__);
+ /* LNBP power off */
+ lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ /* free data */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL);
+ if (!lnbp22)
+ return NULL;
+
+ /* default configuration */
+ lnbp22->config[0] = 0x00; /* ? */
+ lnbp22->config[1] = 0x28; /* ? */
+ lnbp22->config[2] = 0x48; /* ? */
+ lnbp22->config[3] = 0x60; /* Power down */
+ lnbp22->i2c = i2c;
+ fe->sec_priv = lnbp22;
+
+ /* detect if it is present or not */
+ if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ dprintk(0, "%s LNBP22 not found\n", __func__);
+ kfree(lnbp22);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ /* install release callback */
+ fe->ops.release_sec = lnbp22_release;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = lnbp22_set_voltage;
+ fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage;
+
+ return fe;
+}
+EXPORT_SYMBOL(lnbp22_attach);
+
+MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22");
+MODULE_AUTHOR("Dominik Kuhlen");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lnbp22.h b/drivers/media/dvb/frontends/lnbp22.h
new file mode 100644
index 000000000000..63e2dec7e68a
--- /dev/null
+++ b/drivers/media/dvb/frontends/lnbp22.h
@@ -0,0 +1,57 @@
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21.h
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org
+ */
+
+#ifndef _LNBP22_H
+#define _LNBP22_H
+
+/* Enable */
+#define LNBP22_EN 0x10
+/* Voltage selection */
+#define LNBP22_VSEL 0x02
+/* Plus 1 Volt Bit */
+#define LNBP22_LLC 0x01
+
+#include <linux/dvb/frontend.h>
+
+#if defined(CONFIG_DVB_LNBP22) || \
+ (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE))
+/*
+ * override_set and override_clear control which system register bits (above)
+ * to always set & clear
+ */
+extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LNBP22 */
+
+#endif /* _LNBP22_H */
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c
index d70eee00f33a..117a56926dca 100644
--- a/drivers/media/dvb/frontends/stb0899_algo.c
+++ b/drivers/media/dvb/frontends/stb0899_algo.c
@@ -358,6 +358,9 @@ static enum stb0899_status stb0899_check_data(struct stb0899_state *state)
else
dataTime = 500;
+ /* clear previous failed END_LOOPVIT */
+ stb0899_read_reg(state, STB0899_VSTATUS);
+
stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */
while (1) {
/* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index 37a222d9ddb3..8408ef877b4b 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -706,7 +706,7 @@ static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_ma
stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
for (i = 0; i < cmd->msg_len; i++) {
/* wait for FIFO empty */
- if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0)
+ if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0)
return -ETIMEDOUT;
stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
@@ -1426,9 +1426,9 @@ static void stb0899_set_iterations(struct stb0899_state *state)
if (iter_scale > config->ldpc_max_iter)
iter_scale = config->ldpc_max_iter;
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER);
+ reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER);
STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
+ stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
}
static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index 8e0cfadba688..0aa3962ff18b 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -127,6 +127,11 @@ static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
if ((srate < 1000000) || (srate > 45000000))
return -EINVAL;
+ stv0288_writeregI(state, 0x22, 0);
+ stv0288_writeregI(state, 0x23, 0);
+ stv0288_writeregI(state, 0x2b, 0xff);
+ stv0288_writeregI(state, 0x2c, 0xf7);
+
temp = (unsigned int)srate / 1000;
temp = temp * 32768;
@@ -461,6 +466,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe,
char tm;
unsigned char tda[3];
+ u8 reg, time_out = 0;
dprintk("%s : FE_SET_FRONTEND\n", __func__);
@@ -488,22 +494,29 @@ static int stv0288_set_frontend(struct dvb_frontend *fe,
/* Carrier lock control register */
stv0288_writeregI(state, 0x15, 0xc5);
- tda[0] = 0x2b; /* CFRM */
tda[2] = 0x0; /* CFRL */
- for (tm = -6; tm < 7;) {
+ for (tm = -9; tm < 7;) {
/* Viterbi status */
- if (stv0288_readreg(state, 0x24) & 0x8)
- break;
-
- tda[2] += 40;
- if (tda[2] < 40)
+ reg = stv0288_readreg(state, 0x24);
+ if (reg & 0x8)
+ break;
+ if (reg & 0x80) {
+ time_out++;
+ if (time_out > 10)
+ break;
+ tda[2] += 40;
+ if (tda[2] < 40)
+ tm++;
+ } else {
tm++;
+ tda[2] = 0;
+ time_out = 0;
+ }
tda[1] = (unsigned char)tm;
stv0288_writeregI(state, 0x2b, tda[1]);
stv0288_writeregI(state, 0x2c, tda[2]);
udelay(30);
}
-
state->tuner_frequency = c->frequency;
state->fec_inner = FEC_AUTO;
state->symbol_rate = c->symbol_rate;
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 52d8712411e5..ebda41936b90 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -3463,9 +3463,15 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct stv090x_state *state = fe->demodulator_priv;
- u32 reg;
+ u32 reg, dstatus;
u8 search_state;
+ *status = 0;
+
+ dstatus = STV090x_READ_DEMOD(state, DSTATUS);
+ if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD))
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
reg = STV090x_READ_DEMOD(state, DMDSTATE);
search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
@@ -3474,41 +3480,30 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
case 1: /* first PLH detected */
default:
dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)");
- *status = 0;
break;
case 2: /* DVB-S2 mode */
dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
+ *status |= FE_HAS_VITERBI;
reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
- *status = FE_HAS_SIGNAL |
- FE_HAS_CARRIER |
- FE_HAS_VITERBI |
- FE_HAS_SYNC |
- FE_HAS_LOCK;
- }
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
}
}
break;
case 3: /* DVB-S1/legacy mode */
dprintk(FE_DEBUG, 1, "Delivery system: DVB-S");
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) {
+ *status |= FE_HAS_VITERBI;
reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
- *status = FE_HAS_SIGNAL |
- FE_HAS_CARRIER |
- FE_HAS_VITERBI |
- FE_HAS_SYNC |
- FE_HAS_LOCK;
- }
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
}
}
break;
diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c
index 93f6a75c238e..7f105946a434 100644
--- a/drivers/media/dvb/frontends/tda10048.c
+++ b/drivers/media/dvb/frontends/tda10048.c
@@ -206,15 +206,16 @@ static struct init_tab {
static struct pll_tab {
u32 clk_freq_khz;
u32 if_freq_khz;
- u8 m, n, p;
} pll_tab[] = {
- { TDA10048_CLK_4000, TDA10048_IF_36130, 10, 0, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3300, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3500, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_3800, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_4000, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_4300, 10, 3, 0 },
- { TDA10048_CLK_16000, TDA10048_IF_36130, 10, 3, 0 },
+ { TDA10048_CLK_4000, TDA10048_IF_36130 },
+ { TDA10048_CLK_16000, TDA10048_IF_3300 },
+ { TDA10048_CLK_16000, TDA10048_IF_3500 },
+ { TDA10048_CLK_16000, TDA10048_IF_3800 },
+ { TDA10048_CLK_16000, TDA10048_IF_4000 },
+ { TDA10048_CLK_16000, TDA10048_IF_4300 },
+ { TDA10048_CLK_16000, TDA10048_IF_4500 },
+ { TDA10048_CLK_16000, TDA10048_IF_5000 },
+ { TDA10048_CLK_16000, TDA10048_IF_36130 },
};
static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data)
@@ -460,9 +461,6 @@ static int tda10048_set_if(struct dvb_frontend *fe, enum fe_bandwidth bw)
state->freq_if_hz = pll_tab[i].if_freq_khz * 1000;
state->xtal_hz = pll_tab[i].clk_freq_khz * 1000;
- state->pll_mfactor = pll_tab[i].m;
- state->pll_nfactor = pll_tab[i].n;
- state->pll_pfactor = pll_tab[i].p;
break;
}
}
@@ -781,6 +779,10 @@ static int tda10048_init(struct dvb_frontend *fe)
dprintk(1, "%s()\n", __func__);
+ /* PLL */
+ init_tab[4].data = (u8)(state->pll_mfactor);
+ init_tab[5].data = (u8)(state->pll_nfactor) | 0x40;
+
/* Apply register defaults */
for (i = 0; i < ARRAY_SIZE(init_tab); i++)
tda10048_writereg(state, init_tab[i].reg, init_tab[i].data);
@@ -1123,7 +1125,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
/* setup the state and clone the config */
memcpy(&state->config, config, sizeof(*config));
state->i2c = i2c;
- state->fwloaded = 0;
+ state->fwloaded = config->no_firmware;
state->bandwidth = BANDWIDTH_8_MHZ;
/* check if the demod is present */
@@ -1135,6 +1137,17 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+ /* set pll */
+ if (config->set_pll) {
+ state->pll_mfactor = config->pll_m;
+ state->pll_nfactor = config->pll_n;
+ state->pll_pfactor = config->pll_p;
+ } else {
+ state->pll_mfactor = 10;
+ state->pll_nfactor = 3;
+ state->pll_pfactor = 0;
+ }
+
/* Establish any defaults the the user didn't pass */
tda10048_establish_defaults(&state->frontend);
diff --git a/drivers/media/dvb/frontends/tda10048.h b/drivers/media/dvb/frontends/tda10048.h
index 8828ceaf74bb..fb2ef5ac9487 100644
--- a/drivers/media/dvb/frontends/tda10048.h
+++ b/drivers/media/dvb/frontends/tda10048.h
@@ -51,6 +51,7 @@ struct tda10048_config {
#define TDA10048_IF_4300 4300
#define TDA10048_IF_4500 4500
#define TDA10048_IF_4750 4750
+#define TDA10048_IF_5000 5000
#define TDA10048_IF_36130 36130
u16 dtv6_if_freq_khz;
u16 dtv7_if_freq_khz;
@@ -62,6 +63,13 @@ struct tda10048_config {
/* Disable I2C gate access */
u8 disable_gate_access;
+
+ bool no_firmware;
+
+ bool set_pll;
+ u8 pll_m;
+ u8 pll_p;
+ u8 pll_n;
};
#if defined(CONFIG_DVB_TDA10048) || \
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
new file mode 100644
index 000000000000..0c37434d19e2
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071.c
@@ -0,0 +1,1269 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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 "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 */
+static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len+1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .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 registers */
+static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write single register */
+static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val)
+{
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val)
+{
+ return tda10071_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+/* execute firmware command */
+static int tda10071_cmd_execute(struct tda10071_priv *priv,
+ struct tda10071_cmd *cmd)
+{
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* write cmd and args for firmware */
+ ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len);
+ if (ret)
+ goto error;
+
+ /* start cmd execution */
+ ret = tda10071_wr_reg(priv, 0x1f, 1);
+ if (ret)
+ goto error;
+
+ /* wait cmd execution terminate */
+ for (i = 1000, tmp = 1; i && tmp; i--) {
+ ret = tda10071_rd_reg(priv, 0x1f, &tmp);
+ if (ret)
+ goto error;
+
+ usleep_range(200, 5000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t fe_sec_tone_mode)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tone;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode);
+
+ switch (fe_sec_tone_mode) {
+ case SEC_TONE_ON:
+ tone = 1;
+ break;
+ case SEC_TONE_OFF:
+ tone = 0;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_tone_mode", __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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t fe_sec_voltage)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 voltage;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: voltage=%d", __func__, fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_13:
+ voltage = 0;
+ break;
+ case SEC_VOLTAGE_18:
+ voltage = 1;
+ break;
+ case SEC_VOLTAGE_OFF:
+ voltage = 0;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_voltage", __func__);
+ ret = -EINVAL;
+ goto error;
+ };
+
+ cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = voltage;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len);
+
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ 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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
+ struct dvb_diseqc_slave_reply *reply)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s:", __func__);
+
+ /* wait LNB RX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* reply len */
+ ret = tda10071_rd_reg(priv, 0x46, &tmp);
+ if (ret)
+ goto error;
+
+ reply->msg_len = tmp & 0x1f; /* [4:0] */;
+ if (reply->msg_len > sizeof(reply->msg))
+ 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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t fe_sec_mini_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp, burst;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd);
+
+ switch (fe_sec_mini_cmd) {
+ case SEC_MINI_A:
+ burst = 0;
+ break;
+ case SEC_MINI_B:
+ burst = 1;
+ break;
+ default:
+ dbg("%s: invalid fe_sec_mini_cmd", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dbg("%s: loop=%d", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0x00] = CMD_LNB_SEND_TONEBURST;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = burst;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ *status = 0;
+
+ if (!priv->warm) {
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_reg(priv, 0x39, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x01) /* tuner PLL */
+ *status |= FE_HAS_SIGNAL;
+ if (tmp & 0x02) /* demod PLL */
+ *status |= FE_HAS_CARRIER;
+ if (tmp & 0x04) /* viterbi or LDPC*/
+ *status |= FE_HAS_VITERBI;
+ if (tmp & 0x08) /* RS or BCH */
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ priv->fe_status = *status;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *snr = 0;
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x3a, buf, 2);
+ if (ret)
+ goto error;
+
+ /* Es/No dBx10 */
+ *snr = buf[0] << 8 | buf[1];
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *strength = 0;
+ ret = 0;
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_GET_AGCACC;
+ cmd.args[0x01] = 0;
+ cmd.len = 0x02;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ /* input power estimate dBm */
+ ret = tda10071_rd_reg(priv, 0x50, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp < 181)
+ tmp = 181; /* -75 dBm */
+ else if (tmp > 236)
+ tmp = 236; /* -20 dBm */
+
+ /* scale value to 0x0000-0xffff */
+ *strength = (tmp-181) * 0xffff / (236-181);
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len;
+ u8 tmp, reg, buf[8];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ber = priv->ber = 0;
+ ret = 0;
+ goto error;
+ }
+
+ switch (priv->delivery_system) {
+ case SYS_DVBS:
+ reg = 0x4c;
+ len = 8;
+ i = 1;
+ break;
+ case SYS_DVBS2:
+ reg = 0x4d;
+ len = 4;
+ i = 0;
+ break;
+ default:
+ *ber = priv->ber = 0;
+ return 0;
+ }
+
+ ret = tda10071_rd_reg(priv, reg, &tmp);
+ if (ret)
+ goto error;
+
+ if (priv->meas_count[i] == tmp) {
+ dbg("%s: meas not ready=%02x", __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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, len);
+ if (ret)
+ goto error;
+
+ if (priv->delivery_system == SYS_DVBS) {
+ *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ priv->ucb += (buf[4] << 8) | buf[5];
+ } else {
+ *ber = (buf[0] << 8) | buf[1];
+ }
+ priv->ber = *ber;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret = 0;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ucblocks = 0;
+ goto error;
+ }
+
+ /* UCB is updated when BER is read. Assume BER is read anyway. */
+
+ *ucblocks = priv->ucb;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ 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);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ switch (c->inversion) {
+ case INVERSION_OFF:
+ inversion = 1;
+ break;
+ case INVERSION_ON:
+ inversion = 0;
+ break;
+ case INVERSION_AUTO:
+ /* 2 = auto; try first on then off
+ * 3 = auto; try first off then on */
+ inversion = 3;
+ break;
+ default:
+ dbg("%s: invalid inversion", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ rolloff = 0;
+ pilot = 2;
+ break;
+ case SYS_DVBS2:
+ switch (c->rolloff) {
+ case ROLLOFF_20:
+ rolloff = 2;
+ break;
+ case ROLLOFF_25:
+ rolloff = 1;
+ break;
+ case ROLLOFF_35:
+ rolloff = 0;
+ break;
+ case ROLLOFF_AUTO:
+ default:
+ dbg("%s: invalid rolloff", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->pilot) {
+ case PILOT_OFF:
+ pilot = 0;
+ break;
+ case PILOT_ON:
+ pilot = 1;
+ break;
+ case PILOT_AUTO:
+ pilot = 2;
+ break;
+ default:
+ dbg("%s: invalid pilot", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ default:
+ dbg("%s: invalid delivery_system", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (c->delivery_system == TDA10071_MODCOD[i].delivery_system &&
+ 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);
+ break;
+ }
+ }
+
+ if (mode == 0xff) {
+ dbg("%s: invalid parameter combination", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (c->symbol_rate <= 5000000)
+ div = 14;
+ else
+ div = 4;
+
+ ret = tda10071_wr_reg(priv, 0x81, div);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe3, div);
+ 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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ priv->delivery_system = c->delivery_system;
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 buf[5], tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x30, buf, 5);
+ if (ret)
+ goto error;
+
+ tmp = buf[0] & 0x3f;
+ for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (tmp == TDA10071_MODCOD[i].val) {
+ c->modulation = TDA10071_MODCOD[i].modulation;
+ c->fec_inner = TDA10071_MODCOD[i].fec;
+ c->delivery_system = TDA10071_MODCOD[i].delivery_system;
+ }
+ }
+
+ switch ((buf[1] >> 0) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ switch ((buf[1] >> 7) & 0x01) {
+ case 0:
+ c->pilot = PILOT_OFF;
+ break;
+ case 1:
+ c->pilot = PILOT_ON;
+ break;
+ }
+
+ c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0);
+
+ ret = tda10071_rd_regs(priv, 0x52, buf, 3);
+ if (ret)
+ goto error;
+
+ c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_init(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len, remaining, fw_size;
+ const struct firmware *fw;
+ u8 *fw_file = TDA10071_DEFAULT_FIRMWARE;
+ u8 tmp, buf[4];
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x00, 0x07 },
+ { 0x80, 0x00, 0x02 },
+ { 0xcd, 0x00, 0xc0 },
+ { 0xce, 0x00, 0x1b },
+ { 0x9d, 0x00, 0x01 },
+ { 0x9d, 0x00, 0x02 },
+ { 0x9e, 0x00, 0x01 },
+ { 0x87, 0x00, 0x80 },
+ { 0xce, 0x00, 0x08 },
+ { 0xce, 0x00, 0x10 },
+ };
+ struct tda10071_reg_val_mask tab2[] = {
+ { 0xf1, 0x70, 0xff },
+ { 0x88, priv->cfg.pll_multiplier, 0x3f },
+ { 0x89, 0x00, 0x10 },
+ { 0x89, 0x10, 0x10 },
+ { 0xc0, 0x01, 0x01 },
+ { 0xc0, 0x00, 0x01 },
+ { 0xe0, 0xff, 0xff },
+ { 0xe0, 0x00, 0xff },
+ { 0x96, 0x1e, 0x7e },
+ { 0x8b, 0x08, 0x08 },
+ { 0x8b, 0x00, 0x08 },
+ { 0x8f, 0x1a, 0x7e },
+ { 0x8c, 0x68, 0xff },
+ { 0x8d, 0x08, 0xff },
+ { 0x8e, 0x4c, 0xff },
+ { 0x8f, 0x01, 0x01 },
+ { 0x8b, 0x04, 0x04 },
+ { 0x8b, 0x00, 0x04 },
+ { 0x87, 0x05, 0x07 },
+ { 0x80, 0x00, 0x20 },
+ { 0xc8, 0x01, 0xff },
+ { 0xb4, 0x47, 0xff },
+ { 0xb5, 0x9c, 0xff },
+ { 0xb6, 0x7d, 0xff },
+ { 0xba, 0x00, 0x03 },
+ { 0xb7, 0x47, 0xff },
+ { 0xb8, 0x9c, 0xff },
+ { 0xb9, 0x7d, 0xff },
+ { 0xba, 0x00, 0x0c },
+ { 0xc8, 0x00, 0xff },
+ { 0xcd, 0x00, 0x04 },
+ { 0xcd, 0x00, 0x20 },
+ { 0xe8, 0x02, 0xff },
+ { 0xcf, 0x20, 0xff },
+ { 0x9b, 0xd7, 0xff },
+ { 0x9a, 0x01, 0x03 },
+ { 0xa8, 0x05, 0x0f },
+ { 0xa8, 0x65, 0xf0 },
+ { 0xa6, 0xa0, 0xf0 },
+ { 0x9d, 0x50, 0xfc },
+ { 0x9e, 0x20, 0xe0 },
+ { 0xa3, 0x1c, 0x7c },
+ { 0xd5, 0x03, 0x03 },
+ };
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (!tmp) {
+ /* warm state - wake up device from sleep */
+ priv->warm = 1;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_SET_SLEEP_MODE;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 0;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ } else {
+ /* cold state - try to download firmware */
+ priv->warm = 0;
+
+ /* 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);
+ goto error;
+ }
+
+ /* init */
+ for (i = 0; i < ARRAY_SIZE(tab2); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab2[i].reg,
+ tab2[i].val, tab2[i].mask);
+ if (ret)
+ goto error_release_firmware;
+ }
+
+ /* download firmware */
+ ret = tda10071_wr_reg(priv, 0xe0, 0x7f);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x81);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf8, 0x00);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf9, 0x00);
+ 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);
+
+ /* do not download last byte */
+ fw_size = fw->size - 1;
+
+ for (remaining = fw_size; remaining > 0;
+ remaining -= (priv->cfg.i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (priv->cfg.i2c_wr_max - 1))
+ len = (priv->cfg.i2c_wr_max - 1);
+
+ ret = tda10071_wr_regs(priv, 0xfa,
+ (u8 *) &fw->data[fw_size - remaining], len);
+ if (ret) {
+ err("firmware download failed=%d", ret);
+ if (ret)
+ goto error_release_firmware;
+ }
+ }
+ release_firmware(fw);
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x0c);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe0, 0x00);
+ if (ret)
+ goto error;
+
+ /* wait firmware start */
+ msleep(250);
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp) {
+ info("firmware did not run");
+ ret = -EFAULT;
+ goto error;
+ } else {
+ priv->warm = 1;
+ }
+
+ cmd.args[0x00] = CMD_GET_FW_VERSION;
+ cmd.len = 0x01;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, 4);
+ 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);
+
+ 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;
+ 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;
+ 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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01);
+ 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;
+ 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;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error_release_firmware:
+ release_firmware(fw);
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_sleep(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x07, 0x07 },
+ { 0x80, 0x02, 0x02 },
+ { 0xcd, 0xc0, 0xc0 },
+ { 0xce, 0x1b, 0x1b },
+ { 0x9d, 0x01, 0x01 },
+ { 0x9d, 0x02, 0x02 },
+ { 0x9e, 0x01, 0x01 },
+ { 0x87, 0x80, 0x80 },
+ { 0xce, 0x08, 0x08 },
+ { 0xce, 0x10, 0x10 },
+ };
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ cmd.args[0x00] = CMD_SET_SLEEP_MODE;
+ cmd.args[0x01] = 0;
+ cmd.args[0x02] = 1;
+ cmd.len = 0x03;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 8000;
+ s->step_size = 0;
+ s->max_drift = 0;
+
+ return 0;
+}
+
+static void tda10071_release(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ kfree(priv);
+}
+
+struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct tda10071_priv *priv = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, config, sizeof(struct tda10071_config));
+
+ /* chip ID */
+ ret = tda10071_rd_reg(priv, 0xff, &tmp);
+ if (ret || tmp != 0x0f)
+ goto error;
+
+ /* chip type */
+ ret = tda10071_rd_reg(priv, 0xdd, &tmp);
+ if (ret || tmp != 0x00)
+ goto error;
+
+ /* chip version */
+ ret = tda10071_rd_reg(priv, 0xfe, &tmp);
+ if (ret || tmp != 0x01)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ return &priv->fe;
+error:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(tda10071_attach);
+
+static struct dvb_frontend_ops tda10071_ops = {
+ .info = {
+ .name = "NXP TDA10071",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_RECOVER |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = tda10071_release,
+
+ .get_tune_settings = tda10071_get_tune_settings,
+
+ .init = tda10071_init,
+ .sleep = tda10071_sleep,
+
+ .set_frontend = tda10071_set_frontend,
+ .get_frontend = tda10071_get_frontend,
+
+ .read_status = tda10071_read_status,
+ .read_snr = tda10071_read_snr,
+ .read_signal_strength = tda10071_read_signal_strength,
+ .read_ber = tda10071_read_ber,
+ .read_ucblocks = tda10071_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd,
+ .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply,
+ .diseqc_send_burst = tda10071_diseqc_send_burst,
+
+ .set_tone = tda10071_set_tone,
+ .set_voltage = tda10071_set_voltage,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda10071.h b/drivers/media/dvb/frontends/tda10071.h
new file mode 100644
index 000000000000..21163c4b555c
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071.h
@@ -0,0 +1,81 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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 TDA10071_H
+#define TDA10071_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10071_config {
+ /* Demodulator I2C address.
+ * Default: none, must set
+ * Values: 0x55,
+ */
+ u8 i2c_address;
+
+ /* Max bytes I2C provider can write at once.
+ * Note: Buffer is taken from the stack currently!
+ * Default: none, must set
+ * Values:
+ */
+ u16 i2c_wr_max;
+
+ /* TS output mode.
+ * Default: TDA10071_TS_SERIAL
+ * Values:
+ */
+#define TDA10071_TS_SERIAL 0
+#define TDA10071_TS_PARALLEL 1
+ u8 ts_mode;
+
+ /* Input spectrum inversion.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool spec_inv;
+
+ /* Xtal frequency Hz
+ * Default: none, must set
+ * Values:
+ */
+ u32 xtal;
+
+ /* PLL multiplier.
+ * Default: none, must set
+ * Values:
+ */
+ u8 pll_multiplier;
+};
+
+
+#if defined(CONFIG_DVB_TDA10071) || \
+ (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda10071_attach(
+ const struct tda10071_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *tda10071_attach(
+ const struct tda10071_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* TDA10071_H */
diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h
new file mode 100644
index 000000000000..93c5e6317f07
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10071_priv.h
@@ -0,0 +1,122 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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 TDA10071_PRIV
+#define TDA10071_PRIV
+
+#include "dvb_frontend.h"
+#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;
+ struct tda10071_config cfg;
+
+ u8 meas_count[2];
+ u32 ber;
+ u32 ucb;
+ fe_status_t fe_status;
+ fe_delivery_system_t delivery_system;
+ bool warm; /* FW running */
+};
+
+static struct tda10071_modcod {
+ fe_delivery_system_t delivery_system;
+ fe_modulation_t modulation;
+ fe_code_rate_t fec;
+ u8 val;
+} TDA10071_MODCOD[] = {
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 },
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
+ /* QPSK */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0x2d },
+ { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
+ { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
+ { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
+ { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
+ { SYS_DVBS, QPSK, FEC_7_8, 0x32 },
+};
+
+struct tda10071_reg_val_mask {
+ u8 reg;
+ u8 val;
+ u8 mask;
+};
+
+/* firmware filename */
+#define TDA10071_DEFAULT_FIRMWARE "dvb-fe-tda10071.fw"
+
+/* firmware commands */
+#define CMD_DEMOD_INIT 0x10
+#define CMD_CHANGE_CHANNEL 0x11
+#define CMD_MPEG_CONFIG 0x13
+#define CMD_TUNER_INIT 0x15
+#define CMD_GET_AGCACC 0x1a
+
+#define CMD_LNB_CONFIG 0x20
+#define CMD_LNB_SEND_DISEQC 0x21
+#define CMD_LNB_SET_DC_LEVEL 0x22
+#define CMD_LNB_PCB_CONFIG 0x23
+#define CMD_LNB_SEND_TONEBURST 0x24
+#define CMD_LNB_UPDATE_REPLY 0x25
+
+#define CMD_GET_FW_VERSION 0x35
+#define CMD_SET_SLEEP_MODE 0x36
+#define CMD_BER_CONTROL 0x3e
+#define CMD_BER_UPDATE_COUNTERS 0x3f
+
+/* firmare command struct */
+#define TDA10071_ARGLEN 0x1e
+struct tda10071_cmd {
+ u8 args[TDA10071_ARGLEN];
+ u8 len;
+};
+
+
+#endif /* TDA10071_PRIV */
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c
index 0384e8da4f5e..1b1bf200c55c 100644
--- a/drivers/media/dvb/frontends/tda18271c2dd.c
+++ b/drivers/media/dvb/frontends/tda18271c2dd.c
@@ -1195,7 +1195,7 @@ static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc)
}
#endif
-static int get_frequency(struct dvb_frontend *fe, u32 *frequency)
+static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct tda_state *state = fe->tuner_priv;
@@ -1222,7 +1222,7 @@ static struct dvb_tuner_ops tuner_ops = {
.sleep = sleep,
.set_params = set_params,
.release = release,
- .get_frequency = get_frequency,
+ .get_if_frequency = get_if_frequency,
.get_bandwidth = get_bandwidth,
};
diff --git a/drivers/media/dvb/mantis/Makefile b/drivers/media/dvb/mantis/Makefile
index 98dc5cd258ac..ec8116dcb368 100644
--- a/drivers/media/dvb/mantis/Makefile
+++ b/drivers/media/dvb/mantis/Makefile
@@ -25,4 +25,4 @@ obj-$(CONFIG_MANTIS_CORE) += mantis_core.o
obj-$(CONFIG_DVB_MANTIS) += mantis.o
obj-$(CONFIG_DVB_HOPPER) += hopper.o
-EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c
index 1402062f2c89..71622f65c037 100644
--- a/drivers/media/dvb/mantis/hopper_cards.c
+++ b/drivers/media/dvb/mantis/hopper_cards.c
@@ -65,7 +65,7 @@ static int devs;
static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0, mstat = 0;
+ u32 stat = 0, mask = 0, lstat = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -80,7 +80,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- mstat = lstat = stat & ~MANTIS_INT_RISCSTAT;
+ lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
@@ -126,7 +126,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
}
if (stat & MANTIS_INT_RISCI) {
dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
- mantis->finished_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
tasklet_schedule(&mantis->tasklet);
}
if (stat & MANTIS_INT_I2CDONE) {
diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c
index 05cbb9d95727..c2bb90b3e529 100644
--- a/drivers/media/dvb/mantis/mantis_cards.c
+++ b/drivers/media/dvb/mantis/mantis_cards.c
@@ -73,7 +73,7 @@ static char *label[10] = {
static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0, mstat = 0;
+ u32 stat = 0, mask = 0, lstat = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -88,7 +88,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- mstat = lstat = stat & ~MANTIS_INT_RISCSTAT;
+ lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
@@ -134,7 +134,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
}
if (stat & MANTIS_INT_RISCI) {
dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
- mantis->finished_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
tasklet_schedule(&mantis->tasklet);
}
if (stat & MANTIS_INT_I2CDONE) {
diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h
index 49dbca145bb8..f2410cf0a6bf 100644
--- a/drivers/media/dvb/mantis/mantis_common.h
+++ b/drivers/media/dvb/mantis/mantis_common.h
@@ -123,11 +123,8 @@ struct mantis_pci {
unsigned int num;
/* RISC Core */
- u32 finished_block;
+ u32 busy_block;
u32 last_block;
- u32 line_bytes;
- u32 line_count;
- u32 risc_pos;
u8 *buf_cpu;
dma_addr_t buf_dma;
u32 *risc_cpu;
diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c
index 46202a4012aa..c61ca7d3daea 100644
--- a/drivers/media/dvb/mantis/mantis_dma.c
+++ b/drivers/media/dvb/mantis/mantis_dma.c
@@ -43,13 +43,17 @@
#define RISC_IRQ (0x01 << 24)
#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16))
-#define RISC_FLUSH() (mantis->risc_pos = 0)
-#define RISC_INSTR(opcode) (mantis->risc_cpu[mantis->risc_pos++] = cpu_to_le32(opcode))
+#define RISC_FLUSH(risc_pos) (risc_pos = 0)
+#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode))
#define MANTIS_BUF_SIZE (64 * 1024)
-#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE >> 4)
-#define MANTIS_BLOCK_COUNT (1 << 4)
-#define MANTIS_RISC_SIZE PAGE_SIZE
+#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4)
+#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */
+#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES)
+
+#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES)
+/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */
+#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */
int mantis_dma_exit(struct mantis_pci *mantis)
{
@@ -124,27 +128,6 @@ err:
return -ENOMEM;
}
-static inline int mantis_calc_lines(struct mantis_pci *mantis)
-{
- mantis->line_bytes = MANTIS_BLOCK_BYTES;
- mantis->line_count = MANTIS_BLOCK_COUNT;
-
- while (mantis->line_bytes > 4095) {
- mantis->line_bytes >>= 1;
- mantis->line_count <<= 1;
- }
-
- dprintk(MANTIS_DEBUG, 1, "Mantis RISC block bytes=[%d], line bytes=[%d], line count=[%d]",
- MANTIS_BLOCK_BYTES, mantis->line_bytes, mantis->line_count);
-
- if (mantis->line_count > 255) {
- dprintk(MANTIS_ERROR, 1, "Buffer size error");
- return -EINVAL;
- }
-
- return 0;
-}
-
int mantis_dma_init(struct mantis_pci *mantis)
{
int err = 0;
@@ -158,12 +141,6 @@ int mantis_dma_init(struct mantis_pci *mantis)
goto err;
}
- err = mantis_calc_lines(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "Mantis calc lines failed");
-
- goto err;
- }
return 0;
err:
@@ -174,31 +151,32 @@ EXPORT_SYMBOL_GPL(mantis_dma_init);
static inline void mantis_risc_program(struct mantis_pci *mantis)
{
u32 buf_pos = 0;
- u32 line;
+ u32 line, step;
+ u32 risc_pos;
dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program");
- RISC_FLUSH();
-
- dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u",
- mantis->line_count, mantis->line_bytes);
-
- for (line = 0; line < mantis->line_count; line++) {
- dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d]", line);
- if (!(buf_pos % MANTIS_BLOCK_BYTES)) {
- RISC_INSTR(RISC_WRITE |
- RISC_IRQ |
- RISC_STATUS(((buf_pos / MANTIS_BLOCK_BYTES) +
- (MANTIS_BLOCK_COUNT - 1)) %
- MANTIS_BLOCK_COUNT) |
- mantis->line_bytes);
- } else {
- RISC_INSTR(RISC_WRITE | mantis->line_bytes);
- }
- RISC_INSTR(mantis->buf_dma + buf_pos);
- buf_pos += mantis->line_bytes;
+ RISC_FLUSH(risc_pos);
+
+ dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u",
+ MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES);
+
+ for (line = 0; line < MANTIS_BLOCK_COUNT; line++) {
+ for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) {
+ dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step);
+ if (step == 0) {
+ RISC_INSTR(risc_pos, RISC_WRITE |
+ RISC_IRQ |
+ RISC_STATUS(line) |
+ MANTIS_DMA_TR_BYTES);
+ } else {
+ RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES);
+ }
+ RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos);
+ buf_pos += MANTIS_DMA_TR_BYTES;
+ }
}
- RISC_INSTR(RISC_JUMP);
- RISC_INSTR(mantis->risc_dma);
+ RISC_INSTR(risc_pos, RISC_JUMP);
+ RISC_INSTR(risc_pos, mantis->risc_dma);
}
void mantis_dma_start(struct mantis_pci *mantis)
@@ -210,7 +188,7 @@ void mantis_dma_start(struct mantis_pci *mantis)
mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR);
mmwrite(0, MANTIS_DMA_CTL);
- mantis->last_block = mantis->finished_block = 0;
+ mantis->last_block = mantis->busy_block = 0;
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK);
@@ -245,9 +223,9 @@ void mantis_dma_xfer(unsigned long data)
struct mantis_pci *mantis = (struct mantis_pci *) data;
struct mantis_hwconfig *config = mantis->hwconfig;
- while (mantis->last_block != mantis->finished_block) {
+ while (mantis->last_block != mantis->busy_block) {
dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]",
- mantis->last_block, mantis->finished_block);
+ mantis->last_block, mantis->busy_block);
(config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
(&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES);
diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/dvb/mantis/mantis_vp1041.c
index 38a436ca2fdf..07aa887a4b4a 100644
--- a/drivers/media/dvb/mantis/mantis_vp1041.c
+++ b/drivers/media/dvb/mantis/mantis_vp1041.c
@@ -51,7 +51,6 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x99 },
{ STB0899_DISF22RX , 0xa8 },
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
index 2bc96874d044..13ebeffb705f 100644
--- a/drivers/media/dvb/ngene/Makefile
+++ b/drivers/media/dvb/ngene/Makefile
@@ -6,9 +6,9 @@ ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o
obj-$(CONFIG_DVB_NGENE) += ngene.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
# For the staging CI driver cxd2099
-EXTRA_CFLAGS += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile
index 7ac128724df8..700822350ec5 100644
--- a/drivers/media/dvb/pluto2/Makefile
+++ b/drivers/media/dvb/pluto2/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_PLUTO2) += pluto2.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile
index a66da17bbe31..d80d8e8e7c57 100644
--- a/drivers/media/dvb/pt1/Makefile
+++ b/drivers/media/dvb/pt1/Makefile
@@ -2,4 +2,4 @@ earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
obj-$(CONFIG_DVB_PT1) += earth-pt1.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
index c54140b5ab5a..f233b57c86fb 100644
--- a/drivers/media/dvb/siano/Makefile
+++ b/drivers/media/dvb/siano/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile
index 8a4d5bb20a5b..f6e869372e30 100644
--- a/drivers/media/dvb/ttpci/Makefile
+++ b/drivers/media/dvb/ttpci/Makefile
@@ -17,5 +17,5 @@ obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index cdd31cae46c4..ee8ee1d481fa 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -25,6 +25,8 @@
* the project's page is at http://www.linuxtv.org/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/delay.h>
@@ -253,7 +255,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
switch (av7110->current_input) {
case 1:
- dprintk(1, "switching SAA7113 to Analog Tuner Input.\n");
+ dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
@@ -263,7 +265,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
@@ -272,17 +274,17 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
case 2:
- dprintk(1, "switching SAA7113 to Video AV CVBS Input.\n");
+ dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
case 3:
- dprintk(1, "switching SAA7113 to Video AV Y/C Input.\n");
+ dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
break;
default:
- dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input.\n");
+ dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
}
} else {
adswitch = 0;
@@ -299,7 +301,7 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
@@ -413,7 +415,7 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
+ dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
return -EINVAL;
@@ -429,7 +431,7 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
+ dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
return -EINVAL;
@@ -689,12 +691,12 @@ int av7110_init_analog_module(struct av7110 *av7110)
if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
- printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n",
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
av7110->dvb_adapter.num);
av7110->adac_type = DVB_ADAC_MSP34x0;
} else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
- printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3415\n",
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
av7110->dvb_adapter.num);
av7110->adac_type = DVB_ADAC_MSP34x5;
} else
@@ -715,7 +717,7 @@ int av7110_init_analog_module(struct av7110 *av7110)
msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
- INFO(("saa7113 not accessible.\n"));
+ pr_info("saa7113 not accessible\n");
} else {
u8 *i = saa7113_init_regs;
@@ -733,7 +735,7 @@ int av7110_init_analog_module(struct av7110 *av7110)
/* setup for DVB by default */
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed.\n");
+ dprintk(1, "setting band in demodulator failed\n");
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
@@ -797,7 +799,7 @@ int av7110_init_v4l(struct av7110 *av7110)
ret = saa7146_vv_init(dev, vv_data);
if (ret) {
- ERR(("cannot init capture device. skipping.\n"));
+ ERR("cannot init capture device. skipping\n");
return -ENODEV;
}
vv_data->ops.vidioc_enum_input = vidioc_enum_input;
@@ -814,12 +816,12 @@ int av7110_init_v4l(struct av7110 *av7110)
vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
- ERR(("cannot register capture device. skipping.\n"));
+ ERR("cannot register capture device. skipping\n");
saa7146_vv_release(dev);
return -ENODEV;
}
if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
- ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ ERR("cannot register vbi v4l2 device. skipping\n");
return 0;
}
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index e957d7690bcc..78d32f7e49fc 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -33,6 +33,8 @@
* the project's page is at http://www.linuxtv.org/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "budget.h"
#include "stv0299.h"
#include "stb0899_drv.h"
@@ -149,7 +151,7 @@ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int ad
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 1\n");
+ pr_info("cam ejected 1\n");
}
return result;
}
@@ -168,7 +170,7 @@ static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int a
result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 2\n");
+ pr_info("cam ejected 2\n");
}
return result;
}
@@ -187,7 +189,7 @@ static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addre
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 3\n");
+ pr_info("cam ejected 3\n");
return -ETIMEDOUT;
}
return result;
@@ -207,7 +209,7 @@ static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addr
result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
if (result == -ETIMEDOUT) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 5\n");
+ pr_info("cam ejected 5\n");
}
return result;
}
@@ -289,7 +291,7 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
if (saa7146_read(saa, PSR) & MASK_06) {
if (budget_av->slot_status == SLOTSTATUS_NONE) {
budget_av->slot_status = SLOTSTATUS_PRESENT;
- printk(KERN_INFO "budget-av: cam inserted A\n");
+ pr_info("cam inserted A\n");
}
}
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
@@ -306,11 +308,11 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) {
budget_av->slot_status = SLOTSTATUS_PRESENT;
- printk(KERN_INFO "budget-av: cam inserted B\n");
+ pr_info("cam inserted B\n");
} else if (result < 0) {
if (budget_av->slot_status != SLOTSTATUS_NONE) {
ciintf_slot_shutdown(ca, slot);
- printk(KERN_INFO "budget-av: cam ejected 5\n");
+ pr_info("cam ejected 5\n");
return 0;
}
}
@@ -365,11 +367,11 @@ static int ciintf_init(struct budget_av *budget_av)
if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
&budget_av->ca, 0, 1)) != 0) {
- printk(KERN_ERR "budget-av: ci initialisation failed.\n");
+ pr_err("ci initialisation failed\n");
goto error;
}
- printk(KERN_INFO "budget-av: ci interface initialised.\n");
+ pr_info("ci interface initialised\n");
return 0;
error:
@@ -896,7 +898,6 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x8c },
{ STB0899_DISF22RX , 0x9a },
@@ -1197,6 +1198,7 @@ static u8 read_pwm(struct budget_av *budget_av)
#define SUBID_DVBC_KNC1 0x0020
#define SUBID_DVBC_KNC1_PLUS 0x0021
#define SUBID_DVBC_KNC1_MK3 0x0022
+#define SUBID_DVBC_KNC1_TDA10024 0x0028
#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023
#define SUBID_DVBC_CINERGY1200 0x1156
#define SUBID_DVBC_CINERGY1200_MK3 0x1176
@@ -1316,6 +1318,7 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_EASYWATCH_MK3:
case SUBID_DVBC_CINERGY1200_MK3:
case SUBID_DVBC_KNC1_MK3:
+ case SUBID_DVBC_KNC1_TDA10024:
case SUBID_DVBC_KNC1_PLUS_MK3:
budget_av->reinitialise_demod = 1;
budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
@@ -1343,8 +1346,7 @@ static void frontend_init(struct budget_av *budget_av)
}
if (fe == NULL) {
- printk(KERN_ERR "budget-av: A frontend driver was not found "
- "for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
saa->pci->vendor,
saa->pci->device,
saa->pci->subsystem_vendor,
@@ -1356,7 +1358,7 @@ static void frontend_init(struct budget_av *budget_av)
if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
budget_av->budget.dvb_frontend)) {
- printk(KERN_ERR "budget-av: Frontend registration failed!\n");
+ pr_err("Frontend registration failed!\n");
dvb_frontend_detach(budget_av->budget.dvb_frontend);
budget_av->budget.dvb_frontend = NULL;
}
@@ -1414,7 +1416,7 @@ static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
+ dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= KNC1_INPUTS)
return -EINVAL;
memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
@@ -1428,7 +1430,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
*i = budget_av->cur_input;
- dprintk(1, "VIDIOC_G_INPUT %d.\n", *i);
+ dprintk(1, "VIDIOC_G_INPUT %d\n", *i);
return 0;
}
@@ -1437,7 +1439,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
- dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
+ dprintk(1, "VIDIOC_S_INPUT %d\n", input);
return saa7113_setinput(budget_av, input);
}
@@ -1476,7 +1478,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if (0 != saa7146_vv_init(dev, &vv_data)) {
/* fixme: proper cleanup here */
- ERR(("cannot init vv subsystem.\n"));
+ ERR("cannot init vv subsystem\n");
return err;
}
vv_data.ops.vidioc_enum_input = vidioc_enum_input;
@@ -1485,7 +1487,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
/* fixme: proper cleanup here */
- ERR(("cannot register capture v4l2 device.\n"));
+ ERR("cannot register capture v4l2 device\n");
saa7146_vv_release(dev);
return err;
}
@@ -1502,13 +1504,12 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
mac = budget_av->budget.dvb_adapter.proposed_mac;
if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
- printk(KERN_ERR "KNC1-%d: Could not read MAC from KNC1 card\n",
+ pr_err("KNC1-%d: Could not read MAC from KNC1 card\n",
budget_av->budget.dvb_adapter.num);
memset(mac, 0, 6);
} else {
- printk(KERN_INFO "KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- budget_av->budget.dvb_adapter.num,
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ pr_info("KNC1-%d: MAC addr = %pM\n",
+ budget_av->budget.dvb_adapter.num, mac);
}
budget_av->budget.dvb_adapter.priv = budget_av;
@@ -1558,6 +1559,7 @@ MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024);
MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
@@ -1587,6 +1589,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+ MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028),
MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 926f299b5225..ca02e9722172 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -1053,7 +1053,6 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
{ STB0899_DISRX_ST0 , 0x04 },
{ STB0899_DISRX_ST1 , 0x00 },
{ STB0899_DISPARITY , 0x00 },
- { STB0899_DISFIFO , 0x00 },
{ STB0899_DISSTATUS , 0x20 },
{ STB0899_DISF22 , 0x8c },
{ STB0899_DISF22RX , 0x9a },
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index 37666d4edab6..37d02fe09137 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -110,6 +110,7 @@ static int start_ts_capture(struct budget *budget)
break;
case BUDGET_CIN1200C_MK3:
case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
case BUDGET_KNC1CP_MK3:
if (budget->video_port == BUDGET_VIDEO_PORTA) {
saa7146_write(dev, DD1_INIT, 0x06000200);
@@ -434,6 +435,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
case BUDGET_KNC1CP:
case BUDGET_CIN1200C:
case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1C_TDA10024:
case BUDGET_KNC1CP_MK3:
case BUDGET_CIN1200C_MK3:
budget->buffer_width = TS_WIDTH_DVBC;
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index 3ad0c6789ba7..3d8a806c20bb 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -104,6 +104,7 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_KNC1C_MK3 16
#define BUDGET_KNC1CP_MK3 17
#define BUDGET_KNC1S2 18
+#define BUDGET_KNC1C_TDA10024 19
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/dvb/ttpci/ttpci-eeprom.c
index 7dd54b3026a2..32d43156c548 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.c
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.c
@@ -85,6 +85,35 @@ static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC)
return 0;
}
+int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC)
+{
+ u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
+ 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
+ 0x1d, 0x36, 0x64, 0x78};
+ u8 data[20];
+ int i;
+
+ memcpy(data, encodedMAC, 20);
+
+ for (i = 0; i < 20; i++)
+ data[i] ^= xor[i];
+ for (i = 0; i < 10; i++)
+ data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
+ >> ((data[2 * i + 1] >> 6) & 3);
+
+ if (check_mac_tt(data))
+ return -ENODEV;
+
+ decodedMAC[0] = data[2];
+ decodedMAC[1] = data[1];
+ decodedMAC[2] = data[0];
+ decodedMAC[3] = data[6];
+ decodedMAC[4] = data[5];
+ decodedMAC[5] = data[4];
+ return 0;
+}
+EXPORT_SYMBOL(ttpci_eeprom_decode_mac);
+
static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC)
{
int ret;
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/dvb/ttpci/ttpci-eeprom.h
index e2dc6cfe205c..dcc33d5a5cb1 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.h
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.h
@@ -28,6 +28,7 @@
#include <linux/types.h>
#include <linux/i2c.h>
+extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC);
extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
#endif
diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile
index fbe2b9514c21..8d6c4acb7f1d 100644
--- a/drivers/media/dvb/ttusb-budget/Makefile
+++ b/drivers/media/dvb/ttusb-budget/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile
index 2d70a8269391..ed28b5384d20 100644
--- a/drivers/media/dvb/ttusb-dec/Makefile
+++ b/drivers/media/dvb/ttusb-dec/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 52798a111e16..ccd5f0d8a012 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -426,7 +426,6 @@ config RADIO_TIMBERDALE
config RADIO_WL1273
tristate "Texas Instruments WL1273 I2C FM Radio"
depends on I2C && VIDEO_V4L2
- select MFD_CORE
select MFD_WL1273_CORE
select FW_LOADER
---help---
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index f484a6e04eb2..390daf94d847 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -27,4 +27,4 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
obj-$(CONFIG_RADIO_WL128X) += wl128x/
-EXTRA_CFLAGS += -Isound
+ccflags-y += -Isound
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c
index 444b4cf7e65c..d1fab5885061 100644
--- a/drivers/media/radio/radio-si4713.c
+++ b/drivers/media/radio/radio-si4713.c
@@ -92,10 +92,6 @@ static int radio_si4713_s_audout(struct file *file, void *priv,
static int radio_si4713_querycap(struct file *file, void *priv,
struct v4l2_capability *capability)
{
- struct radio_si4713_device *rsdev;
-
- rsdev = video_get_drvdata(video_devdata(file));
-
strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver));
strlcpy(capability->card, "Silicon Labs Si4713 Modulator",
sizeof(capability->card));
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 95ddcc4845d3..db20904d01f0 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -128,8 +128,10 @@ struct tea5764_write_regs {
u16 rdsbbl; /* PAUSEDET & RDSBBL */
} __attribute__ ((packed));
-#ifndef RADIO_TEA5764_XTAL
+#ifdef CONFIG_RADIO_TEA5764_XTAL
#define RADIO_TEA5764_XTAL 1
+#else
+#define RADIO_TEA5764_XTAL 0
#endif
static int radio_nr = -1;
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 46cacf845049..6d1e4e750f63 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -2109,7 +2109,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
V4L2_CID_TUNE_ANTENNA_CAPACITOR,
0, 255, 1, 255);
if (ctrl)
- ctrl->is_volatile = 1;
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
if (radio->ctrl_handler.error) {
r = radio->ctrl_handler.error;
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 4cf537043f99..a6ad707fae96 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -395,7 +395,6 @@ int si470x_disconnect_check(struct si470x_device *radio)
static void si470x_int_in_callback(struct urb *urb)
{
struct si470x_device *radio = urb->context;
- unsigned char buf[RDS_REPORT_SIZE];
int retval;
unsigned char regnr;
unsigned char blocknum;
@@ -423,7 +422,6 @@ static void si470x_int_in_callback(struct urb *urb)
if (urb->actual_length > 0) {
/* Update RDS registers with URB data */
- buf[0] = RDS_REPORT;
for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
radio->registers[STATUSRSSI + regnr] =
get_unaligned_be16(&radio->int_in_buffer[
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index ec1d52f38904..b93d8cf23b03 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -84,12 +84,14 @@ static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
ret = copy_from_user(&rds, buf, sizeof(rds));
fmdbg("(%d)type: %d, text %s, af %d\n",
ret, rds.text_type, rds.text, rds.af_freq);
+ if (ret)
+ return -EFAULT;
fmdev = video_drvdata(file);
fm_tx_set_radio_text(fmdev, rds.text, rds.text_type);
fm_tx_set_af(fmdev, rds.af_freq);
- return 0;
+ return sizeof(rds);
}
static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts)
@@ -557,7 +559,7 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
255, 1, 255);
if (ctrl)
- ctrl->is_volatile = 1;
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
return 0;
}
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 899f783d92fb..aeb7f43dfb65 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -4,8 +4,8 @@ menuconfig RC_CORE
default INPUT
---help---
Enable support for Remote Controllers on Linux. This is
- needed in order to support several video capture adapters.
- Currently, all supported devices use InfraRed.
+ 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
@@ -108,6 +108,25 @@ config IR_LIRC_CODEC
Enable this option to pass raw IR to and from userspace via
the LIRC interface.
+config RC_ATI_REMOTE
+ tristate "ATI / X10 based USB RF remote controls"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ help
+ Say Y here if you want to use an X10 based USB remote control.
+ These are RF remotes with USB receivers.
+
+ Such devices include the ATI remote that comes with many of ATI's
+ All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote,
+ Medion RF remote, and SnapStream FireFly remote.
+
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote.
+
config IR_ENE
tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
depends on PNP
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index f224db027c41..2156e786b557 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
+obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
diff --git a/drivers/input/misc/ati_remote.c b/drivers/media/rc/ati_remote.c
index bce57129afba..303f22ea04c0 100644
--- a/drivers/input/misc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -1,6 +1,7 @@
/*
* USB ATI Remote support
*
+ * Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
*
@@ -90,9 +91,11 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/usb/input.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
+#include <media/rc-core.h>
/*
* Module and Version Information, Module Parameters
@@ -104,8 +107,9 @@
#define ATI_REMOTE_PRODUCT_ID 0x0004
#define NVIDIA_REMOTE_PRODUCT_ID 0x0005
#define MEDION_REMOTE_PRODUCT_ID 0x0006
+#define FIREFLY_REMOTE_PRODUCT_ID 0x0008
-#define DRIVER_VERSION "2.2.1"
+#define DRIVER_VERSION "2.2.1"
#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
@@ -139,16 +143,21 @@ static int repeat_delay = REPEAT_DELAY;
module_param(repeat_delay, int, 0644);
MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+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)
#undef err
#define err(format, arg...) printk(KERN_ERR format , ## arg)
static struct usb_device_id ati_remote_table[] = {
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID) },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY },
{} /* Terminating entry */
};
@@ -167,6 +176,7 @@ static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
struct ati_remote {
struct input_dev *idev;
+ struct rc_dev *rdev;
struct usb_device *udev;
struct usb_interface *interface;
@@ -186,11 +196,16 @@ struct ati_remote {
unsigned int repeat_count;
- char name[NAME_BUFSIZE];
- char phys[NAME_BUFSIZE];
+ char rc_name[NAME_BUFSIZE];
+ char rc_phys[NAME_BUFSIZE];
+ char mouse_name[NAME_BUFSIZE];
+ char mouse_phys[NAME_BUFSIZE];
wait_queue_head_t wait;
int send_flags;
+
+ int users; /* 0-2, users are rc and input */
+ struct mutex open_mutex;
};
/* "Kinds" of messages sent from the hardware to the driver. */
@@ -233,64 +248,11 @@ static const struct {
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
- /* keyboard. */
- {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
- {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
- {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
- {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
- {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
- {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
- {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
- {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
- {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
- {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
- {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
- {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
- {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
- {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
- {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
- {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
-
- /* "special" keys */
- {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
- {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
- {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
- {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
- {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
- {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
- {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
- {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
- {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
- {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
- {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
- {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
- {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
- {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
- {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
- {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
- {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
- {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
- {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
- {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
- {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
- {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
- {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
- {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
- {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
- {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
- {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
- {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
- {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
- {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
- {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
- {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
-
+ /* Non-mouse events are handled by rc-core */
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
};
/* Local function prototypes */
-static int ati_remote_open (struct input_dev *inputdev);
-static void ati_remote_close (struct input_dev *inputdev);
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);
@@ -313,9 +275,10 @@ static struct usb_driver ati_remote_driver = {
static void ati_remote_dump(struct device *dev, unsigned char *data,
unsigned int len)
{
- if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
- dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
- else if (len == 4)
+ if (len == 1) {
+ if (data[0] != (unsigned char)0xff && data[0] != 0x00)
+ dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
+ } else if (len == 4)
dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
data[0], data[1], data[2], data[3]);
else
@@ -326,29 +289,60 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
/*
* ati_remote_open
*/
-static int ati_remote_open(struct input_dev *inputdev)
+static int ati_remote_open(struct ati_remote *ati_remote)
{
- struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ int err = 0;
+
+ mutex_lock(&ati_remote->open_mutex);
+
+ if (ati_remote->users++ != 0)
+ goto out; /* one was already active */
/* On first open, submit the read urb which was set up previously. */
ati_remote->irq_urb->dev = ati_remote->udev;
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
dev_err(&ati_remote->interface->dev,
"%s: usb_submit_urb failed!\n", __func__);
- return -EIO;
+ err = -EIO;
}
- return 0;
+out: mutex_unlock(&ati_remote->open_mutex);
+ return err;
}
/*
* ati_remote_close
*/
-static void ati_remote_close(struct input_dev *inputdev)
+static void ati_remote_close(struct ati_remote *ati_remote)
+{
+ mutex_lock(&ati_remote->open_mutex);
+ if (--ati_remote->users == 0)
+ usb_kill_urb(ati_remote->irq_urb);
+ mutex_unlock(&ati_remote->open_mutex);
+}
+
+static int ati_remote_input_open(struct input_dev *inputdev)
{
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ return ati_remote_open(ati_remote);
+}
- usb_kill_urb(ati_remote->irq_urb);
+static void ati_remote_input_close(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ ati_remote_close(ati_remote);
+}
+
+static int ati_remote_rc_open(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_rc_close(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ ati_remote_close(ati_remote);
}
/*
@@ -413,10 +407,8 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
/*
* Decide if the table entry matches the remote input.
*/
- if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
- ((((ati_remote_tbl[i].data1 >> 4) -
- (d1 >> 4) + rem) & 0x0f) == 0x0f) &&
- (ati_remote_tbl[i].data2 == d2))
+ if (ati_remote_tbl[i].data1 == d1 &&
+ ati_remote_tbl[i].data2 == d2)
return i;
}
@@ -468,8 +460,10 @@ static void ati_remote_input_report(struct urb *urb)
struct ati_remote *ati_remote = urb->context;
unsigned char *data= ati_remote->inbuf;
struct input_dev *dev = ati_remote->idev;
- int index, acc;
+ int index = -1;
+ int acc;
int remote_num;
+ unsigned char scancode[2];
/* Deal with strange looking inputs */
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
@@ -481,26 +475,41 @@ static void ati_remote_input_report(struct urb *urb)
/* Mask unwanted remote channels. */
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
remote_num = (data[3] >> 4) & 0x0f;
- if (channel_mask & (1 << (remote_num + 1))) {
+ 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",
remote_num, data[1], data[2], channel_mask);
return;
}
- /* Look up event code index in translation table */
- index = ati_remote_event_lookup(remote_num, data[1], data[2]);
- if (index < 0) {
- dev_warn(&ati_remote->interface->dev,
- "Unknown input from channel 0x%02x: data %02x,%02x\n",
- remote_num, data[1], data[2]);
- return;
- }
- dbginfo(&ati_remote->interface->dev,
- "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
- remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+ scancode[0] = (((data[1] - ((remote_num + 1) << 4)) & 0xf0) | (data[1] & 0x0f));
- if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+ /*
+ * Some devices (e.g. SnapStream Firefly) use 8080 as toggle code,
+ * so we have to clear them. The first bit is a bit tricky as the
+ * "non-toggled" state depends on remote_num, so we xor it with the
+ * second bit which is only used for toggle.
+ */
+ scancode[0] ^= (data[2] & 0x80);
+
+ scancode[1] = data[2] & ~0x80;
+
+ /* Look up event code index in mouse translation table. */
+ index = ati_remote_event_lookup(remote_num, scancode[0], scancode[1]);
+
+ if (index >= 0) {
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; mouse data %02x,%02x; index %d; keycode %d\n",
+ remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+ if (!dev)
+ return; /* no mouse device */
+ } else
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; key data %02x,%02x, scancode %02x,%02x\n",
+ remote_num, data[1], data[2], scancode[0], scancode[1]);
+
+
+ if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code,
ati_remote_tbl[index].value);
@@ -510,7 +519,7 @@ static void ati_remote_input_report(struct urb *urb)
return;
}
- if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+ if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) {
unsigned long now = jiffies;
/* Filter duplicate events which happen "too close" together. */
@@ -538,6 +547,20 @@ static void ati_remote_input_report(struct urb *urb)
msecs_to_jiffies(repeat_delay))))
return;
+ if (index < 0) {
+ /* Not a mouse event, hand it to rc-core. */
+ u32 rc_code = (scancode[0] << 8) | scancode[1];
+
+ /*
+ * We don't use the rc-core repeat handling yet as
+ * it would cause ghost repeats which would be a
+ * regression for this driver.
+ */
+ rc_keydown_notimeout(ati_remote->rdev, rc_code,
+ data[2]);
+ rc_keyup(ati_remote->rdev);
+ return;
+ }
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 1);
@@ -630,7 +653,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
return -1;
ati_remote->outbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
- &ati_remote->outbuf_dma);
+ &ati_remote->outbuf_dma);
if (!ati_remote->outbuf)
return -1;
@@ -675,14 +698,33 @@ static void ati_remote_input_init(struct ati_remote *ati_remote)
input_set_drvdata(idev, ati_remote);
- idev->open = ati_remote_open;
- idev->close = ati_remote_close;
+ idev->open = ati_remote_input_open;
+ idev->close = ati_remote_input_close;
- idev->name = ati_remote->name;
- idev->phys = ati_remote->phys;
+ idev->name = ati_remote->mouse_name;
+ idev->phys = ati_remote->mouse_phys;
usb_to_input_id(ati_remote->udev, &idev->id);
- idev->dev.parent = &ati_remote->udev->dev;
+ idev->dev.parent = &ati_remote->interface->dev;
+}
+
+static void ati_remote_rc_init(struct ati_remote *ati_remote)
+{
+ struct rc_dev *rdev = ati_remote->rdev;
+
+ rdev->priv = ati_remote;
+ rdev->driver_type = RC_DRIVER_SCANCODE;
+ rdev->allowed_protos = RC_TYPE_OTHER;
+ rdev->driver_name = "ati_remote";
+
+ rdev->open = ati_remote_rc_open;
+ rdev->close = ati_remote_rc_close;
+
+ rdev->input_name = ati_remote->rc_name;
+ rdev->input_phys = ati_remote->rc_phys;
+
+ usb_to_input_id(ati_remote->udev, &rdev->input_id);
+ rdev->dev.parent = &ati_remote->interface->dev;
}
static int ati_remote_initialize(struct ati_remote *ati_remote)
@@ -735,6 +777,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
struct ati_remote *ati_remote;
struct input_dev *input_dev;
+ struct rc_dev *rc_dev;
int err = -ENOMEM;
if (iface_host->desc.bNumEndpoints != 2) {
@@ -755,8 +798,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
}
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ati_remote || !input_dev)
+ rc_dev = rc_allocate_device();
+ if (!ati_remote || !rc_dev)
goto fail1;
/* Allocate URB buffers, URBs */
@@ -766,44 +809,78 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
ati_remote->endpoint_in = endpoint_in;
ati_remote->endpoint_out = endpoint_out;
ati_remote->udev = udev;
- ati_remote->idev = input_dev;
+ ati_remote->rdev = rc_dev;
ati_remote->interface = interface;
- usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
- strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+ usb_make_path(udev, ati_remote->rc_phys, sizeof(ati_remote->rc_phys));
+ strlcpy(ati_remote->mouse_phys, ati_remote->rc_phys,
+ sizeof(ati_remote->mouse_phys));
+
+ strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys));
+ strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys));
if (udev->manufacturer)
- strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
+ strlcpy(ati_remote->rc_name, udev->manufacturer,
+ sizeof(ati_remote->rc_name));
if (udev->product)
- snprintf(ati_remote->name, sizeof(ati_remote->name),
- "%s %s", ati_remote->name, udev->product);
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+ "%s %s", ati_remote->rc_name, udev->product);
- if (!strlen(ati_remote->name))
- snprintf(ati_remote->name, sizeof(ati_remote->name),
+ if (!strlen(ati_remote->rc_name))
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
DRIVER_DESC "(%04x,%04x)",
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
- ati_remote_input_init(ati_remote);
+ snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name),
+ "%s mouse", ati_remote->rc_name);
+
+ if (id->driver_info)
+ rc_dev->map_name = (const char *)id->driver_info;
+ else
+ rc_dev->map_name = RC_MAP_ATI_X10;
+
+ ati_remote_rc_init(ati_remote);
+ mutex_init(&ati_remote->open_mutex);
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
err = ati_remote_initialize(ati_remote);
if (err)
goto fail3;
- /* Set up and register input device */
- err = input_register_device(ati_remote->idev);
+ /* Set up and register rc device */
+ err = rc_register_device(ati_remote->rdev);
if (err)
goto fail3;
+ /* use our delay for rc_dev */
+ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
+
+ /* Set up and register mouse input device */
+ if (mouse) {
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail4;
+
+ ati_remote->idev = input_dev;
+ ati_remote_input_init(ati_remote);
+ err = input_register_device(input_dev);
+
+ if (err)
+ goto fail5;
+ }
+
usb_set_intfdata(interface, ati_remote);
return 0;
+ fail5: input_free_device(input_dev);
+ fail4: rc_unregister_device(rc_dev);
+ rc_dev = NULL;
fail3: usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
fail2: ati_remote_free_buffers(ati_remote);
- fail1: input_free_device(input_dev);
+ fail1: rc_free_device(rc_dev);
kfree(ati_remote);
return err;
}
@@ -824,7 +901,9 @@ static void ati_remote_disconnect(struct usb_interface *interface)
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
- input_unregister_device(ati_remote->idev);
+ if (ati_remote->idev)
+ input_unregister_device(ati_remote->idev);
+ rc_unregister_device(ati_remote->rdev);
ati_remote_free_buffers(ati_remote);
kfree(ati_remote);
}
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 2b9c2569d74a..cf10ecf5acec 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -30,6 +30,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -118,31 +120,31 @@ static int ene_hw_detect(struct ene_device *dev)
dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
if (hw_revision == 0xFF) {
- ene_warn("device seems to be disabled");
- ene_warn("send a mail to lirc-list@lists.sourceforge.net");
- ene_warn("please attach output of acpidump and dmidecode");
+ pr_warn("device seems to be disabled\n");
+ pr_warn("send a mail to lirc-list@lists.sourceforge.net\n");
+ pr_warn("please attach output of acpidump and dmidecode\n");
return -ENODEV;
}
- ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x",
- chip_major, chip_minor, old_ver, hw_revision);
+ pr_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+ chip_major, chip_minor, old_ver, hw_revision);
- ene_notice("PLL freq = %d", dev->pll_freq);
+ pr_notice("PLL freq = %d\n", dev->pll_freq);
if (chip_major == 0x33) {
- ene_warn("chips 0x33xx aren't supported");
+ pr_warn("chips 0x33xx aren't supported\n");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
- ene_notice("KB3926C detected");
+ pr_notice("KB3926C detected\n");
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
- ene_notice("KB3926B detected");
+ pr_notice("KB3926B detected\n");
} else {
dev->hw_revision = ENE_HW_D;
- ene_notice("KB3926D or higher detected");
+ pr_notice("KB3926D or higher detected\n");
}
/* detect features hardware supports */
@@ -152,7 +154,7 @@ static int ene_hw_detect(struct ene_device *dev)
fw_reg1 = ene_read_reg(dev, ENE_FW1);
fw_reg2 = ene_read_reg(dev, ENE_FW2);
- ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2);
+ pr_notice("Firmware regs: %02x %02x\n", fw_reg1, fw_reg2);
dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
@@ -161,30 +163,29 @@ static int ene_hw_detect(struct ene_device *dev)
if (dev->hw_learning_and_tx_capable)
dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
- ene_notice("Hardware features:");
+ pr_notice("Hardware features:\n");
if (dev->hw_learning_and_tx_capable) {
- ene_notice("* Supports transmitting & learning mode");
- ene_notice(" This feature is rare and therefore,");
- ene_notice(" you are welcome to test it,");
- ene_notice(" and/or contact the author via:");
- ene_notice(" lirc-list@lists.sourceforge.net");
- ene_notice(" or maximlevitsky@gmail.com");
+ pr_notice("* Supports transmitting & learning mode\n");
+ pr_notice(" This feature is rare and therefore,\n");
+ pr_notice(" you are welcome to test it,\n");
+ pr_notice(" and/or contact the author via:\n");
+ pr_notice(" lirc-list@lists.sourceforge.net\n");
+ pr_notice(" or maximlevitsky@gmail.com\n");
- ene_notice("* Uses GPIO %s for IR raw input",
- dev->hw_use_gpio_0a ? "40" : "0A");
+ pr_notice("* Uses GPIO %s for IR raw input\n",
+ dev->hw_use_gpio_0a ? "40" : "0A");
if (dev->hw_fan_input)
- ene_notice("* Uses unused fan feedback input as source"
- " of demodulated IR data");
+ pr_notice("* Uses unused fan feedback input as source of demodulated IR data\n");
}
if (!dev->hw_fan_input)
- ene_notice("* Uses GPIO %s for IR demodulated input",
- dev->hw_use_gpio_0a ? "0A" : "40");
+ pr_notice("* Uses GPIO %s for IR demodulated input\n",
+ dev->hw_use_gpio_0a ? "0A" : "40");
if (dev->hw_extra_buffer)
- ene_notice("* Uses new style input buffer");
+ pr_notice("* Uses new style input buffer\n");
return 0;
}
@@ -215,13 +216,13 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
- ene_notice("Hardware uses 2 extended buffers:");
- ene_notice(" 0x%04x - len : %d", dev->extra_buf1_address,
- dev->extra_buf1_len);
- ene_notice(" 0x%04x - len : %d", dev->extra_buf2_address,
- dev->extra_buf2_len);
+ pr_notice("Hardware uses 2 extended buffers:\n");
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf1_address, dev->extra_buf1_len);
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf2_address, dev->extra_buf2_len);
- ene_notice("Total buffer len = %d", dev->buffer_len);
+ pr_notice("Total buffer len = %d\n", dev->buffer_len);
if (dev->buffer_len > 64 || dev->buffer_len < 16)
goto error;
@@ -240,7 +241,7 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
return;
error:
- ene_warn("Error validating extra buffers, device probably won't work");
+ pr_warn("Error validating extra buffers, device probably won't work\n");
dev->hw_extra_buffer = false;
ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
}
@@ -588,7 +589,7 @@ static void ene_tx_enable(struct ene_device *dev)
dbg("TX: Transmitter #2 is connected");
if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN)))
- ene_warn("TX: transmitter cable isn't connected!");
+ pr_warn("TX: transmitter cable isn't connected!\n");
/* disable receive on revc */
if (dev->hw_revision == ENE_HW_C)
@@ -615,7 +616,7 @@ static void ene_tx_sample(struct ene_device *dev)
bool pulse = dev->tx_sample_pulse;
if (!dev->tx_buffer) {
- ene_warn("TX: BUG: attempt to transmit NULL buffer");
+ pr_warn("TX: BUG: attempt to transmit NULL buffer\n");
return;
}
@@ -1049,7 +1050,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
dev->hw_learning_and_tx_capable = true;
setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
(long unsigned int)dev);
- ene_warn("Simulation of TX activated");
+ pr_warn("Simulation of TX activated\n");
}
if (!dev->hw_learning_and_tx_capable)
@@ -1089,7 +1090,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
if (error < 0)
goto error;
- ene_notice("driver has been successfully loaded");
+ pr_notice("driver has been successfully loaded\n");
return 0;
error:
if (dev && dev->irq >= 0)
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 017c209cdf8a..fd108d90f750 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -182,20 +182,11 @@
#define ENE_HW_C 2 /* 3926C */
#define ENE_HW_D 3 /* 3926D or later */
-#define ene_printk(level, text, ...) \
- printk(level ENE_DRIVER_NAME ": " text "\n", ## __VA_ARGS__)
-
-#define ene_notice(text, ...) ene_printk(KERN_NOTICE, text, ## __VA_ARGS__)
-#define ene_warn(text, ...) ene_printk(KERN_WARNING, text, ## __VA_ARGS__)
-
-
-#define __dbg(level, format, ...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG ENE_DRIVER_NAME \
- ": " format "\n", ## __VA_ARGS__); \
- } while (0)
-
+#define __dbg(level, format, ...) \
+do { \
+ if (debug >= level) \
+ pr_debug(format "\n", ## __VA_ARGS__); \
+} while (0)
#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__)
#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__)
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 6bc35eeb653b..6ed96465137a 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
#include <linux/input.h>
#include <linux/usb.h>
@@ -516,19 +517,19 @@ static int send_packet(struct imon_context *ictx)
if (retval) {
ictx->tx.busy = false;
smp_rmb(); /* ensure later readers know we're not busy */
- pr_err("error submitting urb(%d)\n", retval);
+ pr_err_ratelimited("error submitting urb(%d)\n", retval);
} else {
/* Wait for transmission to complete (or abort) */
mutex_unlock(&ictx->lock);
retval = wait_for_completion_interruptible(
&ictx->tx.finished);
if (retval)
- pr_err("task interrupted\n");
+ pr_err_ratelimited("task interrupted\n");
mutex_lock(&ictx->lock);
retval = ictx->tx.status;
if (retval)
- pr_err("packet tx failed (%d)\n", retval);
+ pr_err_ratelimited("packet tx failed (%d)\n", retval);
}
kfree(control_req);
@@ -830,20 +831,20 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->dev_present_intf0) {
- pr_err("no iMON device present\n");
+ pr_err_ratelimited("no iMON device present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes <= 0 || n_bytes > 32) {
- pr_err("invalid payload size\n");
+ pr_err_ratelimited("invalid payload size\n");
retval = -EINVAL;
goto exit;
}
@@ -869,7 +870,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
goto exit;
} else {
seq += 2;
@@ -883,7 +884,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx->usb_tx_buf[7] = (unsigned char) seq;
retval = send_packet(ictx);
if (retval)
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
exit:
mutex_unlock(&ictx->lock);
@@ -912,20 +913,21 @@ static ssize_t lcd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->display_supported) {
- pr_err("no iMON display present\n");
+ pr_err_ratelimited("no iMON display present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes != 8) {
- pr_err("invalid payload size: %d (expected 8)\n", (int)n_bytes);
+ pr_err_ratelimited("invalid payload size: %d (expected 8)\n",
+ (int)n_bytes);
retval = -EINVAL;
goto exit;
}
@@ -937,7 +939,7 @@ static ssize_t lcd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed!\n");
+ pr_err_ratelimited("send packet failed!\n");
goto exit;
} else {
dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
@@ -1656,7 +1658,7 @@ static void usb_rx_callback_intf0(struct urb *urb)
return;
ictx = (struct imon_context *)urb->context;
- if (!ictx)
+ if (!ictx || !ictx->dev_present_intf0)
return;
switch (urb->status) {
@@ -1688,7 +1690,7 @@ static void usb_rx_callback_intf1(struct urb *urb)
return;
ictx = (struct imon_context *)urb->context;
- if (!ictx)
+ if (!ictx || !ictx->dev_present_intf1)
return;
switch (urb->status) {
@@ -2116,7 +2118,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
ictx->dev = dev;
ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf0 = true;
ictx->rx_urb_intf0 = rx_urb;
ictx->tx_urb = tx_urb;
ictx->rf_device = false;
@@ -2155,6 +2156,8 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
goto rdev_setup_failed;
}
+ ictx->dev_present_intf0 = true;
+
mutex_unlock(&ictx->lock);
return ictx;
@@ -2198,7 +2201,6 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
}
ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf1 = true;
ictx->rx_urb_intf1 = rx_urb;
ret = -ENODEV;
@@ -2227,6 +2229,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
goto urb_submit_failed;
}
+ ictx->dev_present_intf1 = true;
+
mutex_unlock(&ictx->lock);
return ictx;
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index e5eeec4da76e..ec2e67fd236b 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -98,7 +98,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
-static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
+static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
size_t n, loff_t *ppos)
{
struct lirc_codec *lirc;
@@ -140,10 +140,11 @@ out:
}
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
- unsigned long __user arg)
+ unsigned long arg)
{
struct lirc_codec *lirc;
struct rc_dev *dev;
+ u32 __user *argp = (u32 __user *)(arg);
int ret = 0;
__u32 val = 0, tmp;
@@ -156,7 +157,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
return -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
- ret = get_user(val, (__u32 *)arg);
+ ret = get_user(val, argp);
if (ret)
return ret;
}
@@ -265,7 +266,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
}
if (_IOC_DIR(cmd) & _IOC_READ)
- ret = put_user(val, (__u32 *)arg);
+ ret = put_user(val, argp);
return ret;
}
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index b57fc83fb4d2..36e4d5e8dd6a 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-apac-viewcomp.o \
rc-asus-pc39.o \
rc-ati-tv-wonder-hd-600.o \
+ rc-ati-x10.o \
rc-avermedia-a16d.o \
rc-avermedia.o \
rc-avermedia-cardbus.o \
@@ -47,6 +48,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-lirc.o \
rc-lme2510.o \
rc-manli.o \
+ rc-medion-x10.o \
rc-msi-digivox-ii.o \
rc-msi-digivox-iii.o \
rc-msi-tvanywhere.o \
@@ -70,6 +72,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-hauppauge.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
+ rc-snapstream-firefly.o \
rc-streamzap.o \
rc-tbs-nec.o \
rc-technisat-usb2.o \
diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c
new file mode 100644
index 000000000000..e1b8b2605c48
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ati-x10.c
@@ -0,0 +1,104 @@
+/*
+ * ATI X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on the static generic keytable previously found in
+ * ati_remote.c, which is
+ * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Copyright (c) 2002 Vladimir Dergachev
+ *
+ * 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/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table ati_x10[] = {
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xc500, KEY_A },
+ { 0xc601, KEY_B },
+ { 0xde19, KEY_C },
+ { 0xe01b, KEY_D },
+ { 0xe621, KEY_E },
+ { 0xe823, KEY_F },
+
+ { 0xdd18, KEY_KPENTER }, /* "check" */
+ { 0xdb16, KEY_MENU }, /* "menu" */
+ { 0xc702, KEY_POWER }, /* Power */
+ { 0xc803, KEY_TV }, /* TV */
+ { 0xc904, KEY_DVD }, /* DVD */
+ { 0xca05, KEY_WWW }, /* WEB */
+ { 0xcb06, KEY_BOOKMARKS }, /* "book" */
+ { 0xcc07, KEY_EDIT }, /* "hand" */
+ { 0xe11c, KEY_COFFEE }, /* "timer" */
+ { 0xe520, KEY_FRONT }, /* "max" */
+ { 0xe21d, KEY_LEFT }, /* left */
+ { 0xe41f, KEY_RIGHT }, /* right */
+ { 0xe722, KEY_DOWN }, /* down */
+ { 0xdf1a, KEY_UP }, /* up */
+ { 0xe31e, KEY_OK }, /* "OK" */
+ { 0xce09, KEY_VOLUMEDOWN }, /* VOL + */
+ { 0xcd08, KEY_VOLUMEUP }, /* VOL - */
+ { 0xcf0a, KEY_MUTE }, /* MUTE */
+ { 0xd00b, KEY_CHANNELUP }, /* CH + */
+ { 0xd10c, KEY_CHANNELDOWN },/* CH - */
+ { 0xec27, KEY_RECORD }, /* ( o) red */
+ { 0xea25, KEY_PLAY }, /* ( >) */
+ { 0xe924, KEY_REWIND }, /* (<<) */
+ { 0xeb26, KEY_FORWARD }, /* (>>) */
+ { 0xed28, KEY_STOP }, /* ([]) */
+ { 0xee29, KEY_PAUSE }, /* ('') */
+ { 0xf02b, KEY_PREVIOUS }, /* (<-) */
+ { 0xef2a, KEY_NEXT }, /* (>+) */
+ { 0xf22d, KEY_INFO }, /* PLAYING */
+ { 0xf32e, KEY_HOME }, /* TOP */
+ { 0xf42f, KEY_END }, /* END */
+ { 0xf530, KEY_SELECT }, /* SELECT */
+};
+
+static struct rc_map_list ati_x10_map = {
+ .map = {
+ .scan = ati_x10,
+ .size = ARRAY_SIZE(ati_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_ATI_X10,
+ }
+};
+
+static int __init init_rc_map_ati_x10(void)
+{
+ return rc_map_register(&ati_x10_map);
+}
+
+static void __exit exit_rc_map_ati_x10(void)
+{
+ rc_map_unregister(&ati_x10_map);
+}
+
+module_init(init_rc_map_ati_x10)
+module_exit(exit_rc_map_ati_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10.c b/drivers/media/rc/keymaps/rc-medion-x10.c
new file mode 100644
index 000000000000..09e2cc01d110
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10.c
@@ -0,0 +1,117 @@
+/*
+ * Medion X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on a keytable provided by
+ * Jan Losinski <losinski@wh2.tu-dresden.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/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10[] = {
+ { 0xf12c, KEY_TV }, /* TV */
+ { 0xf22d, KEY_VCR }, /* VCR */
+ { 0xc904, KEY_DVD }, /* DVD */
+ { 0xcb06, KEY_AUDIO }, /* MUSIC */
+
+ { 0xf32e, KEY_RADIO }, /* RADIO */
+ { 0xca05, KEY_DIRECTORY }, /* PHOTO */
+ { 0xf42f, KEY_INFO }, /* TV-PREVIEW */
+ { 0xf530, KEY_LIST }, /* CHANNEL-LST */
+
+ { 0xe01b, KEY_SETUP }, /* SETUP */
+ { 0xf631, KEY_VIDEO }, /* VIDEO DESKTOP */
+
+ { 0xcd08, KEY_VOLUMEDOWN }, /* VOL - */
+ { 0xce09, KEY_VOLUMEUP }, /* VOL + */
+ { 0xd00b, KEY_CHANNELUP }, /* CHAN + */
+ { 0xd10c, KEY_CHANNELDOWN }, /* CHAN - */
+ { 0xc500, KEY_MUTE }, /* MUTE */
+
+ { 0xf732, KEY_RED }, /* red */
+ { 0xf833, KEY_GREEN }, /* green */
+ { 0xf934, KEY_YELLOW }, /* yellow */
+ { 0xfa35, KEY_BLUE }, /* blue */
+ { 0xdb16, KEY_TEXT }, /* TXT */
+
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xe11c, KEY_SEARCH }, /* TV/RAD, CH SRC */
+ { 0xe520, KEY_DELETE }, /* DELETE */
+
+ { 0xfb36, KEY_KEYBOARD }, /* RENAME */
+ { 0xdd18, KEY_SCREEN }, /* SNAPSHOT */
+
+ { 0xdf1a, KEY_UP }, /* up */
+ { 0xe722, KEY_DOWN }, /* down */
+ { 0xe21d, KEY_LEFT }, /* left */
+ { 0xe41f, KEY_RIGHT }, /* right */
+ { 0xe31e, KEY_OK }, /* OK */
+
+ { 0xfc37, KEY_SELECT }, /* ACQUIRE IMAGE */
+ { 0xfd38, KEY_EDIT }, /* EDIT IMAGE */
+
+ { 0xe924, KEY_REWIND }, /* rewind (<<) */
+ { 0xea25, KEY_PLAY }, /* play ( >) */
+ { 0xeb26, KEY_FORWARD }, /* forward (>>) */
+ { 0xec27, KEY_RECORD }, /* record ( o) */
+ { 0xed28, KEY_STOP }, /* stop ([]) */
+ { 0xee29, KEY_PAUSE }, /* pause ('') */
+
+ { 0xe621, KEY_PREVIOUS }, /* prev */
+ { 0xfe39, KEY_SWITCHVIDEOMODE }, /* F SCR */
+ { 0xe823, KEY_NEXT }, /* next */
+ { 0xde19, KEY_MENU }, /* MENU */
+ { 0xff3a, KEY_LANGUAGE }, /* AUDIO */
+
+ { 0xc702, KEY_POWER }, /* POWER */
+};
+
+static struct rc_map_list medion_x10_map = {
+ .map = {
+ .scan = medion_x10,
+ .size = ARRAY_SIZE(medion_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10,
+ }
+};
+
+static int __init init_rc_map_medion_x10(void)
+{
+ return rc_map_register(&medion_x10_map);
+}
+
+static void __exit exit_rc_map_medion_x10(void)
+{
+ rc_map_unregister(&medion_x10_map);
+}
+
+module_init(init_rc_map_medion_x10)
+module_exit(exit_rc_map_medion_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
index 8d558ae63456..31fc64cd17ba 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
@@ -20,6 +20,7 @@ static struct rc_map_table pinnacle_pctv_hd[] = {
{ 0x0701, KEY_MENU }, /* Pinnacle logo */
{ 0x0739, KEY_POWER },
{ 0x0703, KEY_VOLUMEUP },
+ { 0x0705, KEY_OK },
{ 0x0709, KEY_VOLUMEDOWN },
{ 0x0706, KEY_CHANNELUP },
{ 0x070c, KEY_CHANNELDOWN },
diff --git a/drivers/media/rc/keymaps/rc-snapstream-firefly.c b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
new file mode 100644
index 000000000000..ef146520931c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
@@ -0,0 +1,107 @@
+/*
+ * SnapStream Firefly X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * 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/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table snapstream_firefly[] = {
+ { 0xf12c, KEY_ZOOM }, /* Maximize */
+ { 0xc702, KEY_CLOSE },
+
+ { 0xd20d, KEY_1 },
+ { 0xd30e, KEY_2 },
+ { 0xd40f, KEY_3 },
+ { 0xd510, KEY_4 },
+ { 0xd611, KEY_5 },
+ { 0xd712, KEY_6 },
+ { 0xd813, KEY_7 },
+ { 0xd914, KEY_8 },
+ { 0xda15, KEY_9 },
+ { 0xdc17, KEY_0 },
+ { 0xdb16, KEY_BACK },
+ { 0xdd18, KEY_KPENTER }, /* ent */
+
+ { 0xce09, KEY_VOLUMEUP },
+ { 0xcd08, KEY_VOLUMEDOWN },
+ { 0xcf0a, KEY_MUTE },
+ { 0xd00b, KEY_CHANNELUP },
+ { 0xd10c, KEY_CHANNELDOWN },
+ { 0xc500, KEY_VENDOR }, /* firefly */
+
+ { 0xf32e, KEY_INFO },
+ { 0xf42f, KEY_OPTION },
+
+ { 0xe21d, KEY_LEFT },
+ { 0xe41f, KEY_RIGHT },
+ { 0xe722, KEY_DOWN },
+ { 0xdf1a, KEY_UP },
+ { 0xe31e, KEY_OK },
+
+ { 0xe11c, KEY_MENU },
+ { 0xe520, KEY_EXIT },
+
+ { 0xec27, KEY_RECORD },
+ { 0xea25, KEY_PLAY },
+ { 0xed28, KEY_STOP },
+ { 0xe924, KEY_REWIND },
+ { 0xeb26, KEY_FORWARD },
+ { 0xee29, KEY_PAUSE },
+ { 0xf02b, KEY_PREVIOUS },
+ { 0xef2a, KEY_NEXT },
+
+ { 0xcb06, KEY_AUDIO }, /* Music */
+ { 0xca05, KEY_IMAGES }, /* Photos */
+ { 0xc904, KEY_DVD },
+ { 0xc803, KEY_TV },
+ { 0xcc07, KEY_VIDEO },
+
+ { 0xc601, KEY_HELP },
+ { 0xf22d, KEY_MODE }, /* Mouse */
+
+ { 0xde19, KEY_A },
+ { 0xe01b, KEY_B },
+ { 0xe621, KEY_C },
+ { 0xe823, KEY_D },
+};
+
+static struct rc_map_list snapstream_firefly_map = {
+ .map = {
+ .scan = snapstream_firefly,
+ .size = ARRAY_SIZE(snapstream_firefly),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_SNAPSTREAM_FIREFLY,
+ }
+};
+
+static int __init init_rc_map_snapstream_firefly(void)
+{
+ return rc_map_register(&snapstream_firefly_map);
+}
+
+static void __exit exit_rc_map_snapstream_firefly(void)
+{
+ rc_map_unregister(&snapstream_firefly_map);
+}
+
+module_init(init_rc_map_snapstream_firefly)
+module_exit(exit_rc_map_snapstream_firefly)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 85ff9a1ffb39..60d3c1e09712 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1,7 +1,7 @@
/*
* Driver for USB Windows Media Center Ed. eHome Infrared Transceivers
*
- * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ * Copyright (c) 2010-2011, Jarod Wilson <jarod@redhat.com>
*
* Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan
* Conti, Martin Blatter and Daniel Melander, the latter of which was
@@ -15,6 +15,11 @@
* Jon Smirl, which included enhancements and simplifications to the
* incoming IR buffer parsing routines.
*
+ * Updated in July of 2011 with the aid of Microsoft's official
+ * remote/transceiver requirements and specification document, found at
+ * download.microsoft.com, title
+ * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf
+ *
*
* 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
@@ -37,10 +42,11 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
+#include <linux/pm_wakeup.h>
#include <media/rc-core.h>
-#define DRIVER_VERSION "1.91"
-#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
+#define DRIVER_VERSION "1.92"
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
"device driver"
#define DRIVER_NAME "mceusb"
@@ -63,43 +69,90 @@
#define MCE_PULSE_MASK 0x7f /* Pulse mask */
#define MCE_MAX_PULSE_LENGTH 0x7f /* Longest transmittable pulse symbol */
-#define MCE_HW_CMD_HEADER 0xff /* MCE hardware command header */
-#define MCE_COMMAND_HEADER 0x9f /* MCE command header */
-#define MCE_COMMAND_MASK 0xe0 /* Mask out command bits */
-#define MCE_COMMAND_NULL 0x00 /* These show up various places... */
-/* if buf[i] & MCE_COMMAND_MASK == 0x80 and buf[i] != MCE_COMMAND_HEADER,
- * then we're looking at a raw IR data sample */
-#define MCE_COMMAND_IRDATA 0x80
-#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
-
-/* Sub-commands, which follow MCE_COMMAND_HEADER or MCE_HW_CMD_HEADER */
+/*
+ * The interface between the host and the IR hardware is command-response
+ * based. All commands and responses have a consistent format, where a lead
+ * byte always identifies the type of data following it. The lead byte has
+ * a port value in the 3 highest bits and a length value in the 5 lowest
+ * bits.
+ *
+ * The length field is overloaded, with a value of 11111 indicating that the
+ * following byte is a command or response code, and the length of the entire
+ * message is determined by the code. If the length field is not 11111, then
+ * it specifies the number of bytes of port data that follow.
+ */
+#define MCE_CMD 0x1f
+#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */
+#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */
+#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */
+#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
+
+/* Command port headers */
+#define MCE_CMD_PORT_IR 0x9f /* IR-related cmd/rsp */
+#define MCE_CMD_PORT_SYS 0xff /* System (non-IR) device cmd/rsp */
+
+/* Commands that set device state (2-4 bytes in length) */
+#define MCE_CMD_RESET 0xfe /* Reset device, 2 bytes */
+#define MCE_CMD_RESUME 0xaa /* Resume device after error, 2 bytes */
+#define MCE_CMD_SETIRCFS 0x06 /* Set tx carrier, 4 bytes */
+#define MCE_CMD_SETIRTIMEOUT 0x0c /* Set timeout, 4 bytes */
+#define MCE_CMD_SETIRTXPORTS 0x08 /* Set tx ports, 3 bytes */
+#define MCE_CMD_SETIRRXPORTEN 0x14 /* Set rx ports, 3 bytes */
+#define MCE_CMD_FLASHLED 0x23 /* Flash receiver LED, 2 bytes */
+
+/* Commands that query device state (all 2 bytes, unless noted) */
+#define MCE_CMD_GETIRCFS 0x07 /* Get carrier */
+#define MCE_CMD_GETIRTIMEOUT 0x0d /* Get timeout */
+#define MCE_CMD_GETIRTXPORTS 0x13 /* Get tx ports */
+#define MCE_CMD_GETIRRXPORTEN 0x15 /* Get rx ports */
+#define MCE_CMD_GETPORTSTATUS 0x11 /* Get tx port status, 3 bytes */
+#define MCE_CMD_GETIRNUMPORTS 0x16 /* Get number of ports */
+#define MCE_CMD_GETWAKESOURCE 0x17 /* Get wake source */
+#define MCE_CMD_GETEMVER 0x22 /* Get emulator interface version */
+#define MCE_CMD_GETDEVDETAILS 0x21 /* Get device details (em ver2 only) */
+#define MCE_CMD_GETWAKESUPPORT 0x20 /* Get wake details (em ver2 only) */
+#define MCE_CMD_GETWAKEVERSION 0x18 /* Get wake pattern (em ver2 only) */
+
+/* Misc commands */
+#define MCE_CMD_NOP 0xff /* No operation */
+
+/* Responses to commands (non-error cases) */
+#define MCE_RSP_EQIRCFS 0x06 /* tx carrier, 4 bytes */
+#define MCE_RSP_EQIRTIMEOUT 0x0c /* rx timeout, 4 bytes */
+#define MCE_RSP_GETWAKESOURCE 0x17 /* wake source, 3 bytes */
+#define MCE_RSP_EQIRTXPORTS 0x08 /* tx port mask, 3 bytes */
+#define MCE_RSP_EQIRRXPORTEN 0x14 /* rx port mask, 3 bytes */
+#define MCE_RSP_GETPORTSTATUS 0x11 /* tx port status, 7 bytes */
+#define MCE_RSP_EQIRRXCFCNT 0x15 /* rx carrier count, 4 bytes */
+#define MCE_RSP_EQIRNUMPORTS 0x16 /* number of ports, 4 bytes */
+#define MCE_RSP_EQWAKESUPPORT 0x20 /* wake capabilities, 3 bytes */
+#define MCE_RSP_EQWAKEVERSION 0x18 /* wake pattern details, 6 bytes */
+#define MCE_RSP_EQDEVDETAILS 0x21 /* device capabilities, 3 bytes */
+#define MCE_RSP_EQEMVER 0x22 /* emulator interface ver, 3 bytes */
+#define MCE_RSP_FLASHLED 0x23 /* success flashing LED, 2 bytes */
+
+/* Responses to error cases, must send MCE_CMD_RESUME to clear them */
+#define MCE_RSP_CMD_ILLEGAL 0xfe /* illegal command for port, 2 bytes */
+#define MCE_RSP_TX_TIMEOUT 0x81 /* tx timed out, 2 bytes */
+
+/* Misc commands/responses not defined in the MCE remote/transceiver spec */
#define MCE_CMD_SIG_END 0x01 /* End of signal */
#define MCE_CMD_PING 0x03 /* Ping device */
#define MCE_CMD_UNKNOWN 0x04 /* Unknown */
#define MCE_CMD_UNKNOWN2 0x05 /* Unknown */
-#define MCE_CMD_S_CARRIER 0x06 /* Set TX carrier frequency */
-#define MCE_CMD_G_CARRIER 0x07 /* Get TX carrier frequency */
-#define MCE_CMD_S_TXMASK 0x08 /* Set TX port bitmask */
#define MCE_CMD_UNKNOWN3 0x09 /* Unknown */
#define MCE_CMD_UNKNOWN4 0x0a /* Unknown */
#define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */
-#define MCE_CMD_S_TIMEOUT 0x0c /* Set RX timeout value */
-#define MCE_CMD_G_TIMEOUT 0x0d /* Get RX timeout value */
#define MCE_CMD_UNKNOWN5 0x0e /* Unknown */
#define MCE_CMD_UNKNOWN6 0x0f /* Unknown */
-#define MCE_CMD_G_RXPORTSTS 0x11 /* Get RX port status */
-#define MCE_CMD_G_TXMASK 0x13 /* Set TX port bitmask */
-#define MCE_CMD_S_RXSENSOR 0x14 /* Set RX sensor (std/learning) */
-#define MCE_CMD_G_RXSENSOR 0x15 /* Get RX sensor (std/learning) */
-#define MCE_RSP_PULSE_COUNT 0x15 /* RX pulse count (only if learning) */
-#define MCE_CMD_TX_PORTS 0x16 /* Get number of TX ports */
-#define MCE_CMD_G_WAKESRC 0x17 /* Get wake source */
-#define MCE_CMD_UNKNOWN7 0x18 /* Unknown */
#define MCE_CMD_UNKNOWN8 0x19 /* Unknown */
#define MCE_CMD_UNKNOWN9 0x1b /* Unknown */
-#define MCE_CMD_DEVICE_RESET 0xaa /* Reset the hardware */
-#define MCE_RSP_CMD_INVALID 0xfe /* Invalid command issued */
+#define MCE_CMD_NULL 0x00 /* These show up various places... */
+/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR,
+ * then we're looking at a raw IR data sample */
+#define MCE_COMMAND_IRDATA 0x80
+#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
/* module parameters */
#ifdef CONFIG_USB_DEBUG
@@ -388,48 +441,37 @@ struct mceusb_dev {
char name[128];
char phys[64];
enum mceusb_model_type model;
+
+ bool need_reset; /* flag to issue a device resume cmd */
+ u8 emver; /* emulator interface version */
+ u8 num_txports; /* number of transmit ports */
+ u8 num_rxports; /* number of receive sensors */
+ u8 txports_cabled; /* bitmask of transmitters with cable */
+ u8 rxports_active; /* bitmask of active receive sensors */
};
-/*
- * MCE Device Command Strings
- * Device command responses vary from device to device...
- * - DEVICE_RESET resets the hardware to its default state
- * - GET_REVISION fetches the hardware/software revision, common
- * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42
- * - GET_CARRIER_FREQ gets the carrier mode and frequency of the
- * device, with replies in the form of 9f 06 MM FF, where MM is 0-3,
- * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is
- * ((clk / frequency) - 1)
- * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us,
- * response in the form of 9f 0c msb lsb
- * - GET_TX_BITMASK fetches the transmitter bitmask, replies in
- * the form of 9f 08 bm, where bm is the bitmask
- * - GET_RX_SENSOR fetches the RX sensor setting -- long-range
- * general use one or short-range learning one, in the form of
- * 9f 14 ss, where ss is either 01 for long-range or 02 for short
- * - SET_CARRIER_FREQ sets a new carrier mode and frequency
- * - SET_TX_BITMASK sets the transmitter bitmask
- * - SET_RX_TIMEOUT sets the receiver timeout
- * - SET_RX_SENSOR sets which receiver sensor to use
- */
-static char DEVICE_RESET[] = {MCE_COMMAND_NULL, MCE_HW_CMD_HEADER,
- MCE_CMD_DEVICE_RESET};
-static char GET_REVISION[] = {MCE_HW_CMD_HEADER, MCE_CMD_G_REVISION};
-static char GET_UNKNOWN[] = {MCE_HW_CMD_HEADER, MCE_CMD_UNKNOWN7};
-static char GET_UNKNOWN2[] = {MCE_COMMAND_HEADER, MCE_CMD_UNKNOWN2};
-static char GET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER, MCE_CMD_G_CARRIER};
-static char GET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TIMEOUT};
-static char GET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TXMASK};
-static char GET_RX_SENSOR[] = {MCE_COMMAND_HEADER, MCE_CMD_G_RXSENSOR};
+/* MCE Device Command Strings, generally a port and command pair */
+static char DEVICE_RESUME[] = {MCE_CMD_NULL, MCE_CMD_PORT_SYS,
+ MCE_CMD_RESUME};
+static char GET_REVISION[] = {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION};
+static char GET_EMVER[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER};
+static char GET_WAKEVERSION[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION};
+static char FLASH_LED[] = {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED};
+static char GET_UNKNOWN2[] = {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2};
+static char GET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS};
+static char GET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT};
+static char GET_NUM_PORTS[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS};
+static char GET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS};
+static char GET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN};
/* sub in desired values in lower byte or bytes for full command */
/* FIXME: make use of these for transmit.
-static char SET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00};
-static char SET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_S_TXMASK, 0x00};
-static char SET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_TIMEOUT, 0x00, 0x00};
-static char SET_RX_SENSOR[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_RXSENSOR, 0x00};
+static char SET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00};
+static char SET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00};
+static char SET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRTIMEOUT, 0x00, 0x00};
+static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR,
+ MCE_RSP_EQIRRXPORTEN, 0x00};
*/
static int mceusb_cmdsize(u8 cmd, u8 subcmd)
@@ -437,27 +479,33 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd)
int datasize = 0;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if (subcmd == MCE_HW_CMD_HEADER)
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_PORT_SYS)
datasize = 1;
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_EQWAKEVERSION:
+ datasize = 4;
+ break;
case MCE_CMD_G_REVISION:
datasize = 2;
break;
+ case MCE_RSP_EQWAKESUPPORT:
+ datasize = 1;
+ break;
}
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_UNKNOWN:
- case MCE_CMD_S_CARRIER:
- case MCE_CMD_S_TIMEOUT:
- case MCE_RSP_PULSE_COUNT:
+ case MCE_RSP_EQIRCFS:
+ case MCE_RSP_EQIRTIMEOUT:
+ case MCE_RSP_EQIRRXCFCNT:
datasize = 2;
break;
case MCE_CMD_SIG_END:
- case MCE_CMD_S_TXMASK:
- case MCE_CMD_S_RXSENSOR:
+ case MCE_RSP_EQIRTXPORTS:
+ case MCE_RSP_EQIRRXPORTEN:
datasize = 1;
break;
}
@@ -470,9 +518,10 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
{
char codes[USB_BUFLEN * 3 + 1];
char inout[9];
- u8 cmd, subcmd, data1, data2;
+ u8 cmd, subcmd, data1, data2, data3, data4, data5;
struct device *dev = ir->dev;
int i, start, skip = 0;
+ u32 carrier, period;
if (!debug)
return;
@@ -500,18 +549,28 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
subcmd = buf[start + 1] & 0xff;
data1 = buf[start + 2] & 0xff;
data2 = buf[start + 3] & 0xff;
+ data3 = buf[start + 4] & 0xff;
+ data4 = buf[start + 5] & 0xff;
+ data5 = buf[start + 6] & 0xff;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if ((subcmd == MCE_HW_CMD_HEADER) &&
- (data1 == MCE_CMD_DEVICE_RESET))
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_NULL)
+ break;
+ if ((subcmd == MCE_CMD_PORT_SYS) &&
+ (data1 == MCE_CMD_RESUME))
+ dev_info(dev, "Device resume requested\n");
else
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_EQEMVER:
+ if (!out)
+ dev_info(dev, "Emulator interface version %x\n",
+ data1);
+ break;
case MCE_CMD_G_REVISION:
if (len == 2)
dev_info(dev, "Get hw/sw rev?\n");
@@ -520,21 +579,35 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
"0x%02x 0x%02x\n", data1, data2,
buf[start + 4], buf[start + 5]);
break;
- case MCE_CMD_DEVICE_RESET:
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_RESUME:
+ dev_info(dev, "Device resume requested\n");
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_info(dev, "Illegal PORT_SYS command\n");
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Previous command not supported\n");
+ case MCE_RSP_EQWAKEVERSION:
+ if (!out)
+ dev_info(dev, "Wake version, proto: 0x%02x, "
+ "payload: 0x%02x, address: 0x%02x, "
+ "version: 0x%02x\n",
+ data1, data2, data3, data4);
+ break;
+ case MCE_RSP_GETPORTSTATUS:
+ if (!out)
+ /* We use data1 + 1 here, to match hw labels */
+ dev_info(dev, "TX port %d: blaster is%s connected\n",
+ data1 + 1, data4 ? " not" : "");
+ break;
+ case MCE_CMD_FLASHLED:
+ dev_info(dev, "Attempting to flash LED\n");
break;
- case MCE_CMD_UNKNOWN7:
- case MCE_CMD_UNKNOWN9:
default:
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
break;
}
break;
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_SIG_END:
dev_info(dev, "End of signal\n");
@@ -546,47 +619,55 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
data1, data2);
break;
- case MCE_CMD_S_CARRIER:
- dev_info(dev, "%s carrier mode and freq of "
- "0x%02x 0x%02x\n", inout, data1, data2);
+ case MCE_RSP_EQIRCFS:
+ period = DIV_ROUND_CLOSEST(
+ (1 << data1 * 2) * (data2 + 1), 10);
+ if (!period)
+ break;
+ carrier = (1000 * 1000) / period;
+ dev_info(dev, "%s carrier of %u Hz (period %uus)\n",
+ inout, carrier, period);
break;
- case MCE_CMD_G_CARRIER:
+ case MCE_CMD_GETIRCFS:
dev_info(dev, "Get carrier mode and freq\n");
break;
- case MCE_CMD_S_TXMASK:
+ case MCE_RSP_EQIRTXPORTS:
dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
inout, data1);
break;
- case MCE_CMD_S_TIMEOUT:
+ case MCE_RSP_EQIRTIMEOUT:
/* value is in units of 50us, so x*50/1000 ms */
+ period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000;
dev_info(dev, "%s receive timeout of %d ms\n",
- inout,
- ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000);
+ inout, period);
break;
- case MCE_CMD_G_TIMEOUT:
+ case MCE_CMD_GETIRTIMEOUT:
dev_info(dev, "Get receive timeout\n");
break;
- case MCE_CMD_G_TXMASK:
+ case MCE_CMD_GETIRTXPORTS:
dev_info(dev, "Get transmit blaster mask\n");
break;
- case MCE_CMD_S_RXSENSOR:
+ case MCE_RSP_EQIRRXPORTEN:
dev_info(dev, "%s %s-range receive sensor in use\n",
inout, data1 == 0x02 ? "short" : "long");
break;
- case MCE_CMD_G_RXSENSOR:
- /* aka MCE_RSP_PULSE_COUNT */
+ case MCE_CMD_GETIRRXPORTEN:
+ /* aka MCE_RSP_EQIRRXCFCNT */
if (out)
dev_info(dev, "Get receive sensor\n");
else if (ir->learning_enabled)
dev_info(dev, "RX pulse count: %d\n",
((data1 << 8) | data2));
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Error! Hardware is likely wedged...\n");
+ case MCE_RSP_EQIRNUMPORTS:
+ if (out)
+ break;
+ dev_info(dev, "Num TX ports: %x, num RX ports: %x\n",
+ data1, data2);
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_info(dev, "Illegal PORT_IR command\n");
break;
- case MCE_CMD_UNKNOWN2:
- case MCE_CMD_UNKNOWN3:
- case MCE_CMD_UNKNOWN5:
default:
dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
cmd, subcmd);
@@ -599,8 +680,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
if (cmd == MCE_IRDATA_TRAILER)
dev_info(dev, "End of raw IR data\n");
- else if ((cmd != MCE_COMMAND_HEADER) &&
- ((cmd & MCE_COMMAND_MASK) == MCE_COMMAND_IRDATA))
+ else if ((cmd != MCE_CMD_PORT_IR) &&
+ ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA))
dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
}
@@ -616,9 +697,6 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
if (ir) {
len = urb->actual_length;
- mce_dbg(ir->dev, "callback called (status=%d len=%d)\n",
- urb->status, len);
-
mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
}
@@ -683,7 +761,16 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
{
+ int rsize = sizeof(DEVICE_RESUME);
+
+ if (ir->need_reset) {
+ ir->need_reset = false;
+ mce_request_packet(ir, DEVICE_RESUME, rsize, MCEUSB_TX);
+ msleep(10);
+ }
+
mce_request_packet(ir, data, size, MCEUSB_TX);
+ msleep(10);
}
static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size)
@@ -708,8 +795,8 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
return -ENOMEM;
/* MCE tx init header */
- cmdbuf[cmdcount++] = MCE_COMMAND_HEADER;
- cmdbuf[cmdcount++] = MCE_CMD_S_TXMASK;
+ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR;
+ cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS;
cmdbuf[cmdcount++] = ir->tx_mask;
/* Generate mce packet data */
@@ -795,8 +882,8 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
struct mceusb_dev *ir = dev->priv;
int clk = 10000000;
int prescaler = 0, divisor = 0;
- unsigned char cmdbuf[4] = { MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00 };
+ unsigned char cmdbuf[4] = { MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00 };
/* Carrier has changed */
if (ir->carrier != carrier) {
@@ -844,17 +931,34 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
u8 lo = ir->buf_in[index + 2] & 0xff;
switch (ir->buf_in[index]) {
+ /* the one and only 5-byte return value command */
+ case MCE_RSP_GETPORTSTATUS:
+ if ((ir->buf_in[index + 4] & 0xff) == 0x00)
+ ir->txports_cabled |= 1 << hi;
+ break;
+
/* 2-byte return value commands */
- case MCE_CMD_S_TIMEOUT:
+ case MCE_RSP_EQIRTIMEOUT:
ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT);
break;
+ case MCE_RSP_EQIRNUMPORTS:
+ ir->num_txports = hi;
+ ir->num_rxports = lo;
+ break;
/* 1-byte return value commands */
- case MCE_CMD_S_TXMASK:
+ case MCE_RSP_EQEMVER:
+ ir->emver = hi;
+ break;
+ case MCE_RSP_EQIRTXPORTS:
ir->tx_mask = hi;
break;
- case MCE_CMD_S_RXSENSOR:
- ir->learning_enabled = (hi == 0x02);
+ case MCE_RSP_EQIRRXPORTEN:
+ ir->learning_enabled = ((hi & 0x02) == 0x02);
+ ir->rxports_active = hi;
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ ir->need_reset = true;
break;
default:
break;
@@ -903,8 +1007,8 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
/* decode mce packets of the form (84),AA,BB,CC,DD */
/* IR data packets can span USB messages - rem */
ir->cmd = ir->buf_in[i];
- if ((ir->cmd == MCE_COMMAND_HEADER) ||
- ((ir->cmd & MCE_COMMAND_MASK) !=
+ if ((ir->cmd == MCE_CMD_PORT_IR) ||
+ ((ir->cmd & MCE_PORT_MASK) !=
MCE_COMMAND_IRDATA)) {
ir->parser_state = SUBCMD;
continue;
@@ -969,6 +1073,13 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
usb_submit_urb(urb, GFP_ATOMIC);
}
+static void mceusb_get_emulator_version(struct mceusb_dev *ir)
+{
+ /* If we get no reply or an illegal command reply, its ver 1, says MS */
+ ir->emver = 1;
+ mce_async_out(ir, GET_EMVER, sizeof(GET_EMVER));
+}
+
static void mceusb_gen1_init(struct mceusb_dev *ir)
{
int ret;
@@ -1011,8 +1122,8 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
0x0000, 0x0100, NULL, 0, HZ * 3);
mce_dbg(dev, "%s - retC = %d\n", __func__, ret);
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
/* get hw/sw revision? */
mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
@@ -1022,23 +1133,36 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
static void mceusb_gen2_init(struct mceusb_dev *ir)
{
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
/* get hw/sw revision? */
mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
- /* unknown what the next two actually return... */
- mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN));
+ /* get wake version (protocol, key, address) */
+ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION));
+
+ /* unknown what this one actually returns... */
mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2));
}
static void mceusb_get_parameters(struct mceusb_dev *ir)
{
+ int i;
+ unsigned char cmdbuf[3] = { MCE_CMD_PORT_SYS,
+ MCE_CMD_GETPORTSTATUS, 0x00 };
+
+ /* defaults, if the hardware doesn't support querying */
+ ir->num_txports = 2;
+ ir->num_rxports = 2;
+
+ /* get number of tx and rx ports */
+ mce_async_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS));
+
/* get the carrier and frequency */
mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ));
- if (!ir->flags.no_tx)
+ if (ir->num_txports && !ir->flags.no_tx)
/* get the transmitter bitmask */
mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
@@ -1047,6 +1171,19 @@ static void mceusb_get_parameters(struct mceusb_dev *ir)
/* get receiver sensor setting */
mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
+
+ for (i = 0; i < ir->num_txports; i++) {
+ cmdbuf[2] = i;
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ }
+}
+
+static void mceusb_flash_led(struct mceusb_dev *ir)
+{
+ if (ir->emver < 2)
+ return;
+
+ mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
}
static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
@@ -1220,6 +1357,9 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
mce_dbg(&intf->dev, "Flushing receive buffers\n");
mce_flush_rx_buffer(ir, maxp);
+ /* figure out which firmware/emulator version this hardware has */
+ mceusb_get_emulator_version(ir);
+
/* initialize device */
if (ir->flags.microsoft_gen1)
mceusb_gen1_init(ir);
@@ -1228,13 +1368,23 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
mceusb_get_parameters(ir);
+ mceusb_flash_led(ir);
+
if (!ir->flags.no_tx)
mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK);
usb_set_intfdata(intf, ir);
- dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name,
- dev->bus->busnum, dev->devnum);
+ /* enable wake via this device */
+ device_set_wakeup_capable(ir->dev, true);
+ device_set_wakeup_enable(ir->dev, true);
+
+ dev_info(&intf->dev, "Registered %s with mce emulator interface "
+ "version %x\n", name, ir->emver);
+ dev_info(&intf->dev, "%x tx ports (0x%x cabled) and "
+ "%x rx sensors (0x%x active)\n",
+ ir->num_txports, ir->txports_cabled,
+ ir->num_rxports, ir->rxports_active);
return 0;
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 04c2c722b6ec..c6ca870e8b7e 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -162,49 +162,49 @@ void ir_raw_init(void);
#ifdef CONFIG_IR_NEC_DECODER_MODULE
#define load_nec_decode() request_module("ir-nec-decoder")
#else
-#define load_nec_decode() 0
+static inline void load_nec_decode(void) { }
#endif
/* from ir-rc5-decoder.c */
#ifdef CONFIG_IR_RC5_DECODER_MODULE
#define load_rc5_decode() request_module("ir-rc5-decoder")
#else
-#define load_rc5_decode() 0
+static inline void load_rc5_decode(void) { }
#endif
/* from ir-rc6-decoder.c */
#ifdef CONFIG_IR_RC6_DECODER_MODULE
#define load_rc6_decode() request_module("ir-rc6-decoder")
#else
-#define load_rc6_decode() 0
+static inline void load_rc6_decode(void) { }
#endif
/* from ir-jvc-decoder.c */
#ifdef CONFIG_IR_JVC_DECODER_MODULE
#define load_jvc_decode() request_module("ir-jvc-decoder")
#else
-#define load_jvc_decode() 0
+static inline void load_jvc_decode(void) { }
#endif
/* from ir-sony-decoder.c */
#ifdef CONFIG_IR_SONY_DECODER_MODULE
#define load_sony_decode() request_module("ir-sony-decoder")
#else
-#define load_sony_decode() 0
+static inline void load_sony_decode(void) { }
#endif
/* from ir-mce_kbd-decoder.c */
#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder")
#else
-#define load_mce_kbd_decode() 0
+static inline void load_mce_kbd_decode(void) { }
#endif
/* from ir-lirc-codec.c */
#ifdef CONFIG_IR_LIRC_CODEC_MODULE
#define load_lirc_codec() request_module("ir-lirc-codec")
#else
-#define load_lirc_codec() 0
+static inline void load_lirc_codec(void) { }
#endif
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 51a23f48bc7d..666d4bb5b1fb 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -928,10 +928,6 @@ out:
static void rc_dev_release(struct device *device)
{
- struct rc_dev *dev = to_rc_dev(device);
-
- kfree(dev);
- module_put(THIS_MODULE);
}
#define ADD_HOTPLUG_VAR(fmt, val...) \
@@ -945,6 +941,9 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct rc_dev *dev = to_rc_dev(device);
+ if (!dev || !dev->input_dev)
+ return -ENODEV;
+
if (dev->rc_map.name)
ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name);
if (dev->driver_name)
@@ -1013,10 +1012,16 @@ EXPORT_SYMBOL_GPL(rc_allocate_device);
void rc_free_device(struct rc_dev *dev)
{
- if (dev) {
+ if (!dev)
+ return;
+
+ if (dev->input_dev)
input_free_device(dev->input_dev);
- put_device(&dev->dev);
- }
+
+ put_device(&dev->dev);
+
+ kfree(dev);
+ module_put(THIS_MODULE);
}
EXPORT_SYMBOL_GPL(rc_free_device);
@@ -1143,14 +1148,18 @@ void rc_unregister_device(struct rc_dev *dev)
if (dev->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(dev);
+ /* Freeing the table should also call the stop callback */
+ ir_free_table(&dev->rc_map);
+ IR_dprintk(1, "Freed keycode table\n");
+
input_unregister_device(dev->input_dev);
dev->input_dev = NULL;
- ir_free_table(&dev->rc_map);
- IR_dprintk(1, "Freed keycode table\n");
+ device_del(&dev->dev);
- device_unregister(&dev->dev);
+ rc_free_device(dev);
}
+
EXPORT_SYMBOL_GPL(rc_unregister_device);
/*
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index a16604477917..61287fcca61a 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -195,11 +195,6 @@ struct redrat3_dev {
dma_addr_t dma_in;
dma_addr_t dma_out;
- /* true if write urb is busy */
- bool write_busy;
- /* wait for the write to finish */
- struct completion write_finished;
-
/* locks this structure */
struct mutex lock;
@@ -207,8 +202,6 @@ struct redrat3_dev {
struct timer_list rx_timeout;
u32 hw_timeout;
- /* Is the device currently receiving? */
- bool recv_in_progress;
/* is the detector enabled*/
bool det_enabled;
/* Is the device currently transmitting?*/
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index bec8abc965f7..13f54b51194a 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -41,6 +41,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/interrupt.h>
@@ -1155,12 +1157,12 @@ wbcir_init(void)
case IR_PROTOCOL_RC6:
break;
default:
- printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n");
+ pr_err("Invalid power-on protocol\n");
}
ret = pnp_register_driver(&wbcir_driver);
if (ret)
- printk(KERN_ERR DRVNAME ": Unable to register driver\n");
+ pr_err("Unable to register driver\n");
return ret;
}
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index f574dc012cad..b303a3f8a9f8 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -467,6 +467,20 @@ config VIDEO_OV7670
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_MT9P031
+ tristate "Aptina MT9P031 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt9p031 5 Mpixel camera.
+
+config VIDEO_MT9T001
+ tristate "Aptina MT9T001 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt0t001 3 Mpixel camera.
+
config VIDEO_MT9V011
tristate "Micron mt9v011 sensor support"
depends on I2C && VIDEO_V4L2
@@ -489,6 +503,27 @@ config VIDEO_TCM825X
This is a driver for the Toshiba TCM825x VGA camera sensor.
It is used for example in Nokia N800.
+config VIDEO_SR030PC30
+ tristate "Siliconfile SR030PC30 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---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
+ ---help---
+ This driver supports NOON010PC30 CIF camera from Siliconfile
+
+source "drivers/media/video/m5mols/Kconfig"
+
+config VIDEO_S5K6AA
+ tristate "Samsung S5K6AAFX sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
+ camera sensor with an embedded SoC image signal processor.
+
comment "Flash devices"
config VIDEO_ADP1653
@@ -708,6 +743,8 @@ source "drivers/media/video/cx88/Kconfig"
source "drivers/media/video/cx23885/Kconfig"
+source "drivers/media/video/cx25821/Kconfig"
+
source "drivers/media/video/au0828/Kconfig"
source "drivers/media/video/ivtv/Kconfig"
@@ -737,12 +774,6 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
-config VIDEO_SR030PC30
- tristate "SR030PC30 VGA camera sensor support"
- depends on I2C && VIDEO_V4L2
- ---help---
- This driver supports SR030PC30 VGA camera from Siliconfile
-
config VIDEO_VIA_CAMERA
tristate "VIAFB camera controller support"
depends on FB_VIA
@@ -753,18 +784,9 @@ config VIDEO_VIA_CAMERA
Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
with ov7670 sensors.
-config VIDEO_NOON010PC30
- tristate "NOON010PC30 CIF camera sensor support"
- depends on I2C && VIDEO_V4L2
- ---help---
- This driver supports NOON010PC30 CIF camera from Siliconfile
-
-source "drivers/media/video/m5mols/Kconfig"
-
config VIDEO_OMAP3
tristate "OMAP 3 Camera support (EXPERIMENTAL)"
- select OMAP_IOMMU
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
+ depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
---help---
Driver for an OMAP 3 camera controller.
@@ -949,8 +971,9 @@ config VIDEO_MX2
Interface
config VIDEO_SAMSUNG_S5P_FIMC
- tristate "Samsung S5P and EXYNOS4 camera host interface driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
+ depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
+ VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -1004,6 +1027,8 @@ 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/et61x251/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 272390072aef..117f9c4b4cb9 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -65,11 +65,14 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
+obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
+obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
@@ -102,9 +105,11 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
+obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_MXB) += mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
@@ -190,6 +195,6 @@ obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
index be7befd60947..5914390211ff 100644
--- a/drivers/media/video/adp1653.c
+++ b/drivers/media/video/adp1653.c
@@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <media/adp1653.h>
@@ -258,7 +259,7 @@ static int adp1653_init_controls(struct adp1653_flash *flash)
if (flash->ctrls.error)
return flash->ctrls.error;
- fault->is_volatile = 1;
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
flash->subdev.ctrl_handler = &flash->ctrls;
return 0;
@@ -413,6 +414,10 @@ static int adp1653_probe(struct i2c_client *client,
struct adp1653_flash *flash;
int ret;
+ /* we couldn't work without platform data */
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
flash = kzalloc(sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
@@ -425,12 +430,21 @@ static int adp1653_probe(struct i2c_client *client,
flash->subdev.internal_ops = &adp1653_internal_ops;
flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- adp1653_init_controls(flash);
+ ret = adp1653_init_controls(flash);
+ if (ret)
+ goto free_and_quit;
ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
if (ret < 0)
- kfree(flash);
+ goto free_and_quit;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ return 0;
+free_and_quit:
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
return ret;
}
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index d2327dbb473f..206078eca853 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -61,6 +61,11 @@ static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd)
static char *inputs[] = { "pass_through", "play_back", "color_bar" };
+static enum v4l2_mbus_pixelcode adv7175_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_UYVY8_1X16,
+};
+
/* ----------------------------------------------------------------------- */
static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -296,6 +301,60 @@ static int adv7175_s_routing(struct v4l2_subdev *sd,
return 0;
}
+static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(adv7175_codes))
+ return -EINVAL;
+
+ *code = adv7175_codes[index];
+ return 0;
+}
+
+static int adv7175_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+
+ if ((val & 0x40) == (1 << 6))
+ mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ else
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ mf->width = 0;
+ mf->height = 0;
+ mf->field = V4L2_FIELD_ANY;
+
+ return 0;
+}
+
+static int adv7175_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+ int ret;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ val &= ~0x40;
+ break;
+
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ val |= 0x40;
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
+ return -EINVAL;
+ }
+
+ ret = adv7175_write(sd, 0x7, val);
+
+ return ret;
+}
+
static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -324,6 +383,9 @@ static const struct v4l2_subdev_core_ops adv7175_core_ops = {
static const struct v4l2_subdev_video_ops adv7175_video_ops = {
.s_std_output = adv7175_s_std_output,
.s_routing = adv7175_s_routing,
+ .s_mbus_fmt = adv7175_s_fmt,
+ .g_mbus_fmt = adv7175_g_fmt,
+ .enum_mbus_fmt = adv7175_enum_fmt,
};
static const struct v4l2_subdev_ops adv7175_ops = {
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
index 7b89f00501b8..8c775c59e120 100644
--- a/drivers/media/video/atmel-isi.c
+++ b/drivers/media/video/atmel-isi.c
@@ -94,6 +94,7 @@ struct atmel_isi {
unsigned int irq;
struct isi_platform_data *pdata;
+ u16 width_flags; /* max 12 bits */
struct list_head video_buffer_list;
struct frame_buffer *active;
@@ -248,9 +249,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -341,7 +342,7 @@ static int buffer_prepare(struct vb2_buffer *vb)
/* Initialize the dma descriptor */
desc->p_fbd->fb_address =
- vb2_dma_contig_plane_paddr(vb, 0);
+ vb2_dma_contig_plane_dma_addr(vb, 0);
desc->p_fbd->next_fbd_address = 0;
set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB);
@@ -404,12 +405,13 @@ static void buffer_queue(struct vb2_buffer *vb)
if (isi->active == NULL) {
isi->active = buf;
- start_dma(isi, buf);
+ if (vb2_is_streaming(vb->vb2_queue))
+ start_dma(isi, buf);
}
spin_unlock_irqrestore(&isi->lock, flags);
}
-static int start_streaming(struct vb2_queue *vq)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -431,17 +433,26 @@ static int start_streaming(struct vb2_queue *vq)
ret = wait_event_interruptible(isi->vsync_wq,
isi->state != ISI_STATE_IDLE);
if (ret)
- return ret;
+ goto err;
- if (isi->state != ISI_STATE_READY)
- return -EIO;
+ if (isi->state != ISI_STATE_READY) {
+ ret = -EIO;
+ goto err;
+ }
spin_lock_irq(&isi->lock);
isi->state = ISI_STATE_WAIT_SOF;
isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC);
+ if (count)
+ start_dma(isi, isi->active);
spin_unlock_irq(&isi->lock);
return 0;
+err:
+ isi->active = NULL;
+ isi->sequence = 0;
+ INIT_LIST_HEAD(&isi->video_buffer_list);
+ return ret;
}
/* abort streaming and wait for last buffer */
@@ -637,50 +648,42 @@ static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
}
-static unsigned long make_bus_param(struct atmel_isi *isi)
-{
- unsigned long flags;
- /*
- * Platform specified synchronization and pixel clock polarities are
- * only a recommendation and are only used during probing. Atmel ISI
- * camera interface only works in master mode, i.e., uses HSYNC and
- * VSYNC signals from the sensor
- */
- flags = SOCAM_MASTER |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (isi->pdata->data_width_flags & ISI_DATAWIDTH_10)
- flags |= SOCAM_DATAWIDTH_10;
-
- if (isi->pdata->data_width_flags & ISI_DATAWIDTH_8)
- flags |= SOCAM_DATAWIDTH_8;
-
- if (flags & SOCAM_DATAWIDTH_MASK)
- return flags;
-
- return 0;
-}
+#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
static int isi_camera_try_bus_param(struct soc_camera_device *icd,
unsigned char buswidth)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
- unsigned long camera_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
int ret;
- camera_flags = icd->ops->query_bus_param(icd);
- ret = soc_camera_bus_param_compatible(camera_flags,
- make_bus_param(isi));
- if (!ret)
- return -EINVAL;
- return 0;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ if ((1 << (buswidth - 1)) & isi->width_flags)
+ return 0;
+ return -EINVAL;
}
@@ -802,59 +805,71 @@ static int isi_camera_querycap(struct soc_camera_host *ici,
static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
- unsigned long bus_flags, camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
int ret;
u32 cfg1 = 0;
- camera_flags = icd->ops->query_bus_param(icd);
-
- bus_flags = make_bus_param(isi);
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
- camera_flags, bus_flags, common_flags);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = ISI_BUS_PARAM;
+ }
+ dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n",
+ cfg.flags, ISI_BUS_PARAM, common_flags);
/* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (isi->pdata->hsync_act_low)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (isi->pdata->vsync_act_low)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (isi->pdata->pclk_act_falling)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0) {
- dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n",
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
common_flags, ret);
return ret;
}
/* set bus param for ISI */
- if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
- if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
- if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
if (isi->pdata->has_emb_sync)
@@ -973,6 +988,11 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev)
goto err_ioremap;
}
+ if (pdata->data_width_flags & ISI_DATAWIDTH_8)
+ isi->width_flags = 1 << 7;
+ if (pdata->data_width_flags & ISI_DATAWIDTH_10)
+ isi->width_flags |= 1 << 9;
+
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
index 5c7f2f7d9805..bd22223f8d9f 100644
--- a/drivers/media/video/au0828/Makefile
+++ b/drivers/media/video/au0828/Makefile
@@ -2,8 +2,8 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-vid
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index f87204461cb4..859eabf57978 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -229,7 +229,7 @@ static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
if (pstd)
*pstd = std;
if (pstatus)
- *pstatus = status;
+ *pstatus = res;
v4l2_dbg(1, debug, sd, "get status %x\n", status);
return 0;
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
index e415f6fc447c..3f9a2b22d3d4 100644
--- a/drivers/media/video/bt8xx/Makefile
+++ b/drivers/media/video/bt8xx/Makefile
@@ -8,6 +8,6 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
obj-$(CONFIG_VIDEO_BT848) += bttv.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 5b15f63bf065..5939021d8eba 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -25,6 +25,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kmod.h>
@@ -2905,19 +2907,17 @@ void __devinit bttv_idcard(struct bttv *btv)
if (type != -1) {
/* found it */
- printk(KERN_INFO "bttv%d: detected: %s [card=%d], "
- "PCI subsystem ID is %04x:%04x\n",
- btv->c.nr,cards[type].name,cards[type].cardnr,
- btv->cardid & 0xffff,
- (btv->cardid >> 16) & 0xffff);
+ pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n",
+ btv->c.nr, cards[type].name, cards[type].cardnr,
+ btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
btv->c.type = cards[type].cardnr;
} else {
/* 404 */
- printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n",
- btv->c.nr, btv->cardid & 0xffff,
- (btv->cardid >> 16) & 0xffff);
- printk(KERN_DEBUG "please mail id, board name and "
- "the correct card= insmod option to linux-media@vger.kernel.org\n");
+ pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n",
+ btv->c.nr, btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n");
}
}
@@ -2926,10 +2926,10 @@ void __devinit bttv_idcard(struct bttv *btv)
btv->c.type=card[btv->c.nr];
/* print which card config we are using */
- printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->c.nr,
- bttv_tvcards[btv->c.type].name, btv->c.type,
- card[btv->c.nr] < bttv_num_tvcards
- ? "insmod option" : "autodetected");
+ pr_info("%d: using: %s [card=%d,%s]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type,
+ card[btv->c.nr] < bttv_num_tvcards
+ ? "insmod option" : "autodetected");
/* overwrite gpio stuff ?? */
if (UNSET == audioall && UNSET == audiomux[0])
@@ -2948,12 +2948,13 @@ void __devinit bttv_idcard(struct bttv *btv)
}
}
bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
- printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
- btv->c.nr,bttv_tvcards[btv->c.type].gpiomask);
+ pr_info("%d: gpio config override: mask=0x%x, mux=",
+ btv->c.nr, bttv_tvcards[btv->c.type].gpiomask);
for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
- printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
+ pr_cont("%s0x%x",
+ i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
}
- printk("\n");
+ pr_cont("\n");
}
/*
@@ -2974,8 +2975,8 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256])
if (-1 != type) {
btv->c.type = type;
- printk("bttv%d: detected by eeprom: %s [card=%d]\n",
- btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
+ pr_info("%d: detected by eeprom: %s [card=%d]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
}
}
@@ -3019,7 +3020,7 @@ static void flyvideo_gpio(struct bttv *btv)
tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */
break;
default:
- printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr);
+ pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr);
break;
}
@@ -3036,13 +3037,13 @@ static void flyvideo_gpio(struct bttv *btv)
if (is_capture_only)
tuner_type = TUNER_ABSENT; /* No tuner present */
- printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
- btv->c.nr, has_radio ? "yes" : "no ",
- has_remote ? "yes" : "no ", tuner_type, gpio);
- printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
- btv->c.nr, is_lr90 ? "yes" : "no ",
- has_tda9820_tda9821 ? "yes" : "no ",
- is_capture_only ? "yes" : "no ");
+ pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
+ btv->c.nr, has_radio ? "yes" : "no",
+ has_remote ? "yes" : "no", tuner_type, gpio);
+ pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
+ btv->c.nr, is_lr90 ? "yes" : "no",
+ has_tda9820_tda9821 ? "yes" : "no",
+ is_capture_only ? "yes" : "no");
if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */
btv->tuner_type = tuner_type;
@@ -3091,12 +3092,11 @@ static void miro_pinnacle_gpio(struct bttv *btv)
if (btv->c.type == BTTV_BOARD_PINNACLE)
btv->c.type = BTTV_BOARD_PINNACLEPRO;
}
- printk(KERN_INFO
- "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
- btv->c.nr, id+1, btv->tuner_type,
- !btv->has_radio ? "no" :
- (btv->has_matchbox ? "matchbox" : "fmtuner"),
- (-1 == msp) ? "no" : "yes");
+ pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
+ btv->c.nr, id+1, btv->tuner_type,
+ !btv->has_radio ? "no" :
+ (btv->has_matchbox ? "matchbox" : "fmtuner"),
+ (-1 == msp) ? "no" : "yes");
} else {
/* new cards with microtune tuner */
id = 63 - id;
@@ -3138,9 +3138,8 @@ static void miro_pinnacle_gpio(struct bttv *btv)
}
if (-1 != msp)
btv->c.type = BTTV_BOARD_PINNACLEPRO;
- printk(KERN_INFO
- "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
- btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
+ pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
+ btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
btv->tuner_type = TUNER_MT2032;
}
}
@@ -3202,7 +3201,7 @@ static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
static void init_lmlbt4x(struct bttv *btv)
{
- printk(KERN_DEBUG "LMLBT4x init\n");
+ pr_debug("LMLBT4x init\n");
btwrite(0x000000, BT848_GPIO_REG_INP);
gpio_inout(0xffffff, 0x0006C0);
gpio_write(0x000000);
@@ -3246,7 +3245,7 @@ static void bttv_reset_audio(struct bttv *btv)
return;
if (bttv_debug)
- printk("bttv%d: BT878A ARESET\n",btv->c.nr);
+ pr_debug("%d: BT878A ARESET\n", btv->c.nr);
btwrite((1<<7), 0x058);
udelay(10);
btwrite( 0, 0x058);
@@ -3349,7 +3348,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
case BTTV_BOARD_MAGICTVIEW061:
if (btv->cardid == 0x3002144f) {
btv->has_radio=1;
- printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->c.nr);
+ pr_info("%d: radio detected by subsystem id (CPH05x)\n",
+ btv->c.nr);
}
break;
case BTTV_BOARD_STB2:
@@ -3438,17 +3438,16 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->tuner_type = tuner[btv->c.nr];
if (btv->tuner_type == TUNER_ABSENT)
- printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr);
- else if(btv->tuner_type == UNSET)
- printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr);
+ pr_info("%d: tuner absent\n", btv->c.nr);
+ else if (btv->tuner_type == UNSET)
+ pr_warn("%d: tuner type unset\n", btv->c.nr);
else
- printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr,
- btv->tuner_type);
+ pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type);
if (autoload != UNSET) {
- printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr);
- printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr);
- printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr);
+ pr_warn("%d: the autoload option is obsolete\n", btv->c.nr);
+ pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n",
+ btv->c.nr);
}
if (UNSET == btv->tuner_type)
@@ -3541,8 +3540,7 @@ void __devinit bttv_init_card2(struct bttv *btv)
}
default:
- printk(KERN_WARNING "bttv%d: unknown audiodev value!\n",
- btv->c.nr);
+ pr_warn("%d: unknown audiodev value!\n", btv->c.nr);
return;
}
@@ -3585,8 +3583,7 @@ void __devinit bttv_init_card2(struct bttv *btv)
return;
no_audio:
- printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n",
- btv->c.nr);
+ pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr);
}
@@ -3639,19 +3636,19 @@ static void modtec_eeprom(struct bttv *btv)
{
if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) {
btv->tuner_type=TUNER_PHILIPS_NTSC;
- printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
} else {
- printk("bttv%d: Modtec: Unknown TunerString: %s\n",
- btv->c.nr,&eeprom_data[0x1e]);
+ pr_info("%d: Modtec: Unknown TunerString: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
}
}
@@ -3663,7 +3660,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
btv->tuner_type = tv.tuner_type;
btv->has_radio = tv.has_radio;
- printk("bttv%d: Hauppauge eeprom indicates model#%d\n",
+ pr_info("%d: Hauppauge eeprom indicates model#%d\n",
btv->c.nr, tv.model);
/*
@@ -3671,7 +3668,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
* type based on model #.
*/
if(tv.model == 64900) {
- printk("bttv%d: Switching board type from %s to %s\n",
+ pr_info("%d: Switching board type from %s to %s\n",
btv->c.nr,
bttv_tvcards[btv->c.type].name,
bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name);
@@ -3698,8 +3695,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv)
freq=88000/62.5;
tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
if (0x1ed8 == tea5757_read(btv)) {
- printk("bttv%d: Terratec Active Radio Upgrade found.\n",
- btv->c.nr);
+ pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
btv->has_radio = 1;
btv->has_saa6588 = 1;
btv->has_matchbox = 1;
@@ -3771,13 +3767,12 @@ static int __devinit pvr_boot(struct bttv *btv)
rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
if (rc != 0) {
- printk(KERN_WARNING "bttv%d: no altera firmware [via hotplug]\n",
- btv->c.nr);
+ pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr);
return rc;
}
rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
- printk(KERN_INFO "bttv%d: altera firmware upload %s\n",
- btv->c.nr, (rc < 0) ? "failed" : "ok");
+ pr_info("%d: altera firmware upload %s\n",
+ btv->c.nr, (rc < 0) ? "failed" : "ok");
release_firmware(fw_entry);
return rc;
}
@@ -3873,29 +3868,27 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256])
break;
default:
/* unknown...leave generic, but get serial # */
- printk(KERN_INFO "bttv%d: "
- "osprey eeprom: unknown card type 0x%04x\n",
- btv->c.nr, type);
+ pr_info("%d: osprey eeprom: unknown card type 0x%04x\n",
+ btv->c.nr, type);
break;
}
serial = get_unaligned_be32((__be32 *)(ee+6));
}
- printk(KERN_INFO "bttv%d: osprey eeprom: card=%d '%s' serial=%u\n",
- btv->c.nr, cardid,
- cardid>0 ? bttv_tvcards[cardid].name : "Unknown", serial);
+ pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n",
+ btv->c.nr, cardid,
+ cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial);
if (cardid<0 || btv->c.type == cardid)
return;
/* card type isn't set correctly */
if (card[btv->c.nr] < bttv_num_tvcards) {
- printk(KERN_WARNING "bttv%d: osprey eeprom: "
- "Not overriding user specified card type\n", btv->c.nr);
+ pr_warn("%d: osprey eeprom: Not overriding user specified card type\n",
+ btv->c.nr);
} else {
- printk(KERN_INFO "bttv%d: osprey eeprom: "
- "Changing card type from %d to %d\n", btv->c.nr,
- btv->c.type, cardid);
+ pr_info("%d: osprey eeprom: Changing card type from %d to %d\n",
+ btv->c.nr, btv->c.type, cardid);
btv->c.type = cardid;
}
}
@@ -3938,14 +3931,14 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
if (tuner_format == 0x09)
tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */
- printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=",
+ pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=",
btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]);
if (tuner_type) {
btv->tuner_type = tuner_type;
- printk(KERN_CONT "%d", tuner_type);
+ pr_cont("%d", tuner_type);
} else
- printk(KERN_CONT "Unknown type");
- printk(KERN_CONT " radio:%s remote control:%s\n",
+ pr_cont("Unknown type");
+ pr_cont(" radio:%s remote control:%s\n",
tuner_tv_fm ? "yes" : "no",
btv->has_remote ? "yes" : "no");
}
@@ -3993,8 +3986,8 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin)
if (bttv_gpio)
bttv_gpio_tracking(btv,"msp34xx");
if (bttv_verbose)
- printk(KERN_INFO "bttv%d: Hauppauge/Voodoo msp34xx: reset line "
- "init [%d]\n", btv->c.nr, pin);
+ pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n",
+ btv->c.nr, pin);
}
/* ----------------------------------------------------------------------- */
@@ -4034,7 +4027,7 @@ static void __devinit init_PXC200(struct bttv *btv)
btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC);
/* Initialise MAX517 DAC */
- printk(KERN_INFO "Setting DAC reference voltage level ...\n");
+ pr_info("Setting DAC reference voltage level ...\n");
bttv_I2CWrite(btv,0x5E,0,0x80,1);
/* Initialise 12C508 PIC */
@@ -4043,7 +4036,7 @@ static void __devinit init_PXC200(struct bttv *btv)
* argument so the numbers are different */
- printk(KERN_INFO "Initialising 12C508 PIC chip ...\n");
+ pr_info("Initialising 12C508 PIC chip ...\n");
/* First of all, enable the clock line. This is used in the PXC200-F */
val = btread(BT848_GPIO_DMA_CTL);
@@ -4062,13 +4055,12 @@ static void __devinit init_PXC200(struct bttv *btv)
for (i = 0; i < ARRAY_SIZE(vals); i++) {
tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
if (tmp != -1) {
- printk(KERN_INFO
- "I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
+ pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL));
}
}
- printk(KERN_INFO "PXC200 Initialised.\n");
+ pr_info("PXC200 Initialised\n");
}
@@ -4107,8 +4099,7 @@ init_RTV24 (struct bttv *btv)
uint32_t dataRead = 0;
long watchdog_value = 0x0E;
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation in progress ...\n",
+ pr_info("%d: Adlink RTV-24 initialisation in progress ...\n",
btv->c.nr);
btwrite (0x00c3feff, BT848_GPIO_OUT_EN);
@@ -4122,8 +4113,7 @@ init_RTV24 (struct bttv *btv)
dataRead = btread (BT848_GPIO_DATA);
if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) {
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
+ pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
btv->c.nr, dataRead);
}
@@ -4136,15 +4126,13 @@ init_RTV24 (struct bttv *btv)
dataRead = btread (BT848_GPIO_DATA);
if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) {
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
+ pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
btv->c.nr, dataRead);
return;
}
- printk (KERN_INFO
- "bttv%d: Adlink RTV-24 initialisation complete.\n", btv->c.nr);
+ pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr);
}
@@ -4261,22 +4249,25 @@ static int tea5757_read(struct bttv *btv)
while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
schedule();
if (bus_in(btv,btv->mbox_data)) {
- printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->c.nr);
+ pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
return -1;
}
- dprintk("bttv%d: tea5757:",btv->c.nr);
+ dprintk("%d: tea5757:", btv->c.nr);
for (i = 0; i < 24; i++) {
udelay(5);
bus_high(btv,btv->mbox_clk);
udelay(5);
- dprintk("%c",(bus_in(btv,btv->mbox_most) == 0)?'T':'-');
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
bus_low(btv,btv->mbox_clk);
value <<= 1;
value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */
- dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M');
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
}
- dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->c.nr, value);
+ dprintk_cont("\n");
+ dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
return value;
}
@@ -4295,7 +4286,7 @@ static int tea5757_write(struct bttv *btv, int value)
if (bttv_gpio)
bttv_gpio_tracking(btv,"tea5757 write");
- dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value);
+ dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
bus_low(btv,btv->mbox_clk);
bus_high(btv,btv->mbox_we);
for (i = 0; i < 25; i++) {
@@ -4547,7 +4538,7 @@ static void picolo_tetra_init(struct bttv *btv)
static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
{
- dprintk (KERN_DEBUG "bttv%d : picolo_tetra_muxsel => input = %d\n",btv->c.nr,input);
+ dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input);
/*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/
/*GPIO[20]&GPIO[21] used to choose the right input*/
btwrite (input<<20,BT848_GPIO_DATA);
@@ -4592,7 +4583,7 @@ static void ivc120_muxsel(struct bttv *btv, unsigned int input)
int key = input % 4;
int matrix = input / 4;
- dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+ dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
btv->c.nr, input, matrix, key);
/* Handles the input selection on the TDA8540's */
@@ -4649,15 +4640,17 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input)
buf[1]=0;
rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
if (rc) {
- printk(KERN_DEBUG "bttv%d: PXC200_muxsel: pic cfg write failed:%d\n", btv->c.nr,rc);
+ pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n",
+ btv->c.nr, rc);
/* not PXC ? do nothing */
- return;
+ return;
}
rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL);
if (!(rc & PX_CFG_PXC200F)) {
- printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc);
- return;
+ pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n",
+ btv->c.nr, rc);
+ return;
}
@@ -4696,7 +4689,7 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input)
else /* older device */
btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
- printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux);
+ pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux);
}
static void phytec_muxsel(struct bttv *btv, unsigned int input)
@@ -4847,29 +4840,27 @@ void __init bttv_check_chipset(void)
/* print warnings about any quirks found */
if (triton1)
- printk(KERN_INFO "bttv: Host bridge needs ETBF enabled.\n");
+ pr_info("Host bridge needs ETBF enabled\n");
if (vsfx)
- printk(KERN_INFO "bttv: Host bridge needs VSFX enabled.\n");
+ pr_info("Host bridge needs VSFX enabled\n");
if (pcipci_fail) {
- printk(KERN_INFO "bttv: bttv and your chipset may not work "
- "together.\n");
+ pr_info("bttv and your chipset may not work together\n");
if (!no_overlay) {
- printk(KERN_INFO "bttv: overlay will be disabled.\n");
+ pr_info("overlay will be disabled\n");
no_overlay = 1;
} else {
- printk(KERN_INFO "bttv: overlay forced. Use this "
- "option at your own risk.\n");
+ pr_info("overlay forced. Use this option at your own risk.\n");
}
}
if (UNSET != latency)
- printk(KERN_INFO "bttv: pci latency fixup [%d]\n",latency);
+ pr_info("pci latency fixup [%d]\n", latency);
while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82441, dev))) {
unsigned char b;
pci_read_config_byte(dev, 0x53, &b);
if (bttv_debug)
- printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "
- "bufcon=0x%02x\n",b);
+ pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n",
+ b);
}
}
@@ -4882,12 +4873,13 @@ int __devinit bttv_handle_chipset(struct bttv *btv)
if (bttv_verbose) {
if (triton1)
- printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->c.nr);
+ pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n",
+ btv->c.nr);
if (vsfx && btv->id >= 878)
- printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->c.nr);
+ pr_info("%d: enabling VSFX\n", btv->c.nr);
if (UNSET != latency)
- printk(KERN_INFO "bttv%d: setting pci timer to %d\n",
- btv->c.nr,latency);
+ pr_info("%d: setting pci timer to %d\n",
+ btv->c.nr, latency);
}
if (btv->id < 878) {
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 14444de67d5e..3dd06607aec2 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -34,6 +34,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -942,8 +944,8 @@ static
void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
{
if ((fh->resources & bits) != bits) {
- /* trying to free ressources not allocated by us ... */
- printk("bttv: BUG! (btres)\n");
+ /* trying to free resources not allocated by us ... */
+ pr_err("BUG! (btres)\n");
}
fh->resources &= ~bits;
btv->resources &= ~bits;
@@ -1000,7 +1002,7 @@ static void set_pll(struct bttv *btv)
return;
if (btv->pll.pll_ofreq == btv->pll.pll_current) {
- dprintk("bttv%d: PLL: no change required\n",btv->c.nr);
+ dprintk("%d: PLL: no change required\n", btv->c.nr);
return;
}
@@ -1008,21 +1010,23 @@ static void set_pll(struct bttv *btv)
/* no PLL needed */
if (btv->pll.pll_current == 0)
return;
- bttv_printk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n",
- btv->c.nr,btv->pll.pll_ifreq);
+ if (bttv_verbose)
+ pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+ btv->c.nr, btv->pll.pll_ifreq);
btwrite(0x00,BT848_TGCTRL);
btwrite(0x00,BT848_PLL_XCI);
btv->pll.pll_current = 0;
return;
}
- bttv_printk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr,
- btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ if (bttv_verbose)
+ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+ btv->c.nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
for (i=0; i<10; i++) {
/* Let other people run while the PLL stabilizes */
- bttv_printk(".");
msleep(10);
if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
@@ -1030,12 +1034,14 @@ static void set_pll(struct bttv *btv)
} else {
btwrite(0x08,BT848_TGCTRL);
btv->pll.pll_current = btv->pll.pll_ofreq;
- bttv_printk(" ok\n");
+ if (bttv_verbose)
+ pr_info("PLL set ok\n");
return;
}
}
btv->pll.pll_current = -1;
- bttv_printk("failed\n");
+ if (bttv_verbose)
+ pr_info("Setting PLL failed\n");
return;
}
@@ -1047,7 +1053,7 @@ static void bt848A_set_timing(struct bttv *btv)
int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
if (btv->input == btv->dig) {
- dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
+ dprintk("%d: load digital timing table (table_idx=%d)\n",
btv->c.nr,table_idx);
/* timing change...reset timing generator address */
@@ -1076,7 +1082,7 @@ static void bt848_bright(struct bttv *btv, int bright)
{
int value;
- // printk("bttv: set bright: %d\n",bright); // DEBUG
+ // printk("set bright: %d\n", bright); // DEBUG
btv->bright = bright;
/* We want -128 to 127 we get 0-65535 */
@@ -1150,8 +1156,7 @@ video_mux(struct bttv *btv, unsigned int input)
}
mux = bttv_muxsel(btv, input);
btaor(mux<<5, ~(3<<5), BT848_IFORM);
- dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
- btv->c.nr,input,mux);
+ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
/* card specific hook */
if(bttv_tvcards[btv->c.type].muxsel_hook)
@@ -1440,7 +1445,7 @@ static void bttv_reinit_bt848(struct bttv *btv)
unsigned long flags;
if (bttv_verbose)
- printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->c.nr);
+ pr_info("%d: reset, reinitialize\n", btv->c.nr);
spin_lock_irqsave(&btv->s_lock,flags);
btv->errors=0;
bttv_set_dma(btv,0);
@@ -1622,8 +1627,8 @@ void bttv_gpio_tracking(struct bttv *btv, char *comment)
unsigned int outbits, data;
outbits = btread(BT848_GPIO_OUT_EN);
data = btread(BT848_GPIO_DATA);
- printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
- btv->c.nr,outbits,data & outbits, data & ~outbits, comment);
+ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
}
static void bttv_field_count(struct bttv *btv)
@@ -1668,7 +1673,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
unsigned long flags;
int retval = 0;
- dprintk("switch_overlay: enter [new=%p]\n",new);
+ dprintk("switch_overlay: enter [new=%p]\n", new);
if (new)
new->vb.state = VIDEOBUF_DONE;
spin_lock_irqsave(&btv->s_lock,flags);
@@ -1678,7 +1683,8 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
bttv_set_dma(btv, 0x03);
spin_unlock_irqrestore(&btv->s_lock,flags);
if (NULL != old) {
- dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state);
+ dprintk("switch_overlay: old=%p state is %d\n",
+ old, old->vb.state);
bttv_dma_free(&fh->cap,btv, old);
kfree(old);
}
@@ -2029,11 +2035,11 @@ static int bttv_log_status(struct file *file, void *f)
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
- printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
+ pr_info("%d: ======== START STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
bttv_call_all(btv, core, log_status);
- printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
+ pr_info("%d: ======== END STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
return 0;
}
@@ -2598,7 +2604,7 @@ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
struct bttv *btv = fh->btv;
if (no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
@@ -2673,7 +2679,7 @@ static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
int rc;
if (no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
@@ -2714,7 +2720,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on)
return -EINVAL;
}
if (unlikely(!fh->ov.setup_ok)) {
- dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
+ dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
retval = -EINVAL;
}
if (retval)
@@ -3091,8 +3097,8 @@ static ssize_t bttv_read(struct file *file, char __user *data,
if (fh->btv->errors)
bttv_reinit_bt848(fh->btv);
- dprintk("bttv%d: read count=%d type=%s\n",
- fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]);
+ dprintk("%d: read count=%d type=%s\n",
+ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -3174,7 +3180,7 @@ static int bttv_open(struct file *file)
struct bttv_fh *fh;
enum v4l2_buf_type type = 0;
- dprintk(KERN_DEBUG "bttv: open dev=%s\n", video_device_node_name(vdev));
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
if (vdev->vfl_type == VFL_TYPE_GRABBER) {
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -3185,8 +3191,8 @@ static int bttv_open(struct file *file)
return -ENODEV;
}
- dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
- btv->c.nr,v4l2_type_names[type]);
+ dprintk("%d: open called (type=%s)\n",
+ btv->c.nr, v4l2_type_names[type]);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
@@ -3288,7 +3294,7 @@ bttv_mmap(struct file *file, struct vm_area_struct *vma)
{
struct bttv_fh *fh = file->private_data;
- dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n",
+ dprintk("%d: mmap type=%s 0x%lx+%ld\n",
fh->btv->c.nr, v4l2_type_names[fh->type],
vma->vm_start, vma->vm_end - vma->vm_start);
return videobuf_mmap_mapper(bttv_queue(fh),vma);
@@ -3370,9 +3376,9 @@ static int radio_open(struct file *file)
struct bttv *btv = video_drvdata(file);
struct bttv_fh *fh;
- dprintk("bttv: open dev=%s\n", video_device_node_name(vdev));
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
- dprintk("bttv%d: open called (radio)\n",btv->c.nr);
+ dprintk("%d: open called (radio)\n", btv->c.nr);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
@@ -3616,12 +3622,12 @@ static int bttv_risc_decode(u32 risc)
};
int i;
- printk("0x%08x [ %s", risc,
+ pr_cont("0x%08x [ %s", risc,
instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
if (risc & (1 << (i + 12)))
- printk(" %s",bits[i]);
- printk(" count=%d ]\n", risc & 0xfff);
+ pr_cont(" %s", bits[i]);
+ pr_cont(" count=%d ]\n", risc & 0xfff);
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
@@ -3630,16 +3636,18 @@ static void bttv_risc_disasm(struct bttv *btv,
{
unsigned int i,j,n;
- printk("%s: risc disasm: %p [dma=0x%08lx]\n",
- btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
for (i = 0; i < (risc->size >> 2); i += n) {
- printk("%s: 0x%lx: ", btv->c.v4l2_dev.name,
- (unsigned long)(risc->dma + (i<<2)));
+ pr_info("%s: 0x%lx: ",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
for (j = 1; j < n; j++)
- printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
- btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)),
- risc->cpu[i+j], j);
+ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
if (0 == risc->cpu[i])
break;
}
@@ -3647,17 +3655,18 @@ static void bttv_risc_disasm(struct bttv *btv,
static void bttv_print_riscaddr(struct bttv *btv)
{
- printk(" main: %08Lx\n",
- (unsigned long long)btv->main.dma);
- printk(" vbi : o=%08Lx e=%08Lx\n",
- btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
- btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
- printk(" cap : o=%08Lx e=%08Lx\n",
- btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
- btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
- printk(" scr : o=%08Lx e=%08Lx\n",
- btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
- btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
+ pr_info(" vbi : o=%08llx e=%08llx\n",
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+ pr_info(" cap : o=%08llx e=%08llx\n",
+ btv->curr.top
+ ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->curr.bottom
+ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+ pr_info(" scr : o=%08llx e=%08llx\n",
+ btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
bttv_risc_disasm(btv, &btv->main);
}
@@ -3690,34 +3699,34 @@ static void bttv_print_irqbits(u32 print, u32 mark)
{
unsigned int i;
- printk("bits:");
+ pr_cont("bits:");
for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
if (print & (1 << i))
- printk(" %s",irq_name[i]);
+ pr_cont(" %s", irq_name[i]);
if (mark & (1 << i))
- printk("*");
+ pr_cont("*");
}
}
static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
{
- printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
- btv->c.nr,
- (unsigned long)btv->main.dma,
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
- (unsigned long)rc);
+ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+ btv->c.nr,
+ (unsigned long)btv->main.dma,
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+ (unsigned long)rc);
if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
- printk("bttv%d: Oh, there (temporarely?) is no input signal. "
- "Ok, then this is harmless, don't worry ;)\n",
- btv->c.nr);
+ pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+ "Ok, then this is harmless, don't worry ;)\n",
+ btv->c.nr);
return;
}
- printk("bttv%d: Uhm. Looks like we have unusual high IRQ latencies.\n",
- btv->c.nr);
- printk("bttv%d: Lets try to catch the culpit red-handed ...\n",
- btv->c.nr);
+ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+ btv->c.nr);
+ pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+ btv->c.nr);
dump_stack();
}
@@ -3798,9 +3807,9 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
}
}
- dprintk("bttv%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
- btv->c.nr,set->top, set->bottom,
- btv->screen,set->frame_irq,set->top_irq);
+ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+ btv->c.nr, set->top, set->bottom,
+ btv->screen, set->frame_irq, set->top_irq);
return 0;
}
@@ -3815,7 +3824,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
if (wakeup->top == wakeup->bottom) {
if (NULL != wakeup->top && curr->top != wakeup->top) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: both=%p\n",btv->c.nr,wakeup->top);
+ pr_debug("%d: wakeup: both=%p\n",
+ btv->c.nr, wakeup->top);
wakeup->top->vb.ts = ts;
wakeup->top->vb.field_count = btv->field_count;
wakeup->top->vb.state = state;
@@ -3824,7 +3834,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
} else {
if (NULL != wakeup->top && curr->top != wakeup->top) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: top=%p\n",btv->c.nr,wakeup->top);
+ pr_debug("%d: wakeup: top=%p\n",
+ btv->c.nr, wakeup->top);
wakeup->top->vb.ts = ts;
wakeup->top->vb.field_count = btv->field_count;
wakeup->top->vb.state = state;
@@ -3832,7 +3843,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
}
if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
if (irq_debug > 1)
- printk("bttv%d: wakeup: bottom=%p\n",btv->c.nr,wakeup->bottom);
+ pr_debug("%d: wakeup: bottom=%p\n",
+ btv->c.nr, wakeup->bottom);
wakeup->bottom->vb.ts = ts;
wakeup->bottom->vb.field_count = btv->field_count;
wakeup->bottom->vb.state = state;
@@ -3866,11 +3878,11 @@ static void bttv_irq_timeout(unsigned long data)
unsigned long flags;
if (bttv_verbose) {
- printk(KERN_INFO "bttv%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
- btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
- btread(BT848_RISC_COUNT));
+ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+ btread(BT848_RISC_COUNT));
bttv_print_irqbits(btread(BT848_INT_STAT),0);
- printk("\n");
+ pr_cont("\n");
}
spin_lock_irqsave(&btv->s_lock,flags);
@@ -4033,21 +4045,23 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
dstat=btread(BT848_DSTATUS);
if (irq_debug) {
- printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d "
- "riscs=%x, riscc=%08x, ",
- btv->c.nr, count, btv->field_count,
- stat>>28, btread(BT848_RISC_COUNT));
+ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+ btv->c.nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
bttv_print_irqbits(stat,astat);
if (stat & BT848_INT_HLOCK)
- printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
- ? "yes" : "no");
+ pr_cont(" HLOC => %s",
+ dstat & BT848_DSTATUS_HLOC
+ ? "yes" : "no");
if (stat & BT848_INT_VPRES)
- printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES)
- ? "yes" : "no");
+ pr_cont(" PRES => %s",
+ dstat & BT848_DSTATUS_PRES
+ ? "yes" : "no");
if (stat & BT848_INT_FMTCHG)
- printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML)
- ? "625" : "525");
- printk("\n");
+ pr_cont(" NUML => %s",
+ dstat & BT848_DSTATUS_NUML
+ ? "625" : "525");
+ pr_cont("\n");
}
if (astat&BT848_INT_VSYNC)
@@ -4075,18 +4089,19 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
audio_mute(btv, btv->mute); /* trigger automute */
if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
- printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.nr,
- (astat & BT848_INT_SCERR) ? "SCERR" : "",
- (astat & BT848_INT_OCERR) ? "OCERR" : "",
- btread(BT848_RISC_COUNT));
+ pr_info("%d: %s%s @ %08x,",
+ btv->c.nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
bttv_print_irqbits(stat,astat);
- printk("\n");
+ pr_cont("\n");
if (bttv_debug)
bttv_print_riscaddr(btv);
}
if (fdsr && astat & BT848_INT_FDSR) {
- printk(KERN_INFO "bttv%d: FDSR @ %08x\n",
- btv->c.nr,btread(BT848_RISC_COUNT));
+ pr_info("%d: FDSR @ %08x\n",
+ btv->c.nr, btread(BT848_RISC_COUNT));
if (bttv_debug)
bttv_print_riscaddr(btv);
}
@@ -4097,11 +4112,11 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
if (count > 8 || !(astat & BT848_INT_GPINT)) {
btwrite(0, BT848_INT_MASK);
- printk(KERN_ERR
- "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr);
+ pr_err("%d: IRQ lockup, cleared int mask [",
+ btv->c.nr);
} else {
- printk(KERN_ERR
- "bttv%d: IRQ lockup, clearing GPINT from int mask [", btv->c.nr);
+ pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+ btv->c.nr);
btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
BT848_INT_MASK);
@@ -4109,7 +4124,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
bttv_print_irqbits(stat,astat);
- printk("]\n");
+ pr_cont("]\n");
}
}
btv->irq_total++;
@@ -4171,7 +4186,7 @@ static void bttv_unregister_video(struct bttv *btv)
static int __devinit bttv_register_video(struct bttv *btv)
{
if (no_overlay > 0)
- printk("bttv: Overlay support disabled.\n");
+ pr_notice("Overlay support disabled\n");
/* video */
btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
@@ -4181,12 +4196,11 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
video_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->video_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
if (device_create_file(&btv->video_dev->dev,
&dev_attr_card)<0) {
- printk(KERN_ERR "bttv%d: device_create_file 'card' "
- "failed\n", btv->c.nr);
+ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
goto err;
}
@@ -4198,8 +4212,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
vbi_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->vbi_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
if (!btv->has_radio)
return 0;
@@ -4210,8 +4224,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
radio_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->radio_dev));
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
/* all done */
return 0;
@@ -4244,10 +4258,10 @@ static int __devinit bttv_probe(struct pci_dev *dev,
if (bttv_num == BTTV_MAX)
return -ENOMEM;
- printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
+ pr_info("Bt8xx card found (%d)\n", bttv_num);
bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
if (btv == NULL) {
- printk(KERN_ERR "bttv: out of memory.\n");
+ pr_err("out of memory\n");
return -ENOMEM;
}
btv->c.nr = bttv_num;
@@ -4277,21 +4291,19 @@ static int __devinit bttv_probe(struct pci_dev *dev,
btv->c.pci = dev;
btv->id = dev->device;
if (pci_enable_device(dev)) {
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
return -EIO;
}
if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
- printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
- btv->c.nr);
+ pr_warn("%d: No suitable DMA available\n", btv->c.nr);
return -EIO;
}
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
btv->c.v4l2_dev.name)) {
- printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n",
- btv->c.nr,
- (unsigned long long)pci_resource_start(dev,0));
+ pr_warn("%d: can't request iomem (0x%llx)\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev, 0));
return -EBUSY;
}
pci_set_master(dev);
@@ -4299,22 +4311,21 @@ static int __devinit bttv_probe(struct pci_dev *dev,
result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
if (result < 0) {
- printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr);
+ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
goto fail0;
}
btv->revision = dev->revision;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
- printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ",
- bttv_num,btv->id, btv->revision, pci_name(dev));
- printk("irq: %d, latency: %d, mmio: 0x%llx\n",
- btv->c.pci->irq, lat,
- (unsigned long long)pci_resource_start(dev,0));
+ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+ bttv_num, btv->id, btv->revision, pci_name(dev),
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev, 0));
schedule();
btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
if (NULL == btv->bt848_mmio) {
- printk("bttv%d: ioremap() failed\n", btv->c.nr);
+ pr_err("%d: ioremap() failed\n", btv->c.nr);
result = -EIO;
goto fail1;
}
@@ -4327,8 +4338,8 @@ static int __devinit bttv_probe(struct pci_dev *dev,
result = request_irq(btv->c.pci->irq, bttv_irq,
IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
if (result < 0) {
- printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
- bttv_num,btv->c.pci->irq);
+ pr_err("%d: can't get IRQ %d\n",
+ bttv_num, btv->c.pci->irq);
goto fail1;
}
@@ -4433,7 +4444,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
struct bttv *btv = to_bttv(v4l2_dev);
if (bttv_verbose)
- printk("bttv%d: unloading\n",btv->c.nr);
+ pr_info("%d: unloading\n", btv->c.nr);
if (bttv_tvcards[btv->c.type].has_dvb)
flush_request_modules(btv);
@@ -4481,7 +4492,7 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
struct bttv_buffer_set idle;
unsigned long flags;
- dprintk("bttv%d: suspend %d\n", btv->c.nr, state.event);
+ dprintk("%d: suspend %d\n", btv->c.nr, state.event);
/* stop dma + irqs */
spin_lock_irqsave(&btv->s_lock,flags);
@@ -4517,14 +4528,13 @@ static int bttv_resume(struct pci_dev *pci_dev)
unsigned long flags;
int err;
- dprintk("bttv%d: resume\n", btv->c.nr);
+ dprintk("%d: resume\n", btv->c.nr);
/* restore pci state */
if (btv->state.disabled) {
err=pci_enable_device(pci_dev);
if (err) {
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
return err;
}
btv->state.disabled = 0;
@@ -4532,8 +4542,7 @@ static int bttv_resume(struct pci_dev *pci_dev)
err=pci_set_power_state(pci_dev, PCI_D0);
if (err) {
pci_disable_device(pci_dev);
- printk(KERN_WARNING "bttv%d: Can't enable device.\n",
- btv->c.nr);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
btv->state.disabled = 1;
return err;
}
@@ -4585,22 +4594,21 @@ static int __init bttv_init_module(void)
bttv_num = 0;
- printk(KERN_INFO "bttv: driver version %s loaded\n",
- BTTV_VERSION);
+ pr_info("driver version %s loaded\n", BTTV_VERSION);
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
if (gbufsize > BTTV_MAX_FBUF)
gbufsize = BTTV_MAX_FBUF;
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
if (bttv_verbose)
- printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n",
- gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+ pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
bttv_check_chipset();
ret = bus_register(&bttv_sub_bus_type);
if (ret < 0) {
- printk(KERN_WARNING "bttv: bus_register error: %d\n", ret);
+ pr_warn("bus_register error: %d\n", ret);
return ret;
}
ret = pci_register_driver(&bttv_pci_driver);
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index 13ce72c04b33..922e8233fd0b 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -26,6 +26,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -99,7 +101,7 @@ int bttv_sub_add_device(struct bttv_core *core, char *name)
kfree(sub);
return err;
}
- printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
+ pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
list_add_tail(&sub->list,&core->subs);
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index d49b675045fe..e3952af7e56e 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -27,6 +27,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -154,9 +156,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
if (retval == 0)
goto eio;
if (i2c_debug) {
- printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
+ pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
}
for (cnt = 1; cnt < msg->len; cnt++ ) {
@@ -170,19 +170,18 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
goto err;
if (retval == 0)
goto eio;
- if (i2c_debug) {
- printk(" %02x", msg->buf[cnt]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
- }
+ if (i2c_debug)
+ pr_cont(" %02x", msg->buf[cnt]);
}
+ if (!(xmit & BT878_I2C_NOSTOP))
+ pr_cont(">\n");
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
- printk(" ERR: %d\n",retval);
+ pr_cont(" ERR: %d\n",retval);
return retval;
}
@@ -193,7 +192,7 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
u32 cnt;
int retval;
- for(cnt = 0; cnt < msg->len; cnt++) {
+ for (cnt = 0; cnt < msg->len; cnt++) {
xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
if (cnt < msg->len-1)
xmit |= BT848_I2C_W3B;
@@ -201,6 +200,12 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
xmit |= BT878_I2C_NOSTOP;
if (cnt)
xmit |= BT878_I2C_NOSTART;
+
+ if (i2c_debug) {
+ if (!(xmit & BT878_I2C_NOSTART))
+ pr_cont(" <R %02x", (msg->addr << 1) +1);
+ }
+
btwrite(xmit, BT848_I2C);
retval = bttv_i2c_wait_done(btv);
if (retval < 0)
@@ -209,20 +214,20 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
goto eio;
msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
if (i2c_debug) {
- if (!(xmit & BT878_I2C_NOSTART))
- printk(" <R %02x", (msg->addr << 1) +1);
- printk(" =%02x", msg->buf[cnt]);
- if (!(xmit & BT878_I2C_NOSTOP))
- printk(" >\n");
+ pr_cont(" =%02x", msg->buf[cnt]);
}
+ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP))
+ pr_cont(" >\n");
}
+
+
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
- printk(" ERR: %d\n",retval);
+ pr_cont(" ERR: %d\n",retval);
return retval;
}
@@ -234,7 +239,8 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int
int i;
if (i2c_debug)
- printk("bt-i2c:");
+ pr_debug("bt-i2c:");
+
btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
for (i = 0 ; i < num; i++) {
if (msgs[i].flags & I2C_M_RD) {
@@ -271,20 +277,20 @@ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
if (0 != btv->i2c_rc)
return -1;
if (bttv_verbose && NULL != probe_for)
- printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ",
- btv->c.nr,probe_for,addr);
+ pr_info("%d: i2c: checking for %s @ 0x%02x... ",
+ btv->c.nr, probe_for, addr);
btv->i2c_client.addr = addr >> 1;
if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
if (NULL != probe_for) {
if (bttv_verbose)
- printk("not found\n");
+ pr_cont("not found\n");
} else
- printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n",
- btv->c.nr,addr);
+ pr_warn("%d: i2c read 0x%x: error\n",
+ btv->c.nr, addr);
return -1;
}
if (bttv_verbose && NULL != probe_for)
- printk("found\n");
+ pr_cont("found\n");
return buffer;
}
@@ -335,8 +341,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
continue;
- printk("%s: i2c scan: found device @ 0x%x [%s]\n",
- name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ pr_info("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 677d70c0e1ce..ef4c7cd41982 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -36,9 +38,10 @@ static int ir_rc5_remote_gap = 885;
module_param(ir_rc5_remote_gap, int, 0644);
#undef dprintk
-#define dprintk(arg...) do { \
- if (ir_debug >= 1) \
- printk(arg); \
+#define dprintk(fmt, ...) \
+do { \
+ if (ir_debug >= 1) \
+ pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#define DEVNAME "bttv-input"
@@ -62,7 +65,7 @@ static void ir_handle_key(struct bttv *btv)
/* extract data */
data = ir_extract_bits(gpio, ir->mask_keycode);
- dprintk(KERN_INFO DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n",
+ dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
gpio, data,
ir->polling ? "poll" : "irq",
(gpio & ir->mask_keydown) ? " down" : "",
@@ -96,7 +99,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
if ((ir->last_gpio & 0x7f) != data) {
- dprintk(KERN_INFO DEVNAME ": gpio=0x%x code=%d | %s\n",
+ dprintk("gpio=0x%x code=%d | %s\n",
gpio, data,
(gpio & ir->mask_keyup) ? " up" : "up/down");
@@ -107,7 +110,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
if ((ir->last_gpio & 1 << 31) == keyup)
return;
- dprintk(KERN_INFO DEVNAME ":(cnt) gpio=0x%x code=%d | %s\n",
+ dprintk("(cnt) gpio=0x%x code=%d | %s\n",
gpio, data,
(gpio & ir->mask_keyup) ? " up" : "down");
@@ -177,13 +180,12 @@ static u32 bttv_rc5_decode(unsigned int code)
rc5 |= 1;
break;
case 3:
- dprintk(KERN_INFO DEVNAME ":rc5_decode(%x) bad code\n",
+ dprintk("rc5_decode(%x) bad code\n",
org_code);
return 0;
}
}
- dprintk(KERN_INFO DEVNAME ":"
- "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
"instr=%x\n", rc5, org_code, RC5_START(rc5),
RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
return rc5;
@@ -212,20 +214,20 @@ static void bttv_rc5_timer_end(unsigned long data)
/* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
if (gap < 28000) {
- dprintk(KERN_INFO DEVNAME ": spurious timer_end\n");
+ dprintk("spurious timer_end\n");
return;
}
if (ir->last_bit < 20) {
/* ignore spurious codes (caused by light/other remotes) */
- dprintk(KERN_INFO DEVNAME ": short code: %x\n", ir->code);
+ dprintk("short code: %x\n", ir->code);
} else {
ir->code = (ir->code << ir->shift_by) | 1;
rc5 = bttv_rc5_decode(ir->code);
/* two start bits? */
if (RC5_START(rc5) != ir->start) {
- printk(KERN_INFO DEVNAME ":"
+ pr_info(DEVNAME ":"
" rc5 start bits invalid: %u\n", RC5_START(rc5));
/* right address? */
@@ -235,8 +237,7 @@ static void bttv_rc5_timer_end(unsigned long data)
/* Good code */
rc_keydown(ir->dev, instr, toggle);
- dprintk(KERN_INFO DEVNAME ":"
- " instruction %x, toggle %x\n",
+ dprintk("instruction %x, toggle %x\n",
instr, toggle);
}
}
@@ -265,7 +266,7 @@ static int bttv_rc5_irq(struct bttv *btv)
tv.tv_usec - ir->base_time.tv_usec;
}
- dprintk(KERN_INFO DEVNAME ": RC5 IRQ: gap %d us for %s\n",
+ dprintk("RC5 IRQ: gap %d us for %s\n",
gap, (gpio & 0x20) ? "mark" : "space");
/* remote IRQ? */
@@ -340,14 +341,14 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */
if (1 != i2c_master_recv(ir->c, &b, 1)) {
- dprintk(KERN_INFO DEVNAME ": read error\n");
+ dprintk("read error\n");
return -EIO;
}
/* ignore 0xaa */
if (b==0xaa)
return 0;
- dprintk(KERN_INFO DEVNAME ": key %02x\n", b);
+ dprintk("key %02x\n", b);
/*
* NOTE:
@@ -517,7 +518,7 @@ int bttv_input_init(struct bttv *btv)
break;
}
if (NULL == ir_codes) {
- dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type);
+ dprintk("Ooops: IR config error [card=%d]\n", btv->c.type);
err = -ENODEV;
goto err_out_free;
}
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
index 9b57d091da48..82cc47d2e3fa 100644
--- a/drivers/media/video/bt8xx/bttv-risc.c
+++ b/drivers/media/video/bt8xx/bttv-risc.c
@@ -24,6 +24,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -473,8 +475,7 @@ bttv_set_dma(struct bttv *btv, int override)
capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */
capctl |= override;
- d2printk(KERN_DEBUG
- "bttv%d: capctl=%x lirq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n",
+ d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n",
btv->c.nr,capctl,btv->loop_irq,
btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
@@ -517,8 +518,8 @@ bttv_risc_init_main(struct bttv *btv)
if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
return rc;
- dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n",
- btv->c.nr,(unsigned long long)btv->main.dma);
+ dprintk("%d: risc main @ %08llx\n",
+ btv->c.nr, (unsigned long long)btv->main.dma);
btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
BT848_FIFO_STATUS_VRE);
@@ -557,12 +558,12 @@ bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
unsigned long next = btv->main.dma + ((slot+2) << 2);
if (NULL == risc) {
- d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n",
- btv->c.nr,risc,slot);
+ d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot);
btv->main.cpu[slot+1] = cpu_to_le32(next);
} else {
- d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n",
- btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags);
+ d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n",
+ btv->c.nr, risc, slot,
+ (unsigned long long)risc->dma, irqflags);
cmd = BT848_RISC_JUMP;
if (irqflags) {
cmd |= BT848_RISC_IRQ;
@@ -708,8 +709,7 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- dprintk(KERN_DEBUG
- "bttv%d: buffer field: %s format: %s size: %dx%d\n",
+ dprintk("%d: buffer field: %s format: %s size: %dx%d\n",
btv->c.nr, v4l2_field_names[buf->vb.field],
buf->fmt->name, buf->vb.width, buf->vb.height);
@@ -870,10 +870,9 @@ bttv_overlay_risc(struct bttv *btv,
struct bttv_buffer *buf)
{
/* check interleave, bottom+top fields */
- dprintk(KERN_DEBUG
- "bttv%d: overlay fields: %s format: %s size: %dx%d\n",
+ dprintk("%d: overlay fields: %s format: %s size: %dx%d\n",
btv->c.nr, v4l2_field_names[buf->vb.field],
- fmt->name,ov->w.width,ov->w.height);
+ fmt->name, ov->w.width, ov->w.height);
/* calculate geometry */
bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
index e79a402fa6cd..b433267d9aa9 100644
--- a/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/drivers/media/video/bt8xx/bttv-vbi.c
@@ -23,6 +23,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -65,8 +67,11 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
#ifdef dprintk
# undef dprintk
#endif
-#define dprintk(fmt, arg...) if (vbi_debug) \
- printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg)
+#define dprintk(fmt, ...) \
+do { \
+ if (vbi_debug) \
+ pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \
+} while (0)
#define IMAGE_SIZE(fmt) \
(((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index 318edf2830b4..db943a8d580d 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -310,9 +310,21 @@ extern unsigned int bttv_gpio;
extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
extern int init_bttv_i2c(struct bttv *btv);
-#define bttv_printk if (bttv_verbose) printk
-#define dprintk if (bttv_debug >= 1) printk
-#define d2printk if (bttv_debug >= 2) printk
+#define dprintk(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+#define dprintk_cont(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+} while (0)
+#define d2printk(fmt, ...) \
+do { \
+ if (bttv_debug >= 2) \
+ printk(fmt, ##__VA_ARGS__); \
+} while (0)
#define BTTV_MAX_FBUF 0x208000
#define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
index 2fadd9ded340..a86bab5893ef 100644
--- a/drivers/media/video/cx18/Makefile
+++ b/drivers/media/video/cx18/Makefile
@@ -8,6 +8,6 @@ cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
obj-$(CONFIG_VIDEO_CX18) += cx18.o
obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 9e2f870f4258..c6ff32a6137c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -1085,6 +1085,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
setup.addr = ADDR_UNSET;
setup.type = cx->options.tuner;
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (cx->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
cx18_reset_tuner_gpio : NULL;
cx18_call_all(cx, tuner, s_type_addr, &setup);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 183420723060..b9a94fc5146d 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -409,6 +409,7 @@ struct cx18_stream {
/* Videobuf for YUV video */
u32 pixelformat;
+ u32 vb_bytes_per_frame;
struct list_head vb_capture; /* video capture queue */
spinlock_t vb_lock;
struct timer_list vb_timeout;
@@ -430,10 +431,6 @@ struct cx18_open_id {
u32 open_id;
int type;
struct cx18 *cx;
-
- struct videobuf_queue vbuf_q;
- spinlock_t s_lock; /* Protect vbuf_q */
- enum v4l2_buf_type vb_type;
};
static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh)
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 07411f34885a..14cb961c22bd 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -784,8 +784,6 @@ int cx18_v4l2_close(struct file *filp)
cx18_release_stream(s);
} else {
cx18_stop_capture(id, 0);
- if (id->type == CX18_ENC_STREAM_TYPE_YUV)
- videobuf_mmap_free(&id->vbuf_q);
}
kfree(id);
mutex_unlock(&cx->serialize_lock);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index afe0a29e7200..66b1c15c3541 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -160,12 +160,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
pixfmt->priv = 0;
if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
pixfmt->pixelformat = s->pixelformat;
- /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
- UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
- else
- pixfmt->sizeimage = pixfmt->height * 720 * 2;
+ pixfmt->sizeimage = s->vb_bytes_per_frame;
pixfmt->bytesperline = 720;
} else {
pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
@@ -296,6 +291,12 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
return -EBUSY;
s->pixelformat = fmt->fmt.pix.pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = h * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = h * 720 * 2;
mbus_fmt.width = cx->cxhdl.width = w;
mbus_fmt.height = cx->cxhdl.height = h;
@@ -463,13 +464,16 @@ static int cx18_s_register(struct file *file, void *fh,
static int cx18_querycap(struct file *file, void *fh,
struct v4l2_capability *vcap)
{
- struct cx18 *cx = fh2id(fh)->cx;
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
snprintf(vcap->bus_info, sizeof(vcap->bus_info),
"PCI:%s", pci_name(cx->pci_dev));
vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+ vcap->capabilities |= V4L2_CAP_STREAMING;
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index c07191e09fcb..0c7796e76ac0 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -196,7 +196,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
}
/* If we've filled the buffer as per the callers res then dispatch it */
- if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) {
+ if (vb_buf->bytes_used >= s->vb_bytes_per_frame) {
dispatch = 1;
vb_buf->bytes_used = 0;
}
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 852f420fd271..638cca156b58 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -138,6 +138,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q,
buf->tvnorm = cx->std;
s->pixelformat = pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
cx18_dma_free(q, s, buf);
}
@@ -154,6 +160,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q,
buf->tvnorm = cx->std;
s->pixelformat = pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
rc = videobuf_iolock(q, &buf->vb, NULL);
if (rc != 0)
goto fail;
@@ -287,6 +299,7 @@ static void cx18_stream_init(struct cx18 *cx, int type)
/* Assume the previous pixel default */
s->pixelformat = V4L2_PIX_FMT_HM12;
+ s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
}
}
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
index 2c2484355449..b3348975c7c2 100644
--- a/drivers/media/video/cx231xx/Makefile
+++ b/drivers/media/video/cx231xx/Makefile
@@ -8,9 +8,9 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-usb
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index caab1bfb79e2..b391e9bda877 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -38,7 +38,7 @@ config VIDEO_CX23885
config MEDIA_ALTERA_CI
tristate "Altera FPGA based CI module"
depends on VIDEO_CX23885 && DVB_CORE
- select STAPL_ALTERA
+ select ALTERA_STAPL
---help---
An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 23293c7b6ac7..f81f2796a0f9 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -2,14 +2,14 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
- cx23885-f300.o
+ cx23885-f300.o cx23885-alsa.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/video/cx23885/cx23885-alsa.c
new file mode 100644
index 000000000000..795169237e70
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-alsa.c
@@ -0,0 +1,535 @@
+/*
+ *
+ * Support for CX23885 analog audio capture
+ *
+ * (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>
+ * Adapted from cx88-alsa.c
+ * (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <asm/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <sound/tlv.h>
+
+
+#include "cx23885.h"
+#include "cx23885-reg.h"
+
+#define AUDIO_SRAM_CHANNEL SRAM_CH07
+
+#define dprintk(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg)
+
+#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg)
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static unsigned int disable_analog_audio;
+module_param(disable_analog_audio, int, 0644);
+MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
+
+/****************************************************************************
+ Board specific funtions
+ ****************************************************************************/
+
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1 (1 << 0)
+#define AUD_INT_UP_RISCI1 (1 << 1)
+#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
+#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2 (1 << 5)
+#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
+#define AUD_INT_DN_SYNC (1 << 12)
+#define AUD_INT_UP_SYNC (1 << 13)
+#define AUD_INT_RDS_DN_SYNC (1 << 14)
+#define AUD_INT_OPC_ERR (1 << 16)
+#define AUD_INT_BER_IRQ (1 << 20)
+#define AUD_INT_MCHG_IRQ (1 << 21)
+#define GP_COUNT_CONTROL_RESET 0x3
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_audio_buffer *buf = chip->buf;
+ struct cx23885_dev *dev = chip->dev;
+ struct sram_channel *audio_ch =
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL];
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* setup fifo + format - out channel */
+ cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,
+ buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(AUD_INT_A_LNGTH, buf->bpl);
+
+ /* This is required to get good audio (1 seems to be ok) */
+ cx_write(AUD_INT_A_MODE, 1);
+
+ /* reset counter */
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(AUDIO_INT_INT_STAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+ cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and
+ RISC enable */
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev, audio_ch);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_dev *dev = chip->dev;
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* stop dma */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+ cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Handles audio IRQ
+ */
+int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ if (0 == (status & mask))
+ return 0;
+
+ cx_write(AUDIO_INT_INT_STAT, status);
+
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ printk(KERN_WARNING "%s/1: Audio risc op code error\n",
+ dev->name);
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ dprintk(1, "Downstream sync error\n");
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ return 1;
+ }
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ /* FIXME: Any other status should deserve a special handling? */
+
+ return 1;
+}
+
+static int dsp_buffer_free(struct cx23885_audio_dev *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2, "Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci, &chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+
+static struct snd_pcm_hardware snd_cx23885_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE/4,
+ .period_bytes_max = DEFAULT_FIFO_SIZE/4,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (!chip) {
+ printk(KERN_ERR "BUG: cx23885 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx23885_digital_hw;
+
+ if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+ DEFAULT_FIFO_SIZE) {
+ unsigned int bpl = chip->dev->
+ sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;
+ bpl &= ~7; /* must be multiple of 8 */
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx23885_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx23885_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0)
+ goto error;
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx23885_hw_free(struct snd_pcm_substream *substream)
+{
+
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx23885_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = cx23885_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err = cx23885_stop_audio_dma(chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx23885_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+ return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx23885_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx23885_pcm_ops = {
+ .open = snd_cx23885_pcm_open,
+ .close = snd_cx23885_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx23885_hw_params,
+ .hw_free = snd_cx23885_hw_free,
+ .prepare = snd_cx23885_prepare,
+ .trigger = snd_cx23885_card_trigger,
+ .pointer = snd_cx23885_pointer,
+ .page = snd_cx23885_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,
+ char *name)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)
+{
+ struct snd_card *card;
+ struct cx23885_audio_dev *chip;
+ int err;
+
+ if (disable_analog_audio)
+ return NULL;
+
+ if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {
+ printk(KERN_WARNING "%s(): Missing SRAM channel configuration "
+ "for analog TV Audio\n", __func__);
+ return NULL;
+ }
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);
+ if (err < 0)
+ goto error;
+
+ chip = (struct cx23885_audio_dev *) card->private_data;
+ chip->dev = dev;
+ chip->pci = dev->pci;
+ chip->card = card;
+ spin_lock_init(&chip->lock);
+
+ snd_card_set_dev(card, &dev->pci->dev);
+
+ err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");
+ if (err < 0)
+ goto error;
+
+ strcpy(card->driver, "CX23885");
+ sprintf(card->shortname, "Conexant CX23885");
+ sprintf(card->longname, "%s at %s", card->shortname, dev->name);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dprintk(0, "registered ALSA audio device\n");
+
+ return chip;
+
+error:
+ snd_card_free(card);
+ printk(KERN_ERR "%s(): Failed to register analog "
+ "audio adapter\n", __func__);
+
+ return NULL;
+}
+
+/*
+ * ALSA destructor
+ */
+void cx23885_audio_unregister(struct cx23885_dev *dev)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ snd_card_free(chip->card);
+}
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 76b7563de39c..c3cf08945e4c 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -25,8 +25,8 @@
#include <linux/delay.h>
#include <media/cx25840.h>
#include <linux/firmware.h>
+#include <misc/altera.h>
-#include "../../../staging/altera-stapl/altera.h"
#include "cx23885.h"
#include "tuner-xc2028.h"
#include "netup-eeprom.h"
@@ -106,12 +106,14 @@ struct cx23885_board cx23885_boards[] = {
.vmux = CX25840_VIN7_CH3 |
CX25840_VIN5_CH2 |
CX25840_VIN2_CH1,
+ .amux = CX25840_AUDIO8,
.gpio0 = 0,
}, {
.type = CX23885_VMUX_COMPOSITE1,
.vmux = CX25840_VIN7_CH3 |
CX25840_VIN4_CH2 |
CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
.gpio0 = 0,
}, {
.type = CX23885_VMUX_SVIDEO,
@@ -119,6 +121,7 @@ struct cx23885_board cx23885_boards[] = {
CX25840_VIN4_CH2 |
CX25840_VIN8_CH1 |
CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
.gpio0 = 0,
} },
},
@@ -153,7 +156,30 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_HAUPPAUGE_HVR1500] = {
.name = "Hauppauge WinTV-HVR1500",
+ .porta = CX23885_ANALOG_VIDEO,
.portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61, /* 0xc2 >> 1 */
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
+ } },
},
[CX23885_BOARD_HAUPPAUGE_HVR1200] = {
.name = "Hauppauge WinTV-HVR1200",
@@ -387,6 +413,31 @@ struct cx23885_board cx23885_boards[] = {
.vmux = CX25840_COMPOSITE1,
} },
},
+ [CX23885_BOARD_MPX885] = {
+ .name = "MPX-885",
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = CX25840_COMPOSITE3,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = CX25840_COMPOSITE4,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -1415,6 +1466,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_MPX885:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index ee41a8882f58..40e68b22015e 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -54,7 +54,7 @@ MODULE_PARM_DESC(card, "card type");
#define dprintk(level, fmt, arg...)\
do { if (debug >= level)\
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
} while (0)
static unsigned int cx23885_devcount;
@@ -155,12 +155,12 @@ static struct sram_channel cx23885_sram_channels[] = {
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH07] = {
- .name = "ch7",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "TV Audio",
+ .cmds_start = 0x10190,
+ .ctrl_start = 0x10480,
+ .cdt = 0x10a00,
+ .fifo_start = 0x7000,
+ .fifo_size = 0x1000,
.ptr1_reg = DMA6_PTR1,
.ptr2_reg = DMA6_PTR2,
.cnt1_reg = DMA6_CNT1,
@@ -1082,10 +1082,10 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev)
static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int padding,
- unsigned int lines)
+ unsigned int lines, unsigned int lpi)
{
struct scatterlist *sg;
- unsigned int line, todo;
+ unsigned int line, todo, sol;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
@@ -1098,16 +1098,22 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
offset -= sg_dma_len(sg);
sg++;
}
+
+ if (lpi && line > 0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+
if (bpl <= sg_dma_len(sg)-offset) {
/* fits into current chunk */
- *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl);
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
offset += bpl;
} else {
/* scanline needs to be split */
todo = bpl;
- *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|
(sg_dma_len(sg)-offset));
*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
@@ -1164,10 +1170,10 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
rp = risc->cpu;
if (UNSET != top_offset)
rp = cx23885_risc_field(rp, sglist, top_offset, 0,
- bpl, padding, lines);
+ bpl, padding, lines, 0);
if (UNSET != bottom_offset)
rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
- bpl, padding, lines);
+ bpl, padding, lines, 0);
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -1175,11 +1181,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
return 0;
}
-static int cx23885_risc_databuffer(struct pci_dev *pci,
+int cx23885_risc_databuffer(struct pci_dev *pci,
struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int bpl,
- unsigned int lines)
+ unsigned int lines, unsigned int lpi)
{
u32 instructions;
__le32 *rp;
@@ -1199,7 +1205,55 @@ static int cx23885_risc_databuffer(struct pci_dev *pci,
/* write risc instructions */
rp = risc->cpu;
- rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
+ rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE,
+ bpl, 0, lines, lpi);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int top_offset,
+ unsigned int bottom_offset, unsigned int bpl,
+ unsigned int padding, unsigned int lines)
+{
+ u32 instructions, fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ /* write and jump need and extra dword */
+ instructions = fields * (1 + ((bpl + padding) * lines)
+ / PAGE_SIZE + lines);
+ instructions += 2;
+ rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+ if (rc < 0)
+ return rc;
+ /* write risc instructions */
+ rp = risc->cpu;
+
+ /* Sync to line 6, so US CC line 21 will appear in line '12'
+ * in the userland vbi payload */
+ if (UNSET != top_offset)
+ rp = cx23885_risc_field(rp, sglist, top_offset, 6,
+ bpl, padding, lines, 0);
+
+ if (UNSET != bottom_offset)
+ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207,
+ bpl, padding, lines, 0);
+
+
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -1207,6 +1261,7 @@ static int cx23885_risc_databuffer(struct pci_dev *pci,
return 0;
}
+
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value)
{
@@ -1517,7 +1572,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
goto fail;
cx23885_risc_databuffer(dev->pci, &buf->risc,
videobuf_to_dma(&buf->vb)->sglist,
- buf->vb.width, buf->vb.height);
+ buf->vb.width, buf->vb.height, 0);
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
@@ -1741,15 +1796,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
struct cx23885_tsport *ts2 = &dev->ts2;
u32 pci_status, pci_mask;
u32 vida_status, vida_mask;
+ u32 audint_status, audint_mask;
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+ int audint_count = 0;
bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
+ audint_status = cx_read(AUDIO_INT_INT_STAT);
+ audint_mask = cx_read(AUDIO_INT_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
ts1_mask = cx_read(VID_B_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
@@ -1759,12 +1818,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
goto out;
vida_count = cx_read(VID_A_GPCNT);
+ audint_count = cx_read(AUD_INT_A_GPCNT);
ts1_count = cx_read(ts1->reg_gpcnt);
ts2_count = cx_read(ts2->reg_gpcnt);
dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
pci_status, pci_mask);
dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
vida_status, vida_mask, vida_count);
+ dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n",
+ audint_status, audint_mask, audint_count);
dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
ts1_status, ts1_mask, ts1_count);
dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
@@ -1861,6 +1923,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if (vida_status)
handled += cx23885_video_irq(dev, vida_status);
+ if (audint_status)
+ handled += cx23885_audio_irq(dev, audint_status, audint_mask);
+
if (pci_status & PCI_MSK_IR) {
subdev_handled = false;
v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index aa83f07b1b0f..bcb45be44bb2 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -844,7 +844,7 @@ static int dvb_register(struct cx23885_tsport *port)
static struct xc2028_ctrl ctl = {
.fname = XC3028L_DEFAULT_FIRMWARE,
.max_len = 64,
- .demod = 5000,
+ .demod = XC3028_FE_DIBCOM52,
/* This is true for all demods with
v36 firmware? */
.type = XC2028_D2633,
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index 307ff543c254..0ff7a9e98f3e 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -287,6 +287,7 @@ static char *i2c_devs[128] = {
[0x32 >> 1] = "cx24227",
[0x88 >> 1] = "cx25837",
[0x84 >> 1] = "tda8295",
+ [0x98 >> 1] = "flatiron",
[0xa0 >> 1] = "eeprom",
[0xc0 >> 1] = "tuner/mt2131/tda8275",
[0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028",
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index c87ac682ebbe..a99936e0cbc2 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -203,6 +203,7 @@ Channel manager Data Structure entry = 20 DWORD
#define SD2_BIAS_CTRL 0x0000000A
#define AMP_BIAS_CTRL 0x0000000C
#define CH_PWR_CTRL1 0x0000000E
+#define FLD_CH_SEL (1 << 3)
#define CH_PWR_CTRL2 0x0000000F
#define DSM_STATUS1 0x00000010
#define DSM_STATUS2 0x00000011
@@ -271,7 +272,9 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_BC_MSK_OPC_ERR (1 << 16)
#define VID_BC_MSK_SYNC (1 << 12)
#define VID_BC_MSK_OF (1 << 8)
+#define VID_BC_MSK_VBI_RISCI2 (1 << 5)
#define VID_BC_MSK_RISCI2 (1 << 4)
+#define VID_BC_MSK_VBI_RISCI1 (1 << 1)
#define VID_BC_MSK_RISCI1 1
#define VID_C_INT_MSK 0x00040040
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
index c0b60382ad13..a1154f035bc1 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -41,6 +41,12 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 1440
+#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
+#define NTSC_VBI_END_LINE 21
+#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1)
+
+
int cx23885_vbi_fmt(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -49,43 +55,86 @@ int cx23885_vbi_fmt(struct file *file, void *priv,
if (dev->tvnorm & V4L2_STD_525_60) {
/* ntsc */
- f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sampling_rate = 27000000;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
f->fmt.vbi.start[0] = 10;
- f->fmt.vbi.start[1] = 273;
-
+ f->fmt.vbi.count[0] = 17;
+ f->fmt.vbi.start[1] = 263 + 10 + 1;
+ f->fmt.vbi.count[1] = 17;
} else if (dev->tvnorm & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 - 1;
f->fmt.vbi.start[1] = 319 - 1;
}
+
return 0;
}
+/* We're given the Video Interrupt status register.
+ * The cx23885_video_irq() func has already validated
+ * the potential error bits, we just need to
+ * deal with vbi payload and return indication if
+ * we actually processed any payload.
+ */
+int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 count;
+ int handled = 0;
+
+ if (status & VID_BC_MSK_VBI_RISCI1) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__);
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ if (status & VID_BC_MSK_VBI_RISCI2) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__);
+ dprintk(2, "stopper vbi\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_vbi_queue(dev, &dev->vbiq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ return handled;
+}
+
static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
{
+ dprintk(1, "%s()\n", __func__);
+
/* setup fifo + format */
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
buf->vb.width, buf->risc.dma);
/* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ cx_write(VID_A_VBI_CTRL, 3);
+ cx_write(VBI_A_GPCNT_CTL, 3);
q->count = 1;
- /* enable irqs */
+ /* enable irq */
cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */
cx_set(DEV_CNTRL2, (1<<5));
- cx_set(VID_A_DMA_CTL, 0x00000022);
+ cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */
return 0;
}
-static int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q)
{
struct cx23885_buffer *buf;
@@ -102,7 +151,7 @@ static int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
buf = list_entry(item, struct cx23885_buffer, vb.queue);
buf->count = q->count++;
}
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
return 0;
}
@@ -113,8 +162,7 @@ void cx23885_vbi_timeout(unsigned long data)
struct cx23885_buffer *buf;
unsigned long flags;
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]);
-
+ /* Stop the VBI engine */
cx_clear(VID_A_DMA_CTL, 0x22);
spin_lock_irqsave(&dev->slock, flags);
@@ -132,7 +180,7 @@ void cx23885_vbi_timeout(unsigned long data)
}
/* ------------------------------------------------------------------ */
-#define VBI_LINE_LENGTH 2048
+#define VBI_LINE_LENGTH 1440
#define VBI_LINE_COUNT 17
static int
@@ -173,7 +221,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
rc = videobuf_iolock(q, &buf->vb, NULL);
if (0 != rc)
goto fail;
- cx23885_risc_buffer(dev->pci, &buf->risc,
+ cx23885_risc_vbibuffer(dev->pci, &buf->risc,
dma->sglist,
0, buf->vb.width * buf->vb.height,
buf->vb.width, 0,
@@ -207,7 +255,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
cx23885_start_vbi_dma(dev, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
dprintk(2, "[%p/%d] vbi_queue - first active\n",
buf, buf->vb.i);
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 896bb32dbf03..e730b9263016 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -37,6 +37,8 @@
#include "cx23885-ioctl.h"
#include "tuner-xc2028.h"
+#include <media/cx25840.h>
+
MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
@@ -69,14 +71,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
#define dprintk(level, fmt, arg...)\
do { if (video_debug >= level)\
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
} while (0)
/* ------------------------------------------------------------------- */
/* static data */
#define FORMAT_FLAGS_PACKED 0x01
-
+#if 0
static struct cx23885_fmt formats[] = {
{
.name = "8 bpp, gray",
@@ -130,6 +132,23 @@ static struct cx23885_fmt formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},
};
+#else
+static struct cx23885_fmt formats[] = {
+ {
+#if 0
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+#endif
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }
+};
+#endif
static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
{
@@ -139,7 +158,12 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
if (formats[i].fourcc == fourcc)
return formats+i;
- printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+ printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
+ (fourcc & 0xff),
+ ((fourcc >> 8) & 0xff),
+ ((fourcc >> 16) & 0xff),
+ ((fourcc >> 24) & 0xff)
+ );
return NULL;
}
@@ -171,7 +195,7 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.id = V4L2_CID_CONTRAST,
.name = "Contrast",
.minimum = 0,
- .maximum = 0xff,
+ .maximum = 0x7f,
.step = 1,
.default_value = 0x3f,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -184,10 +208,10 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.v = {
.id = V4L2_CID_HUE,
.name = "Hue",
- .minimum = 0,
- .maximum = 0xff,
+ .minimum = -127,
+ .maximum = 128,
.step = 1,
- .default_value = 0x7f,
+ .default_value = 0x0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 128,
@@ -202,9 +226,9 @@ static struct cx23885_ctrl cx23885_ctls[] = {
.id = V4L2_CID_SATURATION,
.name = "Saturation",
.minimum = 0,
- .maximum = 0xff,
+ .maximum = 0x7f,
.step = 1,
- .default_value = 0x7f,
+ .default_value = 0x3f,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 0,
@@ -258,8 +282,8 @@ static const u32 *ctrl_classes[] = {
NULL
};
-static void cx23885_video_wakeup(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q, u32 count)
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_buffer *buf;
int bc;
@@ -393,6 +417,71 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
mutex_unlock(&dev->lock);
}
+static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
+{
+ /* 8 bit registers, 8 bit values */
+ u8 buf[] = { reg, data };
+
+ struct i2c_msg msg = { .addr = 0x98 >> 1,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
+{
+ /* 8 bit registers, 8 bit values */
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+
+ struct i2c_msg msg[] = {
+ { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
+ if (ret != 2)
+ printk(KERN_ERR "%s() error\n", __func__);
+
+ return b1[0];
+}
+
+static void cx23885_flatiron_dump(struct cx23885_dev *dev)
+{
+ int i;
+ dprintk(1, "Flatiron dump\n");
+ for (i = 0; i < 0x24; i++) {
+ dprintk(1, "FI[%02x] = %02x\n", i,
+ cx23885_flatiron_read(dev, i));
+ }
+}
+
+static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
+{
+ u8 val;
+ dprintk(1, "%s(input = %d)\n", __func__, input);
+
+ if (input == 1)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
+ else if (input == 2)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
+ else
+ return -EINVAL;
+
+ val |= 0x20; /* Enable clock to delta-sigma and dec filter */
+
+ cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
+
+ /* Wake up */
+ cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
+
+ if (video_debug)
+ cx23885_flatiron_dump(dev);
+
+ return 0;
+}
+
static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
{
dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
@@ -413,27 +502,59 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
INPUT(input)->vmux, 0, 0);
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
+ (dev->board == CX23885_BOARD_MPX885)) {
+ /* Configure audio routing */
+ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
+ INPUT(input)->amux, 0, 0);
+
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ }
+
return 0;
}
-/* ------------------------------------------------------------------ */
-static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
- unsigned int height, enum v4l2_field field)
+static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
{
- dprintk(1, "%s()\n", __func__);
+ dprintk(1, "%s(input=%d)\n", __func__, input);
+
+ /* The baseband video core of the cx23885 has two audio inputs.
+ * LR1 and LR2. In almost every single case so far only HVR1xxx
+ * cards we've only ever supported LR1. Time to support LR2,
+ * which is available via the optional white breakout header on
+ * the board.
+ * We'll use a could of existing enums in the card struct to allow
+ * devs to specify which baseband input they need, or just default
+ * to what we've always used.
+ */
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ else {
+ /* Not specifically defined, assume the default. */
+ cx23885_flatiron_mux(dev, 1);
+ }
+
return 0;
}
+/* ------------------------------------------------------------------ */
static int cx23885_start_video_dma(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
{
dprintk(1, "%s()\n", __func__);
+ /* Stop the dma/fifo before we tamper with it's risc programs */
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
/* setup fifo + format */
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
buf->bpl, buf->risc.dma);
- cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
/* reset counter */
cx_write(VID_A_GPCNT_CTL, 3);
@@ -748,7 +869,7 @@ static int video_open(struct file *file)
fh->type = type;
fh->width = 320;
fh->height = 240;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
&dev->pci->dev, &dev->slock,
@@ -757,6 +878,14 @@ static int video_open(struct file *file)
sizeof(struct cx23885_buffer),
fh, NULL);
+ videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+
dprintk(1, "post videobuf_queue_init()\n");
return 0;
@@ -884,8 +1013,9 @@ static int cx23885_get_control(struct cx23885_dev *dev,
static int cx23885_set_control(struct cx23885_dev *dev,
struct v4l2_control *ctl)
{
- dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
- " (disabled - no action)\n", __func__);
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
+ call_all(dev, core, s_ctrl, ctl);
+
return 0;
}
@@ -1059,13 +1189,22 @@ static int vidioc_streamon(struct file *file, void *priv,
struct cx23885_dev *dev = fh->dev;
dprintk(1, "%s()\n", __func__);
- if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
if (unlikely(i != fh->type))
return -EINVAL;
if (unlikely(!res_get(dev, fh, get_resource(fh))))
return -EBUSY;
+
+ /* Don't start VBI streaming unless vida streaming
+ * has already started.
+ */
+ if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
+ ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
+ return -EINVAL;
+
return videobuf_streamon(get_queue(fh));
}
@@ -1076,7 +1215,8 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
int err, res;
dprintk(1, "%s()\n", __func__);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
if (i != fh->type)
return -EINVAL;
@@ -1119,7 +1259,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
dprintk(1, "%s()\n", __func__);
n = i->index;
- if (n >= 4)
+ if (n >= MAX_CX23885_INPUT)
return -EINVAL;
if (0 == INPUT(n)->type)
@@ -1133,6 +1273,11 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
i->type = V4L2_INPUT_TYPE_TUNER;
i->std = CX23885_NORMS;
}
+
+ /* Two selectable audio inputs for non-tv inputs */
+ if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
+ i->audioset = 0x3;
+
return 0;
}
@@ -1159,13 +1304,20 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
dprintk(1, "%s(%d)\n", __func__, i);
- if (i >= 4) {
+ if (i >= MAX_CX23885_INPUT) {
dprintk(1, "%s() -EINVAL\n", __func__);
return -EINVAL;
}
+ if (INPUT(i)->type == 0)
+ return -EINVAL;
+
mutex_lock(&dev->lock);
cx23885_video_mux(dev, i);
+
+ /* By default establish the default audio input for the card also */
+ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
+ cx23885_audio_mux(dev, i);
mutex_unlock(&dev->lock);
return 0;
}
@@ -1185,6 +1337,64 @@ static int vidioc_log_status(struct file *file, void *priv)
return 0;
}
+static int cx23885_query_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ static const char *iname[] = {
+ [0] = "Baseband L/R 1",
+ [1] = "Baseband L/R 2",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ strcpy(i->name, iname[n]);
+ i->capability = V4L2_AUDCAP_STEREO;
+ i->mode = V4L2_AUDMODE_AVL;
+ return 0;
+
+}
+
+static int vidioc_enum_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_g_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ i->index = dev->audinput;
+ dprintk(1, "%s(input=%d)\n", __func__, i->index);
+
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_s_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ if (i->index >= 2)
+ return -EINVAL;
+
+ dprintk(1, "%s(%d)\n", __func__, i->index);
+
+ dev->audinput = i->index;
+
+ /* Skip the audio defaults from the cards struct, caller wants
+ * directly touch the audio mux hardware. */
+ cx23885_flatiron_mux(dev, dev->audinput + 1);
+ return 0;
+}
+
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
@@ -1221,10 +1431,8 @@ 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;
- t->signal = 0xffff ; /* LOCKED */
+
+ call_all(dev, tuner, g_tuner, t);
return 0;
}
@@ -1237,6 +1445,9 @@ static int vidioc_s_tuner(struct file *file, void *priv,
return -EINVAL;
if (0 != t->index)
return -EINVAL;
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
return 0;
}
@@ -1302,10 +1513,6 @@ static void cx23885_vid_timeout(unsigned long data)
struct cx23885_buffer *buf;
unsigned long flags;
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
-
- cx_clear(VID_A_DMA_CTL, 0x11);
-
spin_lock_irqsave(&dev->slock, flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next,
@@ -1313,7 +1520,7 @@ static void cx23885_vid_timeout(unsigned long data)
list_del(&buf->vb.queue);
buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
- printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
+ printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
dev->name, buf, buf->vb.i,
(unsigned long)buf->risc.dma);
}
@@ -1329,27 +1536,43 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
mask = cx_read(VID_A_INT_MSK);
if (0 == (status & mask))
return handled;
+
cx_write(VID_A_INT_STAT, status);
- dprintk(2, "%s() status = 0x%08x\n", __func__, status);
- /* risc op code error */
- if (status & (1 << 16)) {
- printk(KERN_WARNING "%s/0: video risc op code error\n",
- dev->name);
- cx_clear(VID_A_DMA_CTL, 0x11);
- cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+ /* risc op code error, fifo overflow or line sync detection error */
+ if ((status & VID_BC_MSK_OPC_ERR) ||
+ (status & VID_BC_MSK_SYNC) ||
+ (status & VID_BC_MSK_OF)) {
+
+ if (status & VID_BC_MSK_OPC_ERR) {
+ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+ VID_BC_MSK_OPC_ERR);
+ printk(KERN_WARNING "%s: video risc op code error\n",
+ dev->name);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[SRAM_CH01]);
+ }
+
+ if (status & VID_BC_MSK_SYNC)
+ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
+ "video lines miss-match\n",
+ VID_BC_MSK_SYNC);
+
+ if (status & VID_BC_MSK_OF)
+ dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
+ VID_BC_MSK_OF);
+
}
- /* risc1 y */
- if (status & 0x01) {
+ /* Video */
+ if (status & VID_BC_MSK_RISCI1) {
spin_lock(&dev->slock);
count = cx_read(VID_A_GPCNT);
cx23885_video_wakeup(dev, &dev->vidq, count);
spin_unlock(&dev->slock);
handled++;
}
- /* risc2 y */
- if (status & 0x10) {
+ if (status & VID_BC_MSK_RISCI2) {
dprintk(2, "stopper video\n");
spin_lock(&dev->slock);
cx23885_restart_video_queue(dev, &dev->vidq);
@@ -1357,6 +1580,9 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
handled++;
}
+ /* Allow the VBI framework to process it's payload */
+ handled += cx23885_vbi_irq(dev, status);
+
return handled;
}
@@ -1405,6 +1631,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_register = cx23885_g_register,
.vidioc_s_register = cx23885_s_register,
#endif
+ .vidioc_enumaudio = vidioc_enum_audinput,
+ .vidioc_g_audio = vidioc_g_audinput,
+ .vidioc_s_audio = vidioc_s_audinput,
};
static struct video_device cx23885_vbi_template;
@@ -1429,6 +1658,14 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
dprintk(1, "%s()\n", __func__);
cx23885_irq_remove(dev, 0x01);
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+ }
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
@@ -1438,6 +1675,9 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
}
+
+ if (dev->audio_dev)
+ cx23885_audio_unregister(dev);
}
int cx23885_video_register(struct cx23885_dev *dev)
@@ -1463,7 +1703,14 @@ int cx23885_video_register(struct cx23885_dev *dev)
cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
VID_A_DMA_CTL, 0x11, 0x00);
- /* Don't enable VBI yet */
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx23885_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+ VID_A_DMA_CTL, 0x22, 0x00);
cx23885_irq_add_enable(dev, 0x01);
@@ -1504,8 +1751,7 @@ int cx23885_video_register(struct cx23885_dev *dev)
}
}
-
- /* register v4l devices */
+ /* register Video device */
dev->video_dev = cx23885_vdev_init(dev, dev->pci,
&cx23885_video_template, "video");
err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
@@ -1515,13 +1761,31 @@ int cx23885_video_register(struct cx23885_dev *dev)
dev->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
dev->name, video_device_node_name(dev->video_dev));
+
+ /* register VBI device */
+ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_vbi_template, "vbi");
+ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register vbi device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ /* Register ALSA audio device */
+ dev->audio_dev = cx23885_audio_register(dev);
+
/* initial device configuration */
mutex_lock(&dev->lock);
cx23885_set_tvnorm(dev, dev->tvnorm);
init_controls(dev);
cx23885_video_mux(dev, 0);
+ cx23885_audio_mux(dev, 0);
mutex_unlock(&dev->lock);
return 0;
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index d86bc0b1317b..b49036fe3ffd 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -86,6 +86,7 @@
#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
+#define CX23885_BOARD_MPX885 32
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -192,6 +193,7 @@ struct cx23885_buffer {
struct cx23885_input {
enum cx23885_itype type;
unsigned int vmux;
+ unsigned int amux;
u32 gpio0, gpio1, gpio2, gpio3;
};
@@ -318,6 +320,34 @@ struct cx23885_kernel_ir {
struct rc_dev *rc;
};
+struct cx23885_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx23885_audio_dev {
+ struct cx23885_dev *dev;
+
+ struct pci_dev *pci;
+
+ struct snd_card *card;
+
+ spinlock_t lock;
+
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx23885_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+
struct cx23885_dev {
atomic_t refcount;
struct v4l2_device v4l2_dev;
@@ -362,6 +392,7 @@ struct cx23885_dev {
/* Analog video */
u32 resources;
unsigned int input;
+ unsigned int audinput; /* Selectable audio input */
u32 tvaudio;
v4l2_std_id tvnorm;
unsigned int tuner_type;
@@ -400,6 +431,9 @@ struct cx23885_dev {
atomic_t v4l_reader_count;
struct cx23885_tvnorm encodernorm;
+ /* Analog raw audio */
+ struct cx23885_audio_dev *audio_dev;
+
};
static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
@@ -478,6 +512,11 @@ extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines);
+extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc, struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
void cx23885_cancel_buffers(struct cx23885_tsport *port);
extern int cx23885_restart_queue(struct cx23885_tsport *port,
@@ -533,6 +572,8 @@ extern void cx23885_free_buffer(struct videobuf_queue *q,
extern int cx23885_video_register(struct cx23885_dev *dev);
extern void cx23885_video_unregister(struct cx23885_dev *dev);
extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+extern void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count);
/* ----------------------------------------------------------- */
/* cx23885-vbi.c */
@@ -540,6 +581,9 @@ extern int cx23885_vbi_fmt(struct file *file, void *priv,
struct v4l2_format *f);
extern void cx23885_vbi_timeout(unsigned long data);
extern struct videobuf_queue_ops cx23885_vbi_qops;
+extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q);
+extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
@@ -563,6 +607,18 @@ extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
+/* ----------------------------------------------------------- */
+/* cx23885-alsa.c */
+extern struct cx23885_audio_dev *cx23885_audio_register(
+ struct cx23885_dev *dev);
+extern void cx23885_audio_unregister(struct cx23885_dev *dev);
+extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
+extern int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines,
+ unsigned int lpi);
/* ----------------------------------------------------------- */
/* tv norms */
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index e37be6fcf67d..bb1ce346425d 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -673,7 +673,7 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
unsigned int i, n;
union cx23888_ir_fifo_rec *p;
- unsigned u, v;
+ unsigned u, v, w;
n = count / sizeof(union cx23888_ir_fifo_rec)
* sizeof(union cx23888_ir_fifo_rec);
@@ -692,11 +692,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
/* Assume RTO was because of no IR light input */
u = 0;
- v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+ w = 1;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
if (invert)
u = u ? 0 : 1;
+ w = 0;
}
v = (unsigned) pulse_width_count_to_ns(
@@ -707,9 +708,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
init_ir_raw_event(&p->ir_core_data);
p->ir_core_data.pulse = u;
p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
- v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
- v, u ? "mark" : "space");
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
}
return 0;
}
diff --git a/drivers/staging/cx25821/Kconfig b/drivers/media/video/cx25821/Kconfig
index 5f6b54213713..5f6b54213713 100644
--- a/drivers/staging/cx25821/Kconfig
+++ b/drivers/media/video/cx25821/Kconfig
diff --git a/drivers/staging/cx25821/Makefile b/drivers/media/video/cx25821/Makefile
index aedde18c68f9..aedde18c68f9 100644
--- a/drivers/staging/cx25821/Makefile
+++ b/drivers/media/video/cx25821/Makefile
diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c
index 09e99de5fd21..09e99de5fd21 100644
--- a/drivers/staging/cx25821/cx25821-alsa.c
+++ b/drivers/media/video/cx25821/cx25821-alsa.c
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c
index c20d6dece154..c20d6dece154 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.h b/drivers/media/video/cx25821/cx25821-audio-upstream.h
index af2ae7c5815a..af2ae7c5815a 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.h
diff --git a/drivers/staging/cx25821/cx25821-audio.h b/drivers/media/video/cx25821/cx25821-audio.h
index 8eb55b7b88cb..8eb55b7b88cb 100644
--- a/drivers/staging/cx25821/cx25821-audio.h
+++ b/drivers/media/video/cx25821/cx25821-audio.h
diff --git a/drivers/staging/cx25821/cx25821-biffuncs.h b/drivers/media/video/cx25821/cx25821-biffuncs.h
index 9326a7c729ec..9326a7c729ec 100644
--- a/drivers/staging/cx25821/cx25821-biffuncs.h
+++ b/drivers/media/video/cx25821/cx25821-biffuncs.h
diff --git a/drivers/staging/cx25821/cx25821-cards.c b/drivers/media/video/cx25821/cx25821-cards.c
index 6ace60313b49..6ace60313b49 100644
--- a/drivers/staging/cx25821/cx25821-cards.c
+++ b/drivers/media/video/cx25821/cx25821-cards.c
diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
index a7fa38f9594e..a7fa38f9594e 100644
--- a/drivers/staging/cx25821/cx25821-core.c
+++ b/drivers/media/video/cx25821/cx25821-core.c
diff --git a/drivers/staging/cx25821/cx25821-gpio.c b/drivers/media/video/cx25821/cx25821-gpio.c
index 29e43b03c85e..29e43b03c85e 100644
--- a/drivers/staging/cx25821/cx25821-gpio.c
+++ b/drivers/media/video/cx25821/cx25821-gpio.c
diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
index 4d3d0ce40785..4d3d0ce40785 100644
--- a/drivers/staging/cx25821/cx25821-i2c.c
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
diff --git a/drivers/staging/cx25821/cx25821-medusa-defines.h b/drivers/media/video/cx25821/cx25821-medusa-defines.h
index 60d197f57556..60d197f57556 100644
--- a/drivers/staging/cx25821/cx25821-medusa-defines.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-defines.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-reg.h b/drivers/media/video/cx25821/cx25821-medusa-reg.h
index 1c1c228352d1..1c1c228352d1 100644
--- a/drivers/staging/cx25821/cx25821-medusa-reg.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-reg.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
index fc780d0908dc..fc780d0908dc 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.h b/drivers/media/video/cx25821/cx25821-medusa-video.h
index 6175e0961855..6175e0961855 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.h
diff --git a/drivers/staging/cx25821/cx25821-reg.h b/drivers/media/video/cx25821/cx25821-reg.h
index a3fc25a4dc0b..a3fc25a4dc0b 100644
--- a/drivers/staging/cx25821/cx25821-reg.h
+++ b/drivers/media/video/cx25821/cx25821-reg.h
diff --git a/drivers/staging/cx25821/cx25821-sram.h b/drivers/media/video/cx25821/cx25821-sram.h
index 5f05d153bc4d..5f05d153bc4d 100644
--- a/drivers/staging/cx25821/cx25821-sram.h
+++ b/drivers/media/video/cx25821/cx25821-sram.h
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
index 2a724ddfa53f..2a724ddfa53f 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
index d42dab59b663..d42dab59b663 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c
index c0b80068f468..c0b80068f468 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.c
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.h b/drivers/media/video/cx25821/cx25821-video-upstream.h
index 268ec8aa6a61..268ec8aa6a61 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.h
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
index 084fc0899e13..4d6907cda75b 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/media/video/cx25821/cx25821-video.c
@@ -1312,7 +1312,7 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
return err;
}
- if (i > 2) {
+ if (i >= CX25821_NR_INPUT) {
dprintk(1, "%s(): -EINVAL\n", __func__);
return -EINVAL;
}
diff --git a/drivers/staging/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
index d0d9538ca5b3..d0d9538ca5b3 100644
--- a/drivers/staging/cx25821/cx25821-video.h
+++ b/drivers/media/video/cx25821/cx25821-video.h
diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
index db2615b2bac3..2d2d00932823 100644
--- a/drivers/staging/cx25821/cx25821.h
+++ b/drivers/media/video/cx25821/cx25821.h
@@ -98,6 +98,7 @@
#define CX25821_BOARD_CONEXANT_ATHENA10 1
#define MAX_VID_CHANNEL_NUM 12
#define VID_CHANNEL_NUM 8
+#define CX25821_NR_INPUT 2
struct cx25821_fmt {
char *name;
@@ -196,7 +197,7 @@ struct cx25821_board {
unsigned char radio_addr;
u32 clk_freq;
- struct cx25821_input input[2];
+ struct cx25821_input input[CX25821_NR_INPUT];
};
struct cx25821_subid {
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
index 2ee96d3973b8..dc40dde2e0c8 100644
--- a/drivers/media/video/cx25840/Makefile
+++ b/drivers/media/video/cx25840/Makefile
@@ -3,4 +3,4 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
-EXTRA_CFLAGS += -Idrivers/media/video
+ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 34b96c7cfd62..005f11093642 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -480,6 +480,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
static void set_volume(struct i2c_client *client, int volume)
{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
/* Convert the volume to msp3400 values (0-127) */
@@ -495,7 +496,14 @@ static void set_volume(struct i2c_client *client, int volume)
}
/* PATH1_VOLUME */
- cx25840_write(client, 0x8d4, 228 - (vol * 2));
+ if (is_cx2388x(state)) {
+ /* for cx23885 volume doesn't work,
+ * the calculation always results in
+ * e4 regardless.
+ */
+ cx25840_write(client, 0x8d4, volume);
+ } else
+ cx25840_write(client, 0x8d4, 228 - (vol * 2));
}
static void set_balance(struct i2c_client *client, int balance)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index b7ee2ae70583..cd9976408ab3 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -702,6 +702,13 @@ static void cx231xx_initialize(struct i2c_client *client)
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* CC raw enable */
+ cx25840_write(client, 0x404, 0x0b);
+
+ /* CC on */
+ cx25840_write(client, 0x42f, 0x66);
+ cx25840_write4(client, 0x474, 0x1e1e601a);
}
/* ----------------------------------------------------------------------- */
@@ -1067,6 +1074,18 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_write(client, 0x919, 0x01);
}
+ if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) ||
+ (aud_input == CX25840_AUDIO6))) {
+ /* Configure audio from LR1 or LR2 input */
+ cx25840_write4(client, 0x910, 0);
+ cx25840_write4(client, 0x8d0, 0x63073);
+ } else
+ if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) {
+ /* Configure audio from tuner/sif input */
+ cx25840_write4(client, 0x910, 0x12b000c9);
+ cx25840_write4(client, 0x8d0, 0x1f063870);
+ }
+
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
index 7eb79af28aa3..b718a3a4bed3 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -668,7 +668,7 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
u16 divider;
unsigned int i, n;
union cx25840_ir_fifo_rec *p;
- unsigned u, v;
+ unsigned u, v, w;
if (ir_state == NULL)
return -ENODEV;
@@ -694,11 +694,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
/* Assume RTO was because of no IR light input */
u = 0;
- v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ w = 1;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
if (invert)
u = u ? 0 : 1;
+ w = 0;
}
v = (unsigned) pulse_width_count_to_ns(
@@ -709,9 +710,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
init_ir_raw_event(&p->ir_core_data);
p->ir_core_data.pulse = u;
p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
- v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n",
- v, u ? "mark" : "space");
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
}
return 0;
}
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 5b7e26761f0a..c1a2785ba243 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index fbcaa1c5b09d..fbfdd8067937 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -636,9 +636,6 @@ int cx88_reset(struct cx88_core *core)
cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
- /* set default notch filter */
- cx_andor(MO_HTOTAL, 0x1800, (HLNotchFilter4xFsc << 11));
-
/* Reset on-board parts */
cx_write(MO_SRST_IO, 0);
msleep(10);
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 60d28fdd7791..921c56d115d6 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -266,7 +266,7 @@ static const struct cx88_ctrl cx8800_ctls[] = {
.id = V4L2_CID_BAND_STOP_FILTER,
.name = "Notch filter",
.minimum = 0,
- .maximum = 3,
+ .maximum = 1,
.step = 1,
.default_value = 0x0,
.type = V4L2_CTRL_TYPE_INTEGER,
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
index 7f1d83a6d575..8588a86d9b45 100644
--- a/drivers/media/video/davinci/vpbe_display.c
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -43,7 +43,6 @@
static int debug;
-#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2)
#define VPBE_DEFAULT_NUM_BUFS 3
module_param(debug, int, 0644);
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c
index 5352884998f5..ceccf4302518 100644
--- a/drivers/media/video/davinci/vpbe_osd.c
+++ b/drivers/media/video/davinci/vpbe_osd.c
@@ -1162,7 +1162,7 @@ static int osd_probe(struct platform_device *pdev)
goto free_mem;
}
osd->osd_base_phys = res->start;
- osd->osd_size = res->end - res->start + 1;
+ osd->osd_size = resource_size(res);
if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
MODULE_NAME)) {
dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 281ee427c2ab..f6f622e123bd 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -41,6 +41,8 @@ config VIDEO_EM28XX_DVB
select DVB_CXD2820R if !DVB_FE_CUSTOMISE
select DVB_DRXK if !DVB_FE_CUSTOMISE
select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
+ select DVB_TDA10071 if !DVB_FE_CUSTOMISE
+ select DVB_A8293 if !DVB_FE_CUSTOMISE
select VIDEOBUF_DVB
---help---
This adds support for DVB cards based on the
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 38aaa004f57d..2abdf76c5203 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -9,8 +9,8 @@ obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 3e3959fee419..9b747c266afa 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -60,7 +60,7 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
static unsigned long em28xx_devused;
struct em28xx_hash_table {
@@ -317,6 +317,25 @@ static struct em28xx_reg_seq terratec_h5_digital[] = {
};
#endif
+/* 2013:024f PCTV DVB-S2 Stick 460e
+ * GPIO_0 - POWER_ON
+ * GPIO_1 - BOOST
+ * GPIO_2 - VUV_LNB (red LED)
+ * GPIO_3 - EXT_12V
+ * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+ * GPIO_5 - INT_LNB
+ * GPIO_6 - RESET_DEM
+ * GPIO_7 - LED (green LED)
+ */
+static struct em28xx_reg_seq pctv_460e[] = {
+ {EM2874_R80_GPIO, 0x01, 0xff, 50},
+ {0x0d, 0xff, 0xff, 50},
+ {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
+ {0x0d, 0x42, 0xff, 50},
+ {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
+ { -1, -1, -1, -1},
+};
+
/*
* Board definitions
*/
@@ -1810,6 +1829,17 @@ struct em28xx_board em28xx_boards[] = {
.has_dvb = 1,
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+ * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+ [EM28174_BOARD_PCTV_460E] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .name = "PCTV DVB-S2 Stick (460e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_460e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1893,6 +1923,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2860_BOARD_TERRATEC_AV350 },
{ USB_DEVICE(0x0ccd, 0x0096),
.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x10AF),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
{ USB_DEVICE(0x0fd9, 0x0033),
.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
{ USB_DEVICE(0x185b, 0x2870),
@@ -1941,6 +1973,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2870_BOARD_KWORLD_A340 },
{ USB_DEVICE(0x2013, 0x024f),
.driver_info = EM28174_BOARD_PCTV_290E },
+ { USB_DEVICE(0x2013, 0x024c),
+ .driver_info = EM28174_BOARD_PCTV_460E },
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2768,9 +2802,9 @@ static void flush_request_modules(struct em28xx *dev)
#endif /* CONFIG_MODULES */
/*
- * em28xx_realease_resources()
+ * em28xx_release_resources()
* unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
+ * called when the device gets disconnected or at module unload
*/
void em28xx_release_resources(struct em28xx *dev)
{
@@ -2784,8 +2818,6 @@ void em28xx_release_resources(struct em28xx *dev)
em28xx_release_analog_resources(dev);
- em28xx_remove_from_devlist(dev);
-
em28xx_i2c_unregister(dev);
v4l2_device_unregister(&dev->v4l2_dev);
@@ -2793,7 +2825,7 @@ void em28xx_release_resources(struct em28xx *dev)
usb_put_dev(dev->udev);
/* Mark device as unused */
- em28xx_devused &= ~(1 << dev->devno);
+ clear_bit(dev->devno, &em28xx_devused);
};
/*
@@ -2806,7 +2838,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
{
struct em28xx *dev = *devhandle;
int retval;
- int errCode;
dev->udev = udev;
mutex_init(&dev->ctrl_urb_lock);
@@ -2883,10 +2914,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
}
if (dev->is_audio_only) {
- errCode = em28xx_audio_setup(dev);
- if (errCode)
+ retval = em28xx_audio_setup(dev);
+ if (retval)
return -ENODEV;
- em28xx_add_into_devlist(dev);
em28xx_init_extension(dev);
return 0;
@@ -2903,7 +2933,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* Resets I2C speed */
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed!"
+ em28xx_errdev("%s: em28xx_write_reg failed!"
" retval [%d]\n",
__func__, retval);
return retval;
@@ -2917,12 +2947,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
}
/* register i2c bus */
- errCode = em28xx_i2c_register(dev);
- if (errCode < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_i2c_register(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+ __func__, retval);
+ goto unregister_dev;
}
/*
@@ -2936,11 +2965,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_card_setup(dev);
/* Configure audio */
- errCode = em28xx_audio_setup(dev);
- if (errCode < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
- __func__, errCode);
+ retval = em28xx_audio_setup(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+ __func__, retval);
+ goto fail;
}
/* wake i2c devices */
@@ -2954,41 +2983,41 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
if (dev->board.has_msp34xx) {
/* Send a reset to other chips via gpio */
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - "
- "msp34xx(1) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(1) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
}
msleep(3);
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - "
- "msp34xx(2) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(2) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
}
msleep(3);
}
- em28xx_add_into_devlist(dev);
-
retval = em28xx_register_analog_devices(dev);
if (retval < 0) {
- em28xx_release_resources(dev);
- goto fail_reg_devices;
+ goto fail;
}
- em28xx_init_extension(dev);
-
/* Save some power by putting tuner to sleep */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
return 0;
-fail_reg_devices:
+fail:
+ em28xx_i2c_unregister(dev);
+
+unregister_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
return retval;
}
@@ -3015,8 +3044,16 @@ static int em28xx_usb_probe(struct usb_interface *interface,
udev = usb_get_dev(interface_to_usbdev(interface));
/* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- em28xx_devused |= 1<<nr;
+ do {
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ retval = -ENOMEM;
+ goto err_no_slot;
+ }
+ } while (test_and_set_bit(nr, &em28xx_devused));
/* Don't register audio interfaces */
if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
@@ -3027,7 +3064,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
ifnum,
interface->altsetting[0].desc.bInterfaceClass);
- em28xx_devused &= ~(1<<nr);
retval = -ENODEV;
goto err;
}
@@ -3076,7 +3112,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
em28xx_err(DRIVER_NAME " This is an anciliary "
"interface not used by the driver\n");
- em28xx_devused &= ~(1<<nr);
retval = -ENODEV;
goto err;
}
@@ -3132,29 +3167,19 @@ static int em28xx_usb_probe(struct usb_interface *interface,
printk(DRIVER_NAME ": Device initialization failed.\n");
printk(DRIVER_NAME ": Device must be connected to a high-speed"
" USB 2.0 port.\n");
- em28xx_devused &= ~(1<<nr);
retval = -ENODEV;
goto err;
}
- if (nr >= EM28XX_MAXBOARDS) {
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- em28xx_devused &= ~(1<<nr);
- retval = -ENOMEM;
- goto err;
- }
-
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
em28xx_err(DRIVER_NAME ": out of memory!\n");
- em28xx_devused &= ~(1<<nr);
retval = -ENOMEM;
goto err;
}
- snprintf(dev->name, 29, "em28xx #%d", nr);
+ snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
dev->alt = -1;
@@ -3177,7 +3202,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (dev->alt_max_pkt_size == NULL) {
em28xx_errdev("out of memory!\n");
- em28xx_devused &= ~(1<<nr);
kfree(dev);
retval = -ENOMEM;
goto err;
@@ -3204,8 +3228,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
mutex_lock(&dev->lock);
retval = em28xx_init_dev(&dev, udev, interface, nr);
if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
mutex_unlock(&dev->lock);
+ kfree(dev->alt_max_pkt_size);
kfree(dev);
goto err;
}
@@ -3217,15 +3241,26 @@ static int em28xx_usb_probe(struct usb_interface *interface,
*/
mutex_unlock(&dev->lock);
+ /*
+ * These extensions can be modules. If the modules are already
+ * loaded then we can initialise the device now, otherwise we
+ * will initialise it when the modules load instead.
+ */
+ em28xx_init_extension(dev);
+
return 0;
err:
+ clear_bit(nr, &em28xx_devused);
+
+err_no_slot:
+ usb_put_dev(udev);
return retval;
}
/*
* em28xx_usb_disconnect()
- * called when the device gets diconencted
+ * called when the device gets disconnected
* video device will be unregistered on v4l2_close in case it is still open
*/
static void em28xx_usb_disconnect(struct usb_interface *interface)
@@ -3273,10 +3308,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
em28xx_release_resources(dev);
}
- em28xx_close_extension(dev);
-
mutex_unlock(&dev->lock);
+ em28xx_close_extension(dev);
+
if (!dev->users) {
kfree(dev->alt_max_pkt_size);
kfree(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 57b1b5c6d885..804a4ab47ac6 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -1184,25 +1184,6 @@ static LIST_HEAD(em28xx_devlist);
static DEFINE_MUTEX(em28xx_devlist_mutex);
/*
- * em28xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-void em28xx_remove_from_devlist(struct em28xx *dev)
-{
- mutex_lock(&em28xx_devlist_mutex);
- list_del(&dev->devlist);
- mutex_unlock(&em28xx_devlist_mutex);
-};
-
-void em28xx_add_into_devlist(struct em28xx *dev)
-{
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- mutex_unlock(&em28xx_devlist_mutex);
-};
-
-/*
* Extension interface
*/
@@ -1217,8 +1198,8 @@ int em28xx_register_extension(struct em28xx_ops *ops)
list_for_each_entry(dev, &em28xx_devlist, devlist) {
ops->init(dev);
}
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
return 0;
}
EXPORT_SYMBOL(em28xx_register_extension);
@@ -1231,36 +1212,34 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
list_for_each_entry(dev, &em28xx_devlist, devlist) {
ops->fini(dev);
}
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
list_del(&ops->next);
mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
}
EXPORT_SYMBOL(em28xx_unregister_extension);
void em28xx_init_extension(struct em28xx *dev)
{
- struct em28xx_ops *ops = NULL;
+ const struct em28xx_ops *ops = NULL;
mutex_lock(&em28xx_devlist_mutex);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->init)
- ops->init(dev);
- }
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
}
mutex_unlock(&em28xx_devlist_mutex);
}
void em28xx_close_extension(struct em28xx *dev)
{
- struct em28xx_ops *ops = NULL;
+ const struct em28xx_ops *ops = NULL;
mutex_lock(&em28xx_devlist_mutex);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->fini)
- ops->fini(dev);
- }
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
}
+ list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index e5916dee4094..cef7a2d409cb 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -42,6 +42,8 @@
#include "cxd2820r.h"
#include "tda18271c2dd.h"
#include "drxk.h"
+#include "tda10071.h"
+#include "a8293.h"
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -122,7 +124,7 @@ static inline void print_err_status(struct em28xx *dev,
}
}
-static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
+static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
{
int i;
@@ -155,7 +157,7 @@ static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
return 0;
}
-static int start_streaming(struct em28xx_dvb *dvb)
+static int em28xx_start_streaming(struct em28xx_dvb *dvb)
{
int rc;
struct em28xx *dev = dvb->adapter.priv;
@@ -175,10 +177,10 @@ static int start_streaming(struct em28xx_dvb *dvb)
return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
EM28XX_DVB_NUM_BUFS, max_dvb_packet_size,
- dvb_isoc_copy);
+ em28xx_dvb_isoc_copy);
}
-static int stop_streaming(struct em28xx_dvb *dvb)
+static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
{
struct em28xx *dev = dvb->adapter.priv;
@@ -189,7 +191,7 @@ static int stop_streaming(struct em28xx_dvb *dvb)
return 0;
}
-static int start_feed(struct dvb_demux_feed *feed)
+static int em28xx_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct em28xx_dvb *dvb = demux->priv;
@@ -203,7 +205,7 @@ static int start_feed(struct dvb_demux_feed *feed)
rc = dvb->nfeeds;
if (dvb->nfeeds == 1) {
- ret = start_streaming(dvb);
+ ret = em28xx_start_streaming(dvb);
if (ret < 0)
rc = ret;
}
@@ -212,7 +214,7 @@ static int start_feed(struct dvb_demux_feed *feed)
return rc;
}
-static int stop_feed(struct dvb_demux_feed *feed)
+static int em28xx_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct em28xx_dvb *dvb = demux->priv;
@@ -222,7 +224,7 @@ static int stop_feed(struct dvb_demux_feed *feed)
dvb->nfeeds--;
if (0 == dvb->nfeeds)
- err = stop_streaming(dvb);
+ err = em28xx_stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
@@ -380,7 +382,7 @@ static void terratec_h5_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_h5_end);
};
-static int mt352_terratec_xs_init(struct dvb_frontend *fe)
+static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
{
/* Values extracted from a USB trace of the Terratec Windows driver */
static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
@@ -412,7 +414,7 @@ static struct mt352_config terratec_xs_mt352_cfg = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
.if2 = 45600,
- .demod_init = mt352_terratec_xs_init,
+ .demod_init = em28xx_mt352_terratec_xs_init,
};
static struct tda10023_config em28xx_tda10023_config = {
@@ -438,11 +440,25 @@ static struct cxd2820r_config em28xx_cxd2820r_config = {
static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
.output_opt = TDA18271_OUTPUT_LT_OFF,
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static const struct tda10071_config em28xx_tda10071_config = {
+ .i2c_address = 0x55, /* (0xaa >> 1) */
+ .i2c_wr_max = 64,
+ .ts_mode = TDA10071_TS_SERIAL,
+ .spec_inv = 0,
+ .xtal = 40444000, /* 40.444 MHz */
+ .pll_multiplier = 20,
+};
+
+static const struct a8293_config em28xx_a8293_config = {
+ .i2c_addr = 0x08, /* (0x10 >> 1) */
};
/* ------------------------------------------------------------------ */
-static int attach_xc3028(u8 addr, struct em28xx *dev)
+static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
{
struct dvb_frontend *fe;
struct xc2028_config cfg;
@@ -472,10 +488,8 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
/* ------------------------------------------------------------------ */
-static int register_dvb(struct em28xx_dvb *dvb,
- struct module *module,
- struct em28xx *dev,
- struct device *device)
+static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
+ struct em28xx *dev, struct device *device)
{
int result;
@@ -522,8 +536,8 @@ static int register_dvb(struct em28xx_dvb *dvb,
dvb->demux.priv = dvb;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
- dvb->demux.start_feed = start_feed;
- dvb->demux.stop_feed = stop_feed;
+ dvb->demux.start_feed = em28xx_start_feed;
+ dvb->demux.stop_feed = em28xx_stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
@@ -591,7 +605,7 @@ fail_adapter:
return result;
}
-static void unregister_dvb(struct em28xx_dvb *dvb)
+static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
{
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
@@ -607,9 +621,9 @@ static void unregister_dvb(struct em28xx_dvb *dvb)
dvb_unregister_adapter(&dvb->adapter);
}
-static int dvb_init(struct em28xx *dev)
+static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0;
+ int result = 0, mfe_shared = 0;
struct em28xx_dvb *dvb;
if (!dev->board.has_dvb) {
@@ -648,7 +662,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -657,7 +671,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -668,7 +682,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -689,7 +703,7 @@ static int dvb_init(struct em28xx *dev)
&dev->i2c_adap);
}
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -699,7 +713,7 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(s5h1409_attach,
&em28xx_s5h1409_with_xc3028,
&dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -720,7 +734,7 @@ static int dvb_init(struct em28xx *dev)
case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
&dev->i2c_adap, &dev->udev->dev);
- if (attach_xc3028(0x61, dev) < 0) {
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
}
@@ -753,11 +767,9 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0] = dvb_attach(cxd2820r_attach,
&em28xx_cxd2820r_config, &dev->i2c_adap, NULL);
if (dvb->fe[0]) {
- struct i2c_adapter *i2c_tuner;
- i2c_tuner = cxd2820r_get_tuner_i2c_adapter(dvb->fe[0]);
/* FE 0 attach tuner */
if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
+ &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) {
dvb_frontend_detach(dvb->fe[0]);
result = -EINVAL;
goto out_free;
@@ -768,10 +780,12 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[1]->id = 1;
/* FE 1 attach tuner */
if (!dvb_attach(tda18271_attach, dvb->fe[1], 0x60,
- i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
+ &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) {
dvb_frontend_detach(dvb->fe[1]);
/* leave FE 0 still active */
}
+
+ mfe_shared = 1;
}
break;
case EM2884_BOARD_TERRATEC_H5:
@@ -809,6 +823,16 @@ static int dvb_init(struct em28xx *dev)
sizeof(dvb->fe[0]->ops.tuner_ops));
break;
+ case EM28174_BOARD_PCTV_460E:
+ /* attach demod */
+ dvb->fe[0] = dvb_attach(tda10071_attach,
+ &em28xx_tda10071_config, &dev->i2c_adap);
+
+ /* attach SEC */
+ if (dvb->fe[0])
+ dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
+ &em28xx_a8293_config);
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
@@ -823,11 +847,14 @@ static int dvb_init(struct em28xx *dev)
dvb->fe[0]->callback = em28xx_tuner_callback;
/* register everything */
- result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+ result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
if (result < 0)
goto out_free;
+ /* MFE lock */
+ dvb->adapter.mfe_shared = mfe_shared;
+
em28xx_info("Successfully loaded em28xx-dvb\n");
ret:
em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -840,7 +867,14 @@ out_free:
goto ret;
}
-static int dvb_fini(struct em28xx *dev)
+static inline void prevent_sleep(struct dvb_frontend_ops *ops)
+{
+ ops->set_voltage = NULL;
+ ops->sleep = NULL;
+ ops->tuner_ops.sleep = NULL;
+}
+
+static int em28xx_dvb_fini(struct em28xx *dev)
{
if (!dev->board.has_dvb) {
/* This device does not support the extension */
@@ -848,8 +882,19 @@ static int dvb_fini(struct em28xx *dev)
}
if (dev->dvb) {
- unregister_dvb(dev->dvb);
- kfree(dev->dvb);
+ struct em28xx_dvb *dvb = dev->dvb;
+
+ if (dev->state & DEV_DISCONNECTED) {
+ /* We cannot tell the device to sleep
+ * once it has been unplugged. */
+ if (dvb->fe[0])
+ prevent_sleep(&dvb->fe[0]->ops);
+ if (dvb->fe[1])
+ prevent_sleep(&dvb->fe[1]->ops);
+ }
+
+ em28xx_unregister_dvb(dvb);
+ kfree(dvb);
dev->dvb = NULL;
}
@@ -859,8 +904,8 @@ static int dvb_fini(struct em28xx *dev)
static struct em28xx_ops dvb_ops = {
.id = EM28XX_DVB,
.name = "Em28xx dvb Extension",
- .init = dvb_init,
- .fini = dvb_fini,
+ .init = em28xx_dvb_init,
+ .fini = em28xx_dvb_fini,
};
static int __init em28xx_dvb_register(void)
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index 5d12b14282e3..679da4804281 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -463,11 +463,11 @@ int em28xx_ir_fini(struct em28xx *dev)
if (!ir)
return 0;
- em28xx_ir_stop(ir->rc);
- rc_unregister_device(ir->rc);
- kfree(ir);
+ if (ir->rc)
+ rc_unregister_device(ir->rc);
/* done */
+ kfree(ir);
dev->ir = NULL;
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index d176dc0394e2..9b4557a2f6d0 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -1156,6 +1156,21 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
return 0;
}
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+
+ return 0;
+}
+
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
@@ -1787,6 +1802,45 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_fmt *fmt;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ fmt = format_by_fourcc(fsize->pixel_format);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ if (fsize->index > 1)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = maxw / (1 + fsize->index);
+ fsize->discrete.height = maxh / (1 + fsize->index);
+ return 0;
+ }
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ /* Report a continuous range */
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = 48;
+ fsize->stepwise.min_height = 32;
+ fsize->stepwise.max_width = maxw;
+ fsize->stepwise.max_height = maxh;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ return 0;
+}
+
/* Sliced VBI ioctls */
static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
@@ -2200,6 +2254,7 @@ static int em28xx_v4l2_close(struct file *filp)
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
em28xx_release_resources(dev);
+ kfree(dev->alt_max_pkt_size);
kfree(dev);
return 0;
}
@@ -2340,10 +2395,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
-
.vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
.vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
.vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
@@ -2353,6 +2408,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index d80658bf3da9..2a2cb7ed0014 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -120,6 +120,7 @@
#define EM2874_BOARD_LEADERSHIP_ISDBT 77
#define EM28174_BOARD_PCTV_290E 78
#define EM2884_BOARD_TERRATEC_H5 79
+#define EM28174_BOARD_PCTV_460E 80
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -677,8 +678,6 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
void em28xx_wake_i2c(struct em28xx *dev);
-void em28xx_remove_from_devlist(struct em28xx *dev);
-void em28xx_add_into_devlist(struct em28xx *dev);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
void em28xx_init_extension(struct em28xx *dev);
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
index 14bb907d650e..337ded4a6388 100644
--- a/drivers/media/video/et61x251/et61x251.h
+++ b/drivers/media/video/et61x251/et61x251.h
@@ -165,45 +165,49 @@ et61x251_attach_sensor(struct et61x251_device* cam,
#undef DBG
#undef KDBG
#ifdef ET61X251_DEBUG
-# define DBG(level, fmt, args...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1) \
- dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
- else if ((level) == 2) \
- dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
- else if ((level) >= 3) \
- dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
- __FILE__, __func__, __LINE__ , ## args); \
- } \
+#define DBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
} while (0)
-# define KDBG(level, fmt, args...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1 || (level) == 2) \
- pr_info("et61x251: " fmt "\n", ## args); \
- else if ((level) == 3) \
- pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \
- __func__, __LINE__ , ## args); \
- } \
+#define KDBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info(fmt "\n", ##__VA_ARGS__); \
+ else if ((level) == 3) \
+ pr_debug("[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
} while (0)
-# define V4LDBG(level, name, cmd) \
-do { \
- if (debug >= (level)) \
- v4l_print_ioctl(name, cmd); \
+#define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
} while (0)
#else
-# define DBG(level, fmt, args...) do {;} while(0)
-# define KDBG(level, fmt, args...) do {;} while(0)
-# define V4LDBG(level, name, cmd) do {;} while(0)
+#define DBG(level, fmt, ...) do {;} while(0)
+#define KDBG(level, fmt, ...) do {;} while(0)
+#define V4LDBG(level, name, cmd) do {;} while(0)
#endif
#undef PDBG
-#define PDBG(fmt, args...) \
-dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \
- __LINE__ , ## args)
+#define PDBG(fmt, ...) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#undef PDBGG
-#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */
#endif /* _ET61X251_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index 9a1e80a1e145..d3777c86e1de 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
index 04b7fbb310a8..ced2e167935d 100644
--- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c
+++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "et61x251_sensor.h"
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 43d9a20caebc..103af3fe5aa0 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -356,6 +356,16 @@ config USB_GSPCA_T613
To compile this driver as a module, choose M here: the
module will be called gspca_t613.
+config USB_GSPCA_TOPRO
+ tristate "TOPRO USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ TP6800 and TP6810 Topro chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_topro.
+
config USB_GSPCA_TV8532
tristate "TV8532 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index d6364a86333a..f345f494d0f3 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o
obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o
@@ -78,6 +79,7 @@ gspca_stk014-objs := stk014.o
gspca_stv0680-objs := stv0680.o
gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
+gspca_topro-objs := topro.o
gspca_tv8532-objs := tv8532.o
gspca_vc032x-objs := vc032x.o
gspca_vicam-objs := vicam.o
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
index a09c4709d613..6ae26160b81f 100644
--- a/drivers/media/video/gspca/benq.c
+++ b/drivers/media/video/gspca/benq.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "benq"
#include "gspca.h"
@@ -62,7 +64,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -84,20 +86,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static int sd_isoc_init(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface,
- gspca_dev->nbalt - 1);
- if (ret < 0) {
- err("usb_set_interface failed");
- return ret;
- }
-/* reg_w(gspca_dev, 0x0003, 0x0002); */
- return 0;
-}
-
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -113,7 +101,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
for (n = 0; n < 4; n++) {
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -123,7 +111,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_alloc_coherent failed");
+ pr_err("usb_alloc_coherent failed\n");
return -ENOMEM;
}
urb->dev = gspca_dev->dev;
@@ -181,7 +169,7 @@ static void sd_isoc_irq(struct urb *urb)
if (gspca_dev->frozen)
return;
#endif
- err("urb status: %d", urb->status);
+ pr_err("urb status: %d\n", urb->status);
return;
}
@@ -209,7 +197,7 @@ static void sd_isoc_irq(struct urb *urb)
if (st == 0)
st = urb->iso_frame_desc[i].status;
if (st) {
- err("ISOC data error: [%d] status=%d",
+ pr_err("ISOC data error: [%d] status=%d\n",
i, st);
gspca_dev->last_packet_type = DISCARD_PACKET;
continue;
@@ -256,10 +244,10 @@ static void sd_isoc_irq(struct urb *urb)
/* resubmit the URBs */
st = usb_submit_urb(urb0, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb(0) ret %d", st);
+ pr_err("usb_submit_urb(0) ret %d\n", st);
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
/* sub-driver description */
@@ -269,7 +257,6 @@ static const struct sd_desc sd_desc = {
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
- .isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index 8b398493f96b..4c56dbef6d92 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "conex"
#include "gspca.h"
@@ -129,7 +131,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -169,7 +171,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
index f2a9451eea19..f9b86b2484b0 100644
--- a/drivers/media/video/gspca/cpia1.c
+++ b/drivers/media/video/gspca/cpia1.c
@@ -26,6 +26,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "cpia1"
#include <linux/input.h>
@@ -550,8 +552,7 @@ retry:
gspca_dev->usb_buf, databytes, 1000);
if (ret < 0)
- err("usb_control_msg %02x, error %d", command[1],
- ret);
+ pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
if (ret == -EPIPE && retries > 0) {
retries--;
@@ -1279,7 +1280,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev)
cmd[7] = 0;
ret = cpia_usb_transferCmd(gspca_dev, cmd);
if (ret) {
- err("ReadVPRegs(30,4,9,8) - failed: %d", ret);
+ pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
return;
}
exp_acc = gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 4b2c483fce6f..0357d6d461d1 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "etoms"
#include "gspca.h"
@@ -236,7 +238,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -274,7 +276,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 987b4b69d7ab..ea48200fd3a0 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "finepix"
#include "gspca.h"
@@ -182,7 +184,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* Init the device */
ret = command(gspca_dev, 0);
if (ret < 0) {
- err("init failed %d", ret);
+ pr_err("init failed %d\n", ret);
return ret;
}
@@ -194,14 +196,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
FPIX_MAX_TRANSFER, &len,
FPIX_TIMEOUT);
if (ret < 0) {
- err("usb_bulk_msg failed %d", ret);
+ pr_err("usb_bulk_msg failed %d\n", ret);
return ret;
}
/* Request a frame, but don't read it */
ret = command(gspca_dev, 1);
if (ret < 0) {
- err("frame request failed %d", ret);
+ pr_err("frame request failed %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile
index 13c9403cc87d..f511eccdfd9c 100644
--- a/drivers/media/video/gspca/gl860/Makefile
+++ b/drivers/media/video/gspca/gl860/Makefile
@@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \
gl860-ov9655.o \
gl860-mi2020.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index e8e071aa212f..2ced3b73994f 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -18,6 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "gspca.h"
#include "gl860.h"
@@ -572,9 +575,8 @@ int gl860_RTx(struct gspca_dev *gspca_dev,
}
if (r < 0)
- err("ctrl transfer failed %4d "
- "[p%02x r%d v%04x i%04x len%d]",
- r, pref, req, val, index, len);
+ pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n",
+ r, pref, req, val, index, len);
else if (len > 1 && r < len)
PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len);
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 5da4879f47f2..881e04c7ffe6 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -21,7 +21,9 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define MODULE_NAME "gspca"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define GSPCA_VERSION "2.14.0"
#include <linux/init.h>
#include <linux/fs.h>
@@ -50,12 +52,10 @@
#error "DEF_NURBS too big"
#endif
-#define DRIVER_VERSION_NUMBER "2.13.0"
-
MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION_NUMBER);
+MODULE_VERSION(GSPCA_VERSION);
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@@ -148,7 +148,7 @@ static void int_irq(struct urb *urb)
if (ret == 0) {
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0)
- err("Resubmit URB failed with error %i", ret);
+ pr_err("Resubmit URB failed with error %i\n", ret);
}
}
@@ -177,8 +177,8 @@ static int gspca_input_connect(struct gspca_dev *dev)
err = input_register_device(input_dev);
if (err) {
- err("Input device registration failed with error %i",
- err);
+ pr_err("Input device registration failed with error %i\n",
+ err);
input_dev->dev.parent = NULL;
input_free_device(input_dev);
} else {
@@ -323,8 +323,8 @@ static void fill_frame(struct gspca_dev *gspca_dev,
/* check the packet status and length */
st = urb->iso_frame_desc[i].status;
if (st) {
- err("ISOC data error: [%d] len=%d, status=%d",
- i, len, st);
+ pr_err("ISOC data error: [%d] len=%d, status=%d\n",
+ i, len, st);
gspca_dev->last_packet_type = DISCARD_PACKET;
continue;
}
@@ -346,7 +346,7 @@ resubmit:
/* resubmit the URB */
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
/*
@@ -400,7 +400,7 @@ resubmit:
if (gspca_dev->cam.bulk_nurbs != 0) {
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb() ret %d", st);
+ pr_err("usb_submit_urb() ret %d\n", st);
}
}
@@ -464,7 +464,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
} else {
/* !! image is NULL only when last pkt is LAST or DISCARD
if (gspca_dev->image == NULL) {
- err("gspca_frame_add() image == NULL");
+ pr_err("gspca_frame_add() image == NULL\n");
return;
}
*/
@@ -497,19 +497,6 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
}
EXPORT_SYMBOL(gspca_frame_add);
-static int gspca_is_compressed(__u32 format)
-{
- switch (format) {
- case V4L2_PIX_FMT_MJPEG:
- case V4L2_PIX_FMT_JPEG:
- case V4L2_PIX_FMT_SPCA561:
- case V4L2_PIX_FMT_PAC207:
- case V4L2_PIX_FMT_MR97310A:
- return 1;
- }
- return 0;
-}
-
static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
enum v4l2_memory memory, unsigned int count)
{
@@ -525,7 +512,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
count = GSPCA_MAX_FRAMES - 1;
gspca_dev->frbuf = vmalloc_32(frsz * count);
if (!gspca_dev->frbuf) {
- err("frame alloc failed");
+ pr_err("frame alloc failed\n");
return -ENOMEM;
}
gspca_dev->capt_file = file;
@@ -597,7 +584,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
return 0;
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
if (ret < 0)
- err("set alt 0 err %d", ret);
+ pr_err("set alt 0 err %d\n", ret);
return ret;
}
@@ -640,53 +627,104 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
return NULL;
}
+/* compute the minimum bandwidth for the current transfer */
+static u32 which_bandwidth(struct gspca_dev *gspca_dev)
+{
+ u32 bandwidth;
+ int i;
+
+ i = gspca_dev->curr_mode;
+ bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
+
+ /* if the image is compressed, estimate the mean image size */
+ if (bandwidth < gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ bandwidth /= 3;
+
+ /* estimate the frame rate */
+ if (gspca_dev->sd_desc->get_streamparm) {
+ struct v4l2_streamparm parm;
+
+ parm.parm.capture.timeperframe.denominator = 15;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
+ bandwidth *= parm.parm.capture.timeperframe.denominator;
+ } else {
+ bandwidth *= 15; /* 15 fps */
+ }
+
+ PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
+ return bandwidth;
+}
+
+/* endpoint table */
+#define MAX_ALT 16
+struct ep_tb_s {
+ u32 alt;
+ u32 bandwidth;
+};
+
/*
- * look for an input (isoc or bulk) endpoint
- *
- * The endpoint is defined by the subdriver.
- * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
- * This routine may be called many times when the bandwidth is too small
- * (the bandwidth is checked on urb submit).
+ * build the table of the endpoints
+ * and compute the minimum bandwidth for the image transfer
*/
-static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
+static int build_ep_tb(struct gspca_dev *gspca_dev,
+ struct usb_interface *intf,
+ int xfer,
+ struct ep_tb_s *ep_tb)
{
- struct usb_interface *intf;
struct usb_host_endpoint *ep;
- int xfer, i, ret;
+ int i, j, nbalt, psize, found;
+ u32 bandwidth, last_bw;
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- ep = NULL;
- xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
- : USB_ENDPOINT_XFER_ISOC;
- i = gspca_dev->alt; /* previous alt setting */
- if (gspca_dev->cam.reverse_alts) {
- while (++i < gspca_dev->nbalt) {
- ep = alt_xfer(&intf->altsetting[i], xfer);
- if (ep)
- break;
- }
- } else {
- while (--i >= 0) {
- ep = alt_xfer(&intf->altsetting[i], xfer);
- if (ep)
- break;
- }
- }
- if (ep == NULL) {
- err("no transfer endpoint found");
- return NULL;
- }
- PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
- i, ep->desc.bEndpointAddress);
- gspca_dev->alt = i; /* memorize the current alt setting */
- if (gspca_dev->nbalt > 1) {
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
- if (ret < 0) {
- err("set alt %d err %d", i, ret);
- ep = NULL;
+ nbalt = intf->num_altsetting;
+ if (nbalt > MAX_ALT)
+ nbalt = MAX_ALT; /* fixme: should warn */
+
+ /* build the endpoint table */
+ i = 0;
+ last_bw = 0;
+ for (;;) {
+ ep_tb->bandwidth = 2000 * 2000 * 120;
+ found = 0;
+ for (j = 0; j < nbalt; j++) {
+ ep = alt_xfer(&intf->altsetting[j], xfer);
+ if (ep == NULL)
+ continue;
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ if (!gspca_dev->cam.bulk) /* isoc */
+ psize = (psize & 0x07ff) *
+ (1 + ((psize >> 11) & 3));
+ bandwidth = psize * ep->desc.bInterval * 1000;
+ if (gspca_dev->dev->speed == USB_SPEED_HIGH
+ || gspca_dev->dev->speed == USB_SPEED_SUPER)
+ bandwidth *= 8;
+ if (bandwidth <= last_bw)
+ continue;
+ if (bandwidth < ep_tb->bandwidth) {
+ ep_tb->bandwidth = bandwidth;
+ ep_tb->alt = j;
+ found = 1;
+ }
}
+ if (!found)
+ break;
+ PDEBUG(D_STREAM, "alt %d bandwidth %d",
+ ep_tb->alt, ep_tb->bandwidth);
+ last_bw = ep_tb->bandwidth;
+ i++;
+ ep_tb++;
+ }
+
+ /* get the requested bandwidth and start at the highest atlsetting */
+ bandwidth = which_bandwidth(gspca_dev);
+ ep_tb--;
+ while (i > 1) {
+ ep_tb--;
+ if (ep_tb->bandwidth < bandwidth)
+ break;
+ i--;
}
- return ep;
+ return i;
}
/*
@@ -731,7 +769,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -741,7 +779,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_alloc_coherent failed");
+ pr_err("usb_alloc_coherent failed\n");
return -ENOMEM;
}
urb->dev = gspca_dev->dev;
@@ -752,7 +790,10 @@ static int create_urbs(struct gspca_dev *gspca_dev,
ep->desc.bEndpointAddress);
urb->transfer_flags = URB_ISO_ASAP
| URB_NO_TRANSFER_DMA_MAP;
- urb->interval = ep->desc.bInterval;
+ if (gspca_dev->dev->speed == USB_SPEED_LOW)
+ urb->interval = ep->desc.bInterval;
+ else
+ urb->interval = 1 << (ep->desc.bInterval - 1);
urb->complete = isoc_irq;
urb->number_of_packets = npkt;
for (i = 0; i < npkt; i++) {
@@ -774,9 +815,11 @@ static int create_urbs(struct gspca_dev *gspca_dev,
*/
static int gspca_init_transfer(struct gspca_dev *gspca_dev)
{
+ struct usb_interface *intf;
struct usb_host_endpoint *ep;
struct urb *urb;
- int n, ret;
+ struct ep_tb_s ep_tb[MAX_ALT];
+ int n, ret, xfer, alt, alt_idx;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
@@ -794,30 +837,65 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
gspca_dev->usb_err = 0;
- /* set the higher alternate setting and
- * loop until urb submit succeeds */
- if (gspca_dev->cam.reverse_alts)
- gspca_dev->alt = 0;
- else
- gspca_dev->alt = gspca_dev->nbalt;
-
+ /* do the specific subdriver stuff before endpoint selection */
+ gspca_dev->alt = 0;
if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0)
goto unlock;
}
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+ : USB_ENDPOINT_XFER_ISOC;
- gspca_input_destroy_urb(gspca_dev);
- ep = get_ep(gspca_dev);
- if (ep == NULL) {
- ret = -EIO;
- goto out;
+ /* if the subdriver forced an altsetting, get the endpoint */
+ if (gspca_dev->alt != 0) {
+ gspca_dev->alt--; /* (previous version compatibility) */
+ ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
+ if (ep == NULL) {
+ pr_err("bad altsetting %d\n", gspca_dev->alt);
+ ret = -EIO;
+ goto out;
+ }
+ ep_tb[0].alt = gspca_dev->alt;
+ alt_idx = 1;
+ } else {
+
+ /* else, compute the minimum bandwidth
+ * and build the endpoint table */
+ alt_idx = build_ep_tb(gspca_dev, intf, xfer, ep_tb);
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ ret = -EIO;
+ goto unlock;
+ }
}
+
+ /* set the highest alternate setting and
+ * loop until urb submit succeeds */
+ gspca_input_destroy_urb(gspca_dev);
+
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ alt = -1;
for (;;) {
+ if (alt != gspca_dev->alt) {
+ alt = gspca_dev->alt;
+ if (gspca_dev->nbalt > 1) {
+ ret = usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ alt);
+ if (ret < 0) {
+ if (ret == -ENOSPC)
+ goto retry; /*fixme: ugly*/
+ pr_err("set alt %d err %d\n", alt, ret);
+ goto out;
+ }
+ }
+ }
if (!gspca_dev->cam.no_urb_create) {
- PDEBUG(D_STREAM, "init transfer alt %d",
- gspca_dev->alt);
- ret = create_urbs(gspca_dev, ep);
+ PDEBUG(D_STREAM, "init transfer alt %d", alt);
+ ret = create_urbs(gspca_dev,
+ alt_xfer(&intf->altsetting[alt], xfer));
if (ret < 0) {
destroy_urbs(gspca_dev);
goto out;
@@ -851,29 +929,35 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
break;
}
if (ret >= 0)
- break;
+ break; /* transfer is started */
+
+ /* something when wrong
+ * stop the webcam and free the transfer resources */
gspca_stream_off(gspca_dev);
if (ret != -ENOSPC) {
- err("usb_submit_urb alt %d err %d",
- gspca_dev->alt, ret);
+ pr_err("usb_submit_urb alt %d err %d\n",
+ gspca_dev->alt, ret);
goto out;
}
/* the bandwidth is not wide enough
* negotiate or try a lower alternate setting */
+retry:
PDEBUG(D_ERR|D_STREAM,
- "bandwidth not wide enough - trying again");
+ "alt %d - bandwidth not wide enough - trying again",
+ alt);
msleep(20); /* wait for kill complete */
if (gspca_dev->sd_desc->isoc_nego) {
ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
if (ret < 0)
goto out;
} else {
- ep = get_ep(gspca_dev);
- if (ep == NULL) {
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
ret = -EIO;
goto out;
}
+ alt = ep_tb[--alt_idx].alt;
}
}
out:
@@ -1044,7 +1128,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL; /* no more format */
fmtdesc->pixelformat = fmt_tb[index];
- if (gspca_is_compressed(fmt_tb[index]))
+ if (gspca_dev->cam.cam_mode[i].sizeimage <
+ gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
@@ -2195,19 +2281,20 @@ int gspca_dev_probe2(struct usb_interface *intf,
struct usb_device *dev = interface_to_usbdev(intf);
int ret;
- PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
+ pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
+ sd_desc->name, id->idVendor, id->idProduct);
/* create the device */
if (dev_size < sizeof *gspca_dev)
dev_size = sizeof *gspca_dev;
gspca_dev = kzalloc(dev_size, GFP_KERNEL);
if (!gspca_dev) {
- err("couldn't kzalloc gspca struct");
+ pr_err("couldn't kzalloc gspca struct\n");
return -ENOMEM;
}
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
- err("out of memory");
+ pr_err("out of memory\n");
ret = -ENOMEM;
goto out;
}
@@ -2264,7 +2351,7 @@ int gspca_dev_probe2(struct usb_interface *intf,
VFL_TYPE_GRABBER,
-1);
if (ret < 0) {
- err("video_register_device err %d", ret);
+ pr_err("video_register_device err %d\n", ret);
goto out;
}
@@ -2296,8 +2383,8 @@ int gspca_dev_probe(struct usb_interface *intf,
/* we don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1) {
- err("%04x:%04x too many config",
- id->idVendor, id->idProduct);
+ pr_err("%04x:%04x too many config\n",
+ id->idVendor, id->idProduct);
return -ENODEV;
}
@@ -2480,7 +2567,7 @@ EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
/* -- module insert / remove -- */
static int __init gspca_init(void)
{
- info("v" DRIVER_VERSION_NUMBER " registered");
+ pr_info("v" GSPCA_VERSION " registered\n");
return 0;
}
static void __exit gspca_exit(void)
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 49e2fcbe81fb..e444f16e1497 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -14,11 +14,12 @@
#ifdef GSPCA_DEBUG
/* GSPCA our debug messages */
extern int gspca_debug;
-#define PDEBUG(level, fmt, args...) \
- do {\
- if (gspca_debug & (level)) \
- printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
- } while (0)
+#define PDEBUG(level, fmt, ...) \
+do { \
+ if (gspca_debug & (level)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
#define D_ERR 0x01
#define D_PROBE 0x02
#define D_CONF 0x04
@@ -29,17 +30,8 @@ extern int gspca_debug;
#define D_USBO 0x00
#define D_V4L2 0x0100
#else
-#define PDEBUG(level, fmt, args...)
+#define PDEBUG(level, fmt, ...)
#endif
-#undef err
-#define err(fmt, args...) \
- printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args)
-#undef info
-#define info(fmt, args...) \
- printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args)
-#undef warn
-#define warn(fmt, args...) \
- printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args)
#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
/* image transfers */
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 1bd9c4b542dd..8e3dabe30077 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "jeilinj"
#include <linux/slab.h>
@@ -113,8 +115,8 @@ static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
usb_sndbulkpipe(gspca_dev->dev, 3),
gspca_dev->usb_buf, 2, NULL, 500);
if (retval < 0) {
- err("command write [%02x] error %d",
- gspca_dev->usb_buf[0], retval);
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
gspca_dev->usb_err = retval;
}
}
@@ -131,8 +133,8 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
gspca_dev->usb_buf, 1, NULL, 500);
response = gspca_dev->usb_buf[0];
if (retval < 0) {
- err("read command [%02x] error %d",
- gspca_dev->usb_buf[0], retval);
+ pr_err("read command [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
gspca_dev->usb_err = retval;
}
}
@@ -403,13 +405,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
dev->type = id->driver_info;
gspca_dev->cam.ctrls = dev->ctrls;
dev->quality = QUALITY_DEF;
- dev->ctrls[LIGHTFREQ].def = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
- dev->ctrls[RED].def = RED_BALANCE_DEF;
- dev->ctrls[GREEN].def = GREEN_BALANCE_DEF;
- dev->ctrls[BLUE].def = BLUE_BALANCE_DEF;
- PDEBUG(D_PROBE,
- "JEILINJ camera detected"
- " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
cam->cam_mode = jlj_mode;
cam->nmodes = ARRAY_SIZE(jlj_mode);
cam->bulk = 1;
@@ -422,7 +418,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{
int i;
u8 *buf;
- u8 stop_commands[][2] = {
+ static u8 stop_commands[][2] = {
{0x71, 0x00},
{0x70, 0x09},
{0x71, 0x80},
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c
index 26fc206f095e..4fe51fda80f3 100644
--- a/drivers/media/video/gspca/kinect.c
+++ b/drivers/media/video/gspca/kinect.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "kinect"
#include "gspca.h"
@@ -34,11 +36,6 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
MODULE_LICENSE("GPL");
-#ifdef GSPCA_DEBUG
-int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK |
- D_USBI | D_USBO | D_V4L2;
-#endif
-
struct pkt_hdr {
uint8_t magic[2];
uint8_t pad;
@@ -141,7 +138,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
struct cam_hdr *rhdr = (void *)ibuf;
if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
- err("send_cmd: Invalid command length (0x%x)", cmd_len);
+ pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
return -1;
}
@@ -157,7 +154,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd,
sd->cam_tag, cmd_len, res);
if (res < 0) {
- err("send_cmd: Output control transfer failed (%d)", res);
+ pr_err("send_cmd: Output control transfer failed (%d)\n", res);
return res;
}
@@ -166,33 +163,35 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
} while (actual_len == 0);
PDEBUG(D_USBO, "Control reply: %d", res);
if (actual_len < sizeof(*rhdr)) {
- err("send_cmd: Input control transfer failed (%d)", res);
+ pr_err("send_cmd: Input control transfer failed (%d)\n", res);
return res;
}
actual_len -= sizeof(*rhdr);
if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
- err("send_cmd: Bad magic %02x %02x", rhdr->magic[0],
- rhdr->magic[1]);
+ pr_err("send_cmd: Bad magic %02x %02x\n",
+ rhdr->magic[0], rhdr->magic[1]);
return -1;
}
if (rhdr->cmd != chdr->cmd) {
- err("send_cmd: Bad cmd %02x != %02x", rhdr->cmd, chdr->cmd);
+ pr_err("send_cmd: Bad cmd %02x != %02x\n",
+ rhdr->cmd, chdr->cmd);
return -1;
}
if (rhdr->tag != chdr->tag) {
- err("send_cmd: Bad tag %04x != %04x", rhdr->tag, chdr->tag);
+ pr_err("send_cmd: Bad tag %04x != %04x\n",
+ rhdr->tag, chdr->tag);
return -1;
}
if (cpu_to_le16(rhdr->len) != (actual_len/2)) {
- err("send_cmd: Bad len %04x != %04x",
- cpu_to_le16(rhdr->len), (int)(actual_len/2));
+ pr_err("send_cmd: Bad len %04x != %04x\n",
+ cpu_to_le16(rhdr->len), (int)(actual_len/2));
return -1;
}
if (actual_len > reply_len) {
- warn("send_cmd: Data buffer is %d bytes long, but got %d bytes",
- reply_len, actual_len);
+ pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
+ reply_len, actual_len);
memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
} else {
memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
@@ -218,8 +217,8 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
if (res < 0)
return res;
if (res != 2) {
- warn("send_cmd returned %d [%04x %04x], 0000 expected",
- res, reply[0], reply[1]);
+ pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
+ res, reply[0], reply[1]);
}
return 0;
}
@@ -353,8 +352,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
return;
if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
- warn("[Stream %02x] Invalid magic %02x%02x", sd->stream_flag,
- hdr->magic[0], hdr->magic[1]);
+ pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
+ sd->stream_flag, hdr->magic[0], hdr->magic[1]);
return;
}
@@ -368,7 +367,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
else
- warn("Packet type not recognized...");
+ pr_warn("Packet type not recognized...\n");
}
/* sub-driver description */
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c
index 5964691c0e95..f3f7fe0ec4b7 100644
--- a/drivers/media/video/gspca/konica.c
+++ b/drivers/media/video/gspca/konica.c
@@ -28,6 +28,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "konica"
#include <linux/input.h>
@@ -200,7 +202,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
0,
1000);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -221,7 +223,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
2,
1000);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -284,7 +286,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
return -EIO;
}
@@ -315,7 +317,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
if (!urb) {
- err("usb_alloc_urb failed");
+ pr_err("usb_alloc_urb failed\n");
return -ENOMEM;
}
gspca_dev->urb[n] = urb;
@@ -324,7 +326,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
GFP_KERNEL,
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- err("usb_buffer_alloc failed");
+ pr_err("usb_buffer_alloc failed\n");
return -ENOMEM;
}
@@ -386,7 +388,7 @@ static void sd_isoc_irq(struct urb *urb)
PDEBUG(D_ERR, "urb status: %d", urb->status);
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
- err("resubmit urb error %d", st);
+ pr_err("resubmit urb error %d\n", st);
return;
}
@@ -477,7 +479,7 @@ resubmit:
}
st = usb_submit_urb(status_urb, GFP_ATOMIC);
if (st < 0)
- err("usb_submit_urb(status_urb) ret %d", st);
+ pr_err("usb_submit_urb(status_urb) ret %d\n", st);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
index bf7a19a1e6d1..7f52961f439c 100644
--- a/drivers/media/video/gspca/m5602/Makefile
+++ b/drivers/media/video/gspca/m5602/Makefile
@@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \
m5602_s5k83a.o \
m5602_s5k4aa.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index a7722b1aef9b..67533e5582a6 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov9650.h"
#include "m5602_ov7660.h"
#include "m5602_mt9m111.h"
@@ -192,10 +194,9 @@ static void m5602_dump_bridge(struct sd *sd)
for (i = 0; i < 0x80; i++) {
unsigned char val = 0;
m5602_read_bridge(sd, i, &val);
- info("ALi m5602 address 0x%x contains 0x%x", i, val);
+ pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val);
}
- info("Warning: The ALi m5602 webcam probably won't work "
- "until it's power cycled");
+ pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n");
}
static int m5602_probe_sensor(struct sd *sd)
@@ -231,7 +232,7 @@ static int m5602_probe_sensor(struct sd *sd)
return 0;
/* More sensor probe function goes here */
- info("Failed to find a sensor");
+ pr_info("Failed to find a sensor\n");
sd->sensor = NULL;
return -ENODEV;
}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index 0d605a52b924..6268aa24ec5d 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_mt9m111.h"
static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
@@ -163,7 +165,7 @@ int mt9m111_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == MT9M111_SENSOR) {
- info("Forcing a %s sensor", mt9m111.name);
+ pr_info("Forcing a %s sensor\n", mt9m111.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -191,7 +193,7 @@ int mt9m111_probe(struct sd *sd)
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
- info("Detected a mt9m111 sensor");
+ pr_info("Detected a mt9m111 sensor\n");
goto sensor_found;
}
@@ -612,34 +614,34 @@ static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
- info("Dumping the mt9m111 register state");
+ pr_info("Dumping the mt9m111 register state\n");
- info("Dumping the mt9m111 sensor core registers");
+ pr_info("Dumping the mt9m111 sensor core registers\n");
value[1] = MT9M111_SENSOR_CORE;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("Dumping the mt9m111 color pipeline registers");
+ pr_info("Dumping the mt9m111 color pipeline registers\n");
value[1] = MT9M111_COLORPIPE;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("Dumping the mt9m111 camera control registers");
+ pr_info("Dumping the mt9m111 camera control registers\n");
value[1] = MT9M111_CAMERA_CONTROL;
m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
m5602_read_sensor(sd, address, value, 2);
- info("register 0x%x contains 0x%x%x",
- address, value[0], value[1]);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
}
- info("mt9m111 register state dump complete");
+ pr_info("mt9m111 register state dump complete\n");
}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
index b12f60464b3b..9a14835c128f 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov7660.h"
static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -149,7 +151,7 @@ int ov7660_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == OV7660_SENSOR) {
- info("Forcing an %s sensor", ov7660.name);
+ pr_info("Forcing an %s sensor\n", ov7660.name);
goto sensor_found;
}
/* If we want to force another sensor,
@@ -180,10 +182,10 @@ int ov7660_probe(struct sd *sd)
if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
return -ENODEV;
- info("Sensor reported 0x%x%x", prod_id, ver_id);
+ pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
if ((prod_id == 0x76) && (ver_id == 0x60)) {
- info("Detected a ov7660 sensor");
+ pr_info("Detected a ov7660 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -457,17 +459,16 @@ static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
static void ov7660_dump_registers(struct sd *sd)
{
int address;
- info("Dumping the ov7660 register state");
+ pr_info("Dumping the ov7660 register state\n");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("ov7660 register state dump complete");
+ pr_info("ov7660 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -477,9 +478,9 @@ static void ov7660_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 703d48670a24..2114a8b90ec9 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_ov9650.h"
static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
@@ -299,7 +301,7 @@ int ov9650_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == OV9650_SENSOR) {
- info("Forcing an %s sensor", ov9650.name);
+ pr_info("Forcing an %s sensor\n", ov9650.name);
goto sensor_found;
}
/* If we want to force another sensor,
@@ -330,7 +332,7 @@ int ov9650_probe(struct sd *sd)
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
- info("Detected an ov9650 sensor");
+ pr_info("Detected an ov9650 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -850,17 +852,16 @@ static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
static void ov9650_dump_registers(struct sd *sd)
{
int address;
- info("Dumping the ov9650 register state");
+ pr_info("Dumping the ov9650 register state\n");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("ov9650 register state dump complete");
+ pr_info("ov9650 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -870,9 +871,9 @@ static void ov9650_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index 1febd34c2f05..b8771698cbcb 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_po1030.h"
static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
@@ -197,7 +199,7 @@ int po1030_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == PO1030_SENSOR) {
- info("Forcing a %s sensor", po1030.name);
+ pr_info("Forcing a %s sensor\n", po1030.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -221,7 +223,7 @@ int po1030_probe(struct sd *sd)
return -ENODEV;
if (dev_id_h == 0x30) {
- info("Detected a po1030 sensor");
+ pr_info("Detected a po1030 sensor\n");
goto sensor_found;
}
return -ENODEV;
@@ -267,7 +269,7 @@ int po1030_init(struct sd *sd)
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -733,16 +735,15 @@ static void po1030_dump_registers(struct sd *sd)
int address;
u8 value = 0;
- info("Dumping the po1030 sensor core registers");
+ pr_info("Dumping the po1030 sensor core registers\n");
for (address = 0; address < 0x7f; address++) {
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
}
- info("po1030 register state dump complete");
+ pr_info("po1030 register state dump complete\n");
- info("Probing for which registers that are read/write");
+ pr_info("Probing for which registers that are read/write\n");
for (address = 0; address < 0xff; address++) {
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
@@ -752,9 +753,9 @@ static void po1030_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n", address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index d27280be9852..cc8ec3f7e8dc 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "m5602_s5k4aa.h"
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
@@ -240,7 +242,7 @@ int s5k4aa_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == S5K4AA_SENSOR) {
- info("Forcing a %s sensor", s5k4aa.name);
+ pr_info("Forcing a %s sensor\n", s5k4aa.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -276,7 +278,7 @@ int s5k4aa_probe(struct sd *sd)
data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -292,7 +294,7 @@ int s5k4aa_probe(struct sd *sd)
if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
return -ENODEV;
else
- info("Detected a s5k4aa sensor");
+ pr_info("Detected a s5k4aa sensor\n");
sensor_found:
sensor_settings = kmalloc(
@@ -347,7 +349,7 @@ int s5k4aa_start(struct sd *sd)
break;
default:
- err("Invalid stream command, exiting init");
+ pr_err("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -383,7 +385,7 @@ int s5k4aa_start(struct sd *sd)
break;
default:
- err("Invalid stream command, exiting init");
+ pr_err("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -447,7 +449,7 @@ int s5k4aa_init(struct sd *sd)
init_s5k4aa[i][1], data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -686,20 +688,21 @@ static void s5k4aa_dump_registers(struct sd *sd)
m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
- info("Dumping the s5k4aa register state for page 0x%x", page);
+ pr_info("Dumping the s5k4aa register state for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
m5602_read_sensor(sd, address, &value, 1);
- info("register 0x%x contains 0x%x",
- address, value);
+ pr_info("register 0x%x contains 0x%x\n",
+ address, value);
}
}
- info("s5k4aa register state dump complete");
+ pr_info("s5k4aa register state dump complete\n");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
- info("Probing for which registers that are "
- "read/write for page 0x%x", page);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 old_value, ctrl_value, test_value = 0xff;
@@ -708,14 +711,16 @@ static void s5k4aa_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value)
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n",
+ address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n",
+ address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}
- info("Read/write register probing complete");
+ pr_info("Read/write register probing complete\n");
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index fbd91545497a..1de743a02b02 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kthread.h>
#include "m5602_s5k83a.h"
@@ -135,7 +137,7 @@ int s5k83a_probe(struct sd *sd)
if (force_sensor) {
if (force_sensor == S5K83A_SENSOR) {
- info("Forcing a %s sensor", s5k83a.name);
+ pr_info("Forcing a %s sensor\n", s5k83a.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
@@ -168,7 +170,7 @@ int s5k83a_probe(struct sd *sd)
if ((prod_id == 0xff) || (ver_id == 0xff))
return -ENODEV;
else
- info("Detected a s5k83a sensor");
+ pr_info("Detected a s5k83a sensor\n");
sensor_found:
sens_priv = kmalloc(
@@ -227,7 +229,7 @@ int s5k83a_init(struct sd *sd)
init_s5k83a[i][1], data, 2);
break;
default:
- info("Invalid stream command, exiting init");
+ pr_info("Invalid stream command, exiting init\n");
return -EINVAL;
}
}
@@ -273,7 +275,7 @@ static int rotation_thread_function(void *data)
s5k83a_get_rotation(sd, &reg);
if (previous_rotation != reg) {
previous_rotation = reg;
- info("Camera was flipped");
+ pr_info("Camera was flipped\n");
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
@@ -566,20 +568,20 @@ static void s5k83a_dump_registers(struct sd *sd)
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
- info("Dumping the s5k83a register state for page 0x%x", page);
+ pr_info("Dumping the s5k83a register state for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
m5602_read_sensor(sd, address, &val, 1);
- info("register 0x%x contains 0x%x",
- address, val);
+ pr_info("register 0x%x contains 0x%x\n", address, val);
}
}
- info("s5k83a register state dump complete");
+ pr_info("s5k83a register state dump complete\n");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
- info("Probing for which registers that are read/write "
- "for page 0x%x", page);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
@@ -588,14 +590,16 @@ static void s5k83a_dump_registers(struct sd *sd)
m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
- info("register 0x%x is writeable", address);
+ pr_info("register 0x%x is writeable\n",
+ address);
else
- info("register 0x%x is read only", address);
+ pr_info("register 0x%x is read only\n",
+ address);
/* Restore original val */
m5602_write_sensor(sd, address, &old_val, 1);
}
}
- info("Read/write register probing complete");
+ pr_info("Read/write register probing complete\n");
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 0196209a948a..ef45fa575752 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "mars"
#include "gspca.h"
@@ -178,8 +180,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("reg write [%02x] error %d",
- gspca_dev->usb_buf[0], ret);
+ pr_err("reg write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], ret);
gspca_dev->usb_err = ret;
}
}
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index 97e507967434..473e813b680e 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -40,6 +40,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "mr97310a"
#include "gspca.h"
@@ -267,7 +269,7 @@ static int mr_write(struct gspca_dev *gspca_dev, int len)
usb_sndbulkpipe(gspca_dev->dev, 4),
gspca_dev->usb_buf, len, NULL, 500);
if (rc < 0)
- err("reg write [%02x] error %d",
+ pr_err("reg write [%02x] error %d\n",
gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -281,7 +283,7 @@ static int mr_read(struct gspca_dev *gspca_dev, int len)
usb_rcvbulkpipe(gspca_dev->dev, 3),
gspca_dev->usb_buf, len, NULL, 500);
if (rc < 0)
- err("reg read [%02x] error %d",
+ pr_err("reg read [%02x] error %d\n",
gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -540,7 +542,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type = 1;
break;
default:
- err("Unknown CIF Sensor id : %02x",
+ pr_err("Unknown CIF Sensor id : %02x\n",
gspca_dev->usb_buf[1]);
return -ENODEV;
}
@@ -575,10 +577,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type = 2;
} else if ((gspca_dev->usb_buf[0] != 0x03) &&
(gspca_dev->usb_buf[0] != 0x04)) {
- err("Unknown VGA Sensor id Byte 0: %02x",
- gspca_dev->usb_buf[0]);
- err("Defaults assumed, may not work");
- err("Please report this");
+ pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
+ gspca_dev->usb_buf[0]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
}
/* Sakar Digital color needs to be adjusted. */
if ((gspca_dev->usb_buf[0] == 0x03) &&
@@ -595,10 +597,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* Nothing to do here. */
break;
default:
- err("Unknown VGA Sensor id Byte 1: %02x",
- gspca_dev->usb_buf[1]);
- err("Defaults assumed, may not work");
- err("Please report this");
+ pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
+ gspca_dev->usb_buf[1]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
}
}
PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c
index 8e754fd4dc5e..7681814e594f 100644
--- a/drivers/media/video/gspca/nw80x.c
+++ b/drivers/media/video/gspca/nw80x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "nw80x"
#include "gspca.h"
@@ -1571,7 +1573,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
len,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1592,7 +1594,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
0x00, index,
gspca_dev->usb_buf, len, 500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
@@ -1802,7 +1804,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
}
if (webcam_chip[sd->webcam] != sd->bridge) {
- err("Bad webcam type %d for NW80%d", sd->webcam, sd->bridge);
+ pr_err("Bad webcam type %d for NW80%d\n",
+ sd->webcam, sd->bridge);
gspca_dev->usb_err = -ENODEV;
return gspca_dev->usb_err;
}
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 18305c89083c..6a01b35a9478 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -36,6 +36,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov519"
#include <linux/input.h>
@@ -2171,7 +2174,7 @@ static void reg_w(struct sd *sd, u16 index, u16 value)
sd->gspca_dev.usb_buf, 1, 500);
leave:
if (ret < 0) {
- err("reg_w %02x failed %d", index, ret);
+ pr_err("reg_w %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
return;
}
@@ -2210,7 +2213,7 @@ static int reg_r(struct sd *sd, u16 index)
PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
req, index, ret);
} else {
- err("reg_r %02x failed %d", index, ret);
+ pr_err("reg_r %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2235,7 +2238,7 @@ static int reg_r8(struct sd *sd,
if (ret >= 0) {
ret = sd->gspca_dev.usb_buf[0];
} else {
- err("reg_r8 %02x failed %d", index, ret);
+ pr_err("reg_r8 %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2288,7 +2291,7 @@ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
0, index,
sd->gspca_dev.usb_buf, n, 500);
if (ret < 0) {
- err("reg_w32 %02x failed %d", index, ret);
+ pr_err("reg_w32 %02x failed %d\n", index, ret);
sd->gspca_dev.usb_err = ret;
}
}
@@ -2457,7 +2460,7 @@ static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
(u16) value, (u16) reg, NULL, 0, 500);
if (ret < 0) {
- err("ovfx2_i2c_w %02x failed %d", reg, ret);
+ pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2481,7 +2484,7 @@ static int ovfx2_i2c_r(struct sd *sd, u8 reg)
ret = sd->gspca_dev.usb_buf[0];
PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
} else {
- err("ovfx2_i2c_r %02x failed %d", reg, ret);
+ pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret);
sd->gspca_dev.usb_err = ret;
}
@@ -2727,7 +2730,7 @@ static void ov_hires_configure(struct sd *sd)
int high, low;
if (sd->bridge != BRIDGE_OVFX2) {
- err("error hires sensors only supported with ovfx2");
+ pr_err("error hires sensors only supported with ovfx2\n");
return;
}
@@ -2762,7 +2765,7 @@ static void ov_hires_configure(struct sd *sd)
}
break;
}
- err("Error unknown sensor type: %02x%02x", high, low);
+ pr_err("Error unknown sensor type: %02x%02x\n", high, low);
}
/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
@@ -2783,7 +2786,7 @@ static void ov8xx0_configure(struct sd *sd)
if ((rc & 3) == 1)
sd->sensor = SEN_OV8610;
else
- err("Unknown image sensor version: %d", rc & 3);
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
}
/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
@@ -2840,8 +2843,8 @@ static void ov7xx0_configure(struct sd *sd)
if (high == 0x76) {
switch (low) {
case 0x30:
- err("Sensor is an OV7630/OV7635");
- err("7630 is not supported by this driver");
+ pr_err("Sensor is an OV7630/OV7635\n");
+ pr_err("7630 is not supported by this driver\n");
return;
case 0x40:
PDEBUG(D_PROBE, "Sensor is an OV7645");
@@ -2868,7 +2871,7 @@ static void ov7xx0_configure(struct sd *sd)
sd->sensor = SEN_OV7620;
}
} else {
- err("Unknown image sensor version: %d", rc & 3);
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
}
}
@@ -2891,8 +2894,7 @@ static void ov6xx0_configure(struct sd *sd)
switch (rc) {
case 0x00:
sd->sensor = SEN_OV6630;
- warn("WARNING: Sensor is an OV66308. Your camera may have");
- warn("been misdetected in previous driver versions.");
+ pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
break;
case 0x01:
sd->sensor = SEN_OV6620;
@@ -2908,11 +2910,10 @@ static void ov6xx0_configure(struct sd *sd)
break;
case 0x90:
sd->sensor = SEN_OV6630;
- warn("WARNING: Sensor is an OV66307. Your camera may have");
- warn("been misdetected in previous driver versions.");
+ pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
break;
default:
- err("FATAL: Unknown sensor version: 0x%02x", rc);
+ pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc);
return;
}
@@ -3405,7 +3406,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
} else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
ov_hires_configure(sd);
} else {
- err("Can't determine sensor slave IDs");
+ pr_err("Can't determine sensor slave IDs\n");
goto error;
}
@@ -3590,7 +3591,7 @@ static void ov511_mode_init_regs(struct sd *sd)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
sd->gspca_dev.usb_err = -EIO;
return;
}
@@ -3713,7 +3714,7 @@ static void ov518_mode_init_regs(struct sd *sd)
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
sd->gspca_dev.usb_err = -EIO;
return;
}
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 0c6369b7fe18..76907eced4a1 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -28,6 +28,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov534"
#include "gspca.h"
@@ -775,7 +777,7 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
if (ret < 0) {
- err("write failed %d", ret);
+ pr_err("write failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -794,7 +796,7 @@ static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("read failed %d", ret);
+ pr_err("read failed %d\n", ret);
gspca_dev->usb_err = ret;
}
return gspca_dev->usb_buf[0];
@@ -858,7 +860,7 @@ static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
if (!sccb_check_status(gspca_dev)) {
- err("sccb_reg_write failed");
+ pr_err("sccb_reg_write failed\n");
gspca_dev->usb_err = -EIO;
}
}
@@ -868,11 +870,11 @@ static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_reg_read failed 1");
+ pr_err("sccb_reg_read failed 1\n");
ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_reg_read failed 2");
+ pr_err("sccb_reg_read failed 2\n");
return ov534_reg_read(gspca_dev, OV534_REG_READ);
}
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index aaf5428c57f5..b3b1ea60a841 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -1,7 +1,7 @@
/*
- * ov534-ov965x gspca driver
+ * ov534-ov9xxx gspca driver
*
- * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr
+ * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
* Copyright (C) 2008 Jim Paris <jim@jtan.com>
*
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "ov534_9"
#include "gspca.h"
@@ -45,39 +47,44 @@ 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;
- u8 brightness;
- u8 contrast;
- u8 autogain;
- u8 exposure;
- s8 sharpness;
- u8 satur;
- u8 freq;
+ u8 sensor;
+};
+enum sensors {
+ SENSOR_OV965x, /* ov9657 */
+ SENSOR_OV971x, /* ov9712 */
+ 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_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(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_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsatur(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 const struct ctrl sd_ctrls[] = {
- { /* 0 */
+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,
@@ -85,13 +92,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 15,
.step = 1,
-#define BRIGHTNESS_DEF 7
- .default_value = BRIGHTNESS_DEF,
+ .default_value = 7
},
- .set = sd_setbrightness,
- .get = sd_getbrightness,
+ .set_control = setbrightness
},
- { /* 1 */
+[CONTRAST] = {
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -99,13 +104,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 15,
.step = 1,
-#define CONTRAST_DEF 3
- .default_value = CONTRAST_DEF,
+ .default_value = 3
},
- .set = sd_setcontrast,
- .get = sd_getcontrast,
+ .set_control = setcontrast
},
- { /* 2 */
+[AUTOGAIN] = {
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -116,11 +119,9 @@ static const struct ctrl sd_ctrls[] = {
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set_control = setautogain
},
-#define EXPO_IDX 3
- { /* 3 */
+[EXPOSURE] = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -128,13 +129,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 3,
.step = 1,
-#define EXPO_DEF 0
- .default_value = EXPO_DEF,
+ .default_value = 0
},
- .set = sd_setexposure,
- .get = sd_getexposure,
+ .set_control = setexposure
},
- { /* 4 */
+[SHARPNESS] = {
{
.id = V4L2_CID_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -142,13 +141,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = -1, /* -1 = auto */
.maximum = 4,
.step = 1,
-#define SHARPNESS_DEF -1
- .default_value = SHARPNESS_DEF,
+ .default_value = -1
},
- .set = sd_setsharpness,
- .get = sd_getsharpness,
+ .set_control = setsharpness
},
- { /* 5 */
+[SATUR] = {
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -156,13 +153,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 4,
.step = 1,
-#define SATUR_DEF 2
- .default_value = SATUR_DEF,
+ .default_value = 2
},
- .set = sd_setsatur,
- .get = sd_getsatur,
+ .set_control = setsatur
},
- {
+[LIGHTFREQ] = {
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.type = V4L2_CTRL_TYPE_MENU,
@@ -170,11 +165,9 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
-#define FREQ_DEF 0
- .default_value = FREQ_DEF,
+ .default_value = 0
},
- .set = sd_setfreq,
- .get = sd_getfreq,
+ .set_control = setlightfreq
},
};
@@ -206,6 +199,14 @@ static const struct v4l2_pix_format ov965x_mode[] = {
.colorspace = V4L2_COLORSPACE_JPEG},
};
+static const struct v4l2_pix_format ov971x_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB
+ }
+};
+
static const u8 bridge_init[][2] = {
{0x88, 0xf8},
{0x89, 0xff},
@@ -240,7 +241,7 @@ static const u8 bridge_init[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_init[][2] = {
+static const u8 ov965x_init[][2] = {
{0x12, 0x80}, /* com7 - SSCB reset */
{0x00, 0x00}, /* gain */
{0x01, 0x80}, /* blue */
@@ -450,7 +451,7 @@ static const u8 bridge_init_2[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_init_2[][2] = {
+static const u8 ov965x_init_2[][2] = {
{0x3b, 0xc4},
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -492,7 +493,65 @@ static const u8 sensor_init_2[][2] = {
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
};
-static const u8 sensor_start_1_vga[][2] = { /* same for qvga */
+static const u8 ov971x_init[][2] = {
+ {0x12, 0x80},
+ {0x09, 0x10},
+ {0x1e, 0x07},
+ {0x5f, 0x18},
+ {0x69, 0x04},
+ {0x65, 0x2a},
+ {0x68, 0x0a},
+ {0x39, 0x28},
+ {0x4d, 0x90},
+ {0xc1, 0x80},
+ {0x0c, 0x30},
+ {0x6d, 0x02},
+ {0x96, 0xf1},
+ {0xbc, 0x68},
+ {0x12, 0x00},
+ {0x3b, 0x00},
+ {0x97, 0x80},
+ {0x17, 0x25},
+ {0x18, 0xa2},
+ {0x19, 0x01},
+ {0x1a, 0xca},
+ {0x03, 0x0a},
+ {0x32, 0x07},
+ {0x98, 0x40}, /*{0x98, 0x00},*/
+ {0x99, 0xA0}, /*{0x99, 0x00},*/
+ {0x9a, 0x01}, /*{0x9a, 0x00},*/
+ {0x57, 0x00},
+ {0x58, 0x78}, /*{0x58, 0xc8},*/
+ {0x59, 0x50}, /*{0x59, 0xa0},*/
+ {0x4c, 0x13},
+ {0x4b, 0x36},
+ {0x3d, 0x3c},
+ {0x3e, 0x03},
+ {0xbd, 0x50}, /*{0xbd, 0xa0},*/
+ {0xbe, 0x78}, /*{0xbe, 0xc8},*/
+ {0x4e, 0x55},
+ {0x4f, 0x55},
+ {0x50, 0x55},
+ {0x51, 0x55},
+ {0x24, 0x55},
+ {0x25, 0x40},
+ {0x26, 0xa1},
+ {0x5c, 0x59},
+ {0x5d, 0x00},
+ {0x11, 0x00},
+ {0x2a, 0x98},
+ {0x2b, 0x06},
+ {0x2d, 0x00},
+ {0x2e, 0x00},
+ {0x13, 0xa5},
+ {0x14, 0x40},
+ {0x4a, 0x00},
+ {0x49, 0xce},
+ {0x22, 0x03},
+ {0x09, 0x00}
+};
+
+static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */
{0x12, 0x62}, /* com7 - 30fps VGA YUV */
{0x36, 0xfa}, /* aref3 */
{0x69, 0x0a}, /* hv */
@@ -515,7 +574,7 @@ static const u8 sensor_start_1_vga[][2] = { /* same for qvga */
{0xc0, 0xaa},
};
-static const u8 sensor_start_1_svga[][2] = {
+static const u8 ov965x_start_1_svga[][2] = {
{0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -537,7 +596,7 @@ static const u8 sensor_start_1_svga[][2] = {
{0xc0, 0xe2},
};
-static const u8 sensor_start_1_xga[][2] = {
+static const u8 ov965x_start_1_xga[][2] = {
{0x12, 0x02}, /* com7 */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -560,7 +619,7 @@ static const u8 sensor_start_1_xga[][2] = {
{0xc0, 0xe2},
};
-static const u8 sensor_start_1_sxga[][2] = {
+static const u8 ov965x_start_1_sxga[][2] = {
{0x12, 0x02}, /* com7 */
{0x36, 0xf8}, /* aref3 */
{0x69, 0x02}, /* hv */
@@ -709,7 +768,7 @@ static const u8 bridge_start_sxga[][2] = {
{0x94, 0x11},
};
-static const u8 sensor_start_2_qvga[][2] = {
+static const u8 ov965x_start_2_qvga[][2] = {
{0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -727,7 +786,7 @@ static const u8 sensor_start_2_qvga[][2] = {
{0x3a, 0x80}, /* tslb - yuyv */
};
-static const u8 sensor_start_2_vga[][2] = {
+static const u8 ov965x_start_2_vga[][2] = {
{0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -743,7 +802,7 @@ static const u8 sensor_start_2_vga[][2] = {
{0x2d, 0x00}, /* advfl */
};
-static const u8 sensor_start_2_svga[][2] = { /* same for xga */
+static const u8 ov965x_start_2_svga[][2] = { /* same for xga */
{0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -757,7 +816,7 @@ static const u8 sensor_start_2_svga[][2] = { /* same for xga */
{0xa3, 0x41}, /* bd60 */
};
-static const u8 sensor_start_2_sxga[][2] = {
+static const u8 ov965x_start_2_sxga[][2] = {
{0x13, 0xe0}, /* com8 */
{0x00, 0x00},
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
@@ -785,7 +844,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
if (ret < 0) {
- err("reg_w failed %d", ret);
+ pr_err("reg_w failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -810,7 +869,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
return gspca_dev->usb_buf[0];
@@ -848,7 +907,7 @@ static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
if (!sccb_check_status(gspca_dev))
- err("sccb_write failed");
+ pr_err("sccb_write failed\n");
}
static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
@@ -856,11 +915,11 @@ static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_read failed 1");
+ pr_err("sccb_read failed 1\n");
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
if (!sccb_check_status(gspca_dev))
- err("sccb_read failed 2");
+ pr_err("sccb_read failed 2\n");
return reg_r(gspca_dev, OV534_REG_READ);
}
@@ -922,7 +981,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- val = sd->brightness;
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+ return;
+ val = sd->ctrls[BRIGHTNESS].val;
if (val < 8)
val = 15 - val; /* f .. 8 */
else
@@ -935,8 +996,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
{
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->contrast << 4);
+ sd->ctrls[CONTRAST].val << 4);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -944,10 +1007,12 @@ static void setautogain(struct gspca_dev *gspca_dev)
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->autogain)
+ if (sd->ctrls[AUTOGAIN].val)
val |= 0x05; /* agc & aec */
else
val &= 0xfa;
@@ -960,8 +1025,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
u8 val;
static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
+ return;
sccb_write(gspca_dev, 0x10, /* aec[9:2] */
- expo[sd->exposure]);
+ expo[sd->ctrls[EXPOSURE].val]);
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
@@ -977,7 +1044,9 @@ static void setsharpness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
s8 val;
- val = sd->sharpness;
+ 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);
@@ -1006,8 +1075,10 @@ static void setsatur(struct gspca_dev *gspca_dev)
{0x48, 0x90}
};
- val1 = matrix[sd->satur][0];
- val2 = matrix[sd->satur][1];
+ if (gspca_dev->ctrl_dis & (1 << SATUR))
+ return;
+ val1 = matrix[sd->ctrls[SATUR].val][0];
+ val2 = matrix[sd->ctrls[SATUR].val][1];
val3 = val1 + val2;
sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
sccb_write(gspca_dev, 0x50, val3);
@@ -1022,14 +1093,16 @@ static void setsatur(struct gspca_dev *gspca_dev)
sccb_write(gspca_dev, 0x41, val1);
}
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
{
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->freq == 0) {
+ if (sd->ctrls[LIGHTFREQ].val == 0) {
sccb_write(gspca_dev, 0x13, val & 0xdf);
return;
}
@@ -1037,7 +1110,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
val = sccb_read(gspca_dev, 0x42); /* com17 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->freq == 1)
+ if (sd->ctrls[LIGHTFREQ].val == 1)
val |= 0x01;
else
val &= 0xfe;
@@ -1049,34 +1122,19 @@ 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 = ov965x_mode;
- cam->nmodes = ARRAY_SIZE(ov965x_mode);
+ gspca_dev->cam.ctrls = sd->ctrls;
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
#if AUTOGAIN_DEF != 0
- sd->autogain = AUTOGAIN_DEF;
- gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
-#endif
-#if EXPO_DEF != 0
- sd->exposure = EXPO_DEF;
-#endif
-#if SHARPNESS_DEF != 0
- sd->sharpness = SHARPNESS_DEF;
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE);
#endif
- sd->satur = SATUR_DEF;
- sd->freq = FREQ_DEF;
-
return 0;
}
/* 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;
u16 sensor_id;
/* reset bridge */
@@ -1099,68 +1157,117 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
/* initialize */
- reg_w_array(gspca_dev, bridge_init,
- ARRAY_SIZE(bridge_init));
- sccb_w_array(gspca_dev, sensor_init,
- ARRAY_SIZE(sensor_init));
- reg_w_array(gspca_dev, bridge_init_2,
- ARRAY_SIZE(bridge_init_2));
- sccb_w_array(gspca_dev, sensor_init_2,
- ARRAY_SIZE(sensor_init_2));
- reg_w(gspca_dev, 0xe0, 0x00);
- reg_w(gspca_dev, 0xe0, 0x01);
- set_led(gspca_dev, 0);
- reg_w(gspca_dev, 0xe0, 0x00);
+ if ((sensor_id & 0xfff0) == 0x9650) {
+ sd->sensor = SENSOR_OV965x;
+
+ gspca_dev->cam.cam_mode = ov965x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode);
+
+ reg_w_array(gspca_dev, bridge_init,
+ ARRAY_SIZE(bridge_init));
+ sccb_w_array(gspca_dev, ov965x_init,
+ ARRAY_SIZE(ov965x_init));
+ reg_w_array(gspca_dev, bridge_init_2,
+ ARRAY_SIZE(bridge_init_2));
+ sccb_w_array(gspca_dev, ov965x_init_2,
+ ARRAY_SIZE(ov965x_init_2));
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+ } else if ((sensor_id & 0xfff0) == 0x9710) {
+ const char *p;
+ int l;
+
+ sd->sensor = SENSOR_OV971x;
+
+ 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;
+
+ sccb_w_array(gspca_dev, ov971x_init,
+ ARRAY_SIZE(ov971x_init));
+
+ /* set video format on bridge processor */
+ /* access bridge processor's video format registers at: 0x00 */
+ reg_w(gspca_dev, 0x1c, 0x00);
+ /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/
+ reg_w(gspca_dev, 0x1d, 0x00);
+
+ /* Will W. specific stuff
+ * set VSYNC to
+ * output (0x1f) if first webcam
+ * input (0x17) if 2nd or 3rd webcam */
+ p = video_device_node_name(&gspca_dev->vdev);
+ l = strlen(p) - 1;
+ if (p[l] == '0')
+ reg_w(gspca_dev, 0x56, 0x1f);
+ else
+ reg_w(gspca_dev, 0x56, 0x17);
+ } else {
+ err("Unknown sensor %04x", sensor_id);
+ return -EINVAL;
+ }
return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_OV971x)
+ return gspca_dev->usb_err;
switch (gspca_dev->curr_mode) {
case QVGA_MODE: /* 320x240 */
- sccb_w_array(gspca_dev, sensor_start_1_vga,
- ARRAY_SIZE(sensor_start_1_vga));
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_qvga,
ARRAY_SIZE(bridge_start_qvga));
- sccb_w_array(gspca_dev, sensor_start_2_qvga,
- ARRAY_SIZE(sensor_start_2_qvga));
+ sccb_w_array(gspca_dev, ov965x_start_2_qvga,
+ ARRAY_SIZE(ov965x_start_2_qvga));
break;
case VGA_MODE: /* 640x480 */
- sccb_w_array(gspca_dev, sensor_start_1_vga,
- ARRAY_SIZE(sensor_start_1_vga));
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_vga,
ARRAY_SIZE(bridge_start_vga));
- sccb_w_array(gspca_dev, sensor_start_2_vga,
- ARRAY_SIZE(sensor_start_2_vga));
+ sccb_w_array(gspca_dev, ov965x_start_2_vga,
+ ARRAY_SIZE(ov965x_start_2_vga));
break;
case SVGA_MODE: /* 800x600 */
- sccb_w_array(gspca_dev, sensor_start_1_svga,
- ARRAY_SIZE(sensor_start_1_svga));
+ sccb_w_array(gspca_dev, ov965x_start_1_svga,
+ ARRAY_SIZE(ov965x_start_1_svga));
reg_w_array(gspca_dev, bridge_start_svga,
ARRAY_SIZE(bridge_start_svga));
- sccb_w_array(gspca_dev, sensor_start_2_svga,
- ARRAY_SIZE(sensor_start_2_svga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
break;
case XGA_MODE: /* 1024x768 */
- sccb_w_array(gspca_dev, sensor_start_1_xga,
- ARRAY_SIZE(sensor_start_1_xga));
+ sccb_w_array(gspca_dev, ov965x_start_1_xga,
+ ARRAY_SIZE(ov965x_start_1_xga));
reg_w_array(gspca_dev, bridge_start_xga,
ARRAY_SIZE(bridge_start_xga));
- sccb_w_array(gspca_dev, sensor_start_2_svga,
- ARRAY_SIZE(sensor_start_2_svga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
break;
default:
/* case SXGA_MODE: * 1280x1024 */
- sccb_w_array(gspca_dev, sensor_start_1_sxga,
- ARRAY_SIZE(sensor_start_1_sxga));
+ sccb_w_array(gspca_dev, ov965x_start_1_sxga,
+ ARRAY_SIZE(ov965x_start_1_sxga));
reg_w_array(gspca_dev, bridge_start_sxga,
ARRAY_SIZE(bridge_start_sxga));
- sccb_w_array(gspca_dev, sensor_start_2_sxga,
- ARRAY_SIZE(sensor_start_2_sxga));
+ sccb_w_array(gspca_dev, ov965x_start_2_sxga,
+ ARRAY_SIZE(ov965x_start_2_sxga));
break;
}
- setfreq(gspca_dev);
+ setlightfreq(gspca_dev);
setautogain(gspca_dev);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
@@ -1198,9 +1305,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
__u32 this_pts;
u8 this_fid;
int remaining_len = len;
+ int payload_len;
+ payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
do {
- len = min(remaining_len, 2040);
+ len = min(remaining_len, payload_len);
/* Payloads are prefixed with a UVC-style header. We
consider a frame to start when the FID toggles, or the PTS
@@ -1262,138 +1371,6 @@ scan_next:
} while (remaining_len > 0);
}
-/* controls */
-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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
-
- if (gspca_dev->streaming) {
- if (val)
- gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
- else
- gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX);
- setautogain(gspca_dev);
- }
- 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_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_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_setsatur(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->satur = val;
- if (gspca_dev->streaming)
- setsatur(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->satur;
- return 0;
-}
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->freq = val;
- if (gspca_dev->streaming)
- setfreq(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->freq;
- return 0;
-}
-
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1419,7 +1396,7 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
+ .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -1430,6 +1407,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x05a9, 0x8065)},
{USB_DEVICE(0x06f8, 0x3003)},
{}
};
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 81739a2f205e..1600df152fd6 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -23,6 +23,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac207"
#include <linux/input.h>
@@ -178,8 +180,8 @@ static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
0x00, index,
gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
if (err < 0)
- err("Failed to write registers to index 0x%04X, error %d)",
- index, err);
+ pr_err("Failed to write registers to index 0x%04X, error %d\n",
+ index, err);
return err;
}
@@ -194,8 +196,8 @@ static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
if (err)
- err("Failed to write a register (index 0x%04X,"
- " value 0x%02X, error %d)", index, value, err);
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
return err;
}
@@ -210,8 +212,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
0x00, index,
gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
if (res < 0) {
- err("Failed to read a register (index 0x%04X, error %d)",
- index, res);
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
return res;
}
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index 5615d7bd8304..1c44f78ff9e2 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -61,6 +61,8 @@
3 | 0x21 | sethvflip()
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac7302"
#include <linux/input.h>
@@ -408,8 +410,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w_buf failed index 0x%02x, error %d",
- index, ret);
+ pr_err("reg_w_buf failed index 0x%02x, error %d\n",
+ index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -431,8 +433,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
- index, value, ret);
+ pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -466,9 +468,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_page() failed index 0x%02x, "
- "value 0x%02x, error %d",
- index, page[index], ret);
+ pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, page[index], ret);
gspca_dev->usb_err = ret;
break;
}
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index f8801b50e64f..7509d05dc065 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -49,6 +49,8 @@
for max gain, 0x14 for minimal gain.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "pac7311"
#include <linux/input.h>
@@ -276,8 +278,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w_buf() failed index 0x%02x, error %d",
- index, ret);
+ pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
+ index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -299,8 +301,8 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
- index, value, ret);
+ pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -334,9 +336,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_page() failed index 0x%02x, "
- "value 0x%02x, error %d",
- index, page[index], ret);
+ pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, page[index], ret);
gspca_dev->usb_err = ret;
break;
}
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c
index 4c283c24c752..3b71bbcd977a 100644
--- a/drivers/media/video/gspca/se401.c
+++ b/drivers/media/video/gspca/se401.c
@@ -23,6 +23,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "se401"
#define BULK_SIZE 4096
@@ -144,8 +146,8 @@ static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
value, 0, NULL, 0, 1000);
if (err < 0) {
if (!silent)
- err("write req failed req %#04x val %#04x error %d",
- req, value, err);
+ pr_err("write req failed req %#04x val %#04x error %d\n",
+ req, value, err);
gspca_dev->usb_err = err;
}
}
@@ -158,7 +160,7 @@ static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
return;
if (USB_BUF_SZ < READ_REQ_SIZE) {
- err("USB_BUF_SZ too small!!");
+ pr_err("USB_BUF_SZ too small!!\n");
gspca_dev->usb_err = -ENOBUFS;
return;
}
@@ -169,7 +171,8 @@ static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000);
if (err < 0) {
if (!silent)
- err("read req failed req %#04x error %d", req, err);
+ pr_err("read req failed req %#04x error %d\n",
+ req, err);
gspca_dev->usb_err = err;
}
}
@@ -188,8 +191,8 @@ static void se401_set_feature(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
param, selector, NULL, 0, 1000);
if (err < 0) {
- err("set feature failed sel %#04x param %#04x error %d",
- selector, param, err);
+ pr_err("set feature failed sel %#04x param %#04x error %d\n",
+ selector, param, err);
gspca_dev->usb_err = err;
}
}
@@ -202,7 +205,7 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
return gspca_dev->usb_err;
if (USB_BUF_SZ < 2) {
- err("USB_BUF_SZ too small!!");
+ pr_err("USB_BUF_SZ too small!!\n");
gspca_dev->usb_err = -ENOBUFS;
return gspca_dev->usb_err;
}
@@ -213,7 +216,8 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, selector, gspca_dev->usb_buf, 2, 1000);
if (err < 0) {
- err("get feature failed sel %#04x error %d", selector, err);
+ pr_err("get feature failed sel %#04x error %d\n",
+ selector, err);
gspca_dev->usb_err = err;
return err;
}
@@ -300,21 +304,21 @@ static int sd_config(struct gspca_dev *gspca_dev,
return gspca_dev->usb_err;
if (cd[1] != 0x41) {
- err("Wrong descriptor type");
+ pr_err("Wrong descriptor type\n");
return -ENODEV;
}
if (!(cd[2] & SE401_FORMAT_BAYER)) {
- err("Bayer format not supported!");
+ pr_err("Bayer format not supported!\n");
return -ENODEV;
}
if (cd[3])
- info("ExtraFeatures: %d", cd[3]);
+ pr_info("ExtraFeatures: %d\n", cd[3]);
n = cd[4] | (cd[5] << 8);
if (n > MAX_MODES) {
- err("Too many frame sizes");
+ pr_err("Too many frame sizes\n");
return -ENODEV;
}
@@ -353,15 +357,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8;
sd->fmts[i].bytesperline = widths[i];
sd->fmts[i].sizeimage = widths[i] * heights[i];
- info("Frame size: %dx%d bayer", widths[i], heights[i]);
+ pr_info("Frame size: %dx%d bayer\n",
+ widths[i], heights[i]);
} else {
/* Found a match use janggu compression */
sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401;
sd->fmts[i].bytesperline = 0;
sd->fmts[i].sizeimage = widths[i] * heights[i] * 3;
- info("Frame size: %dx%d 1/%dth janggu",
- widths[i], heights[i],
- sd->fmts[i].priv * sd->fmts[i].priv);
+ pr_info("Frame size: %dx%d 1/%dth janggu\n",
+ widths[i], heights[i],
+ sd->fmts[i].priv * sd->fmts[i].priv);
}
}
@@ -571,11 +576,12 @@ static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
plen = ((bits + 47) >> 4) << 1;
/* Sanity checks */
if (plen > 1024) {
- err("invalid packet len %d restarting stream", plen);
+ pr_err("invalid packet len %d restarting stream\n",
+ plen);
goto error;
}
if (info == 3) {
- err("unknown frame info value restarting stream");
+ pr_err("unknown frame info value restarting stream\n");
goto error;
}
@@ -599,8 +605,8 @@ static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
break;
case 1: /* EOF */
if (sd->pixels_read != imagesize) {
- err("frame size %d expected %d",
- sd->pixels_read, imagesize);
+ pr_err("frame size %d expected %d\n",
+ sd->pixels_read, imagesize);
goto error;
}
sd_complete_frame(gspca_dev, sd->packet, plen);
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
index 4271f86dfe01..48aae3926a33 100644
--- a/drivers/media/video/gspca/sn9c2028.c
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sn9c2028"
#include "gspca.h"
@@ -75,8 +77,8 @@ static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
2, 0, gspca_dev->usb_buf, 6, 500);
if (rc < 0) {
- err("command write [%02x] error %d",
- gspca_dev->usb_buf[0], rc);
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], rc);
return rc;
}
@@ -93,7 +95,7 @@ static int sn9c2028_read1(struct gspca_dev *gspca_dev)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
1, 0, gspca_dev->usb_buf, 1, 500);
if (rc != 1) {
- err("read1 error %d", rc);
+ pr_err("read1 error %d\n", rc);
return (rc < 0) ? rc : -EIO;
}
PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
@@ -109,7 +111,7 @@ static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
4, 0, gspca_dev->usb_buf, 4, 500);
if (rc != 4) {
- err("read4 error %d", rc);
+ pr_err("read4 error %d\n", rc);
return (rc < 0) ? rc : -EIO;
}
memcpy(reading, gspca_dev->usb_buf, 4);
@@ -131,7 +133,7 @@ static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
for (i = 0; i < 256 && status < 2; i++)
status = sn9c2028_read1(gspca_dev);
if (status != 2) {
- err("long command status read error %d", status);
+ pr_err("long command status read error %d\n", status);
return (status < 0) ? status : -EIO;
}
@@ -638,7 +640,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
err_code = start_vivitar_cam(gspca_dev);
break;
default:
- err("Starting unknown camera, please report this");
+ pr_err("Starting unknown camera, please report this\n");
return -ENXIO;
}
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index c431900cd292..86e07a139a16 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/input.h>
#include "gspca.h"
@@ -1123,7 +1125,7 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
length,
500);
if (unlikely(result < 0 || result != length)) {
- err("Read register failed 0x%02X", reg);
+ pr_err("Read register failed 0x%02X\n", reg);
return -EIO;
}
return 0;
@@ -1144,7 +1146,7 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg,
length,
500);
if (unlikely(result < 0 || result != length)) {
- err("Write register failed index 0x%02X", reg);
+ pr_err("Write register failed index 0x%02X\n", reg);
return -EIO;
}
return 0;
@@ -1275,14 +1277,14 @@ static int ov9650_init_sensor(struct gspca_dev *gspca_dev)
return -EINVAL;
if (id != 0x7fa2) {
- err("sensor id for ov9650 doesn't match (0x%04x)", id);
+ pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
return -ENODEV;
}
for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) {
if (i2c_w1(gspca_dev, ov9650_init[i].reg,
ov9650_init[i].val) < 0) {
- err("OV9650 sensor initialization failed");
+ pr_err("OV9650 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1299,7 +1301,7 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) {
if (i2c_w1(gspca_dev, ov9655_init[i].reg,
ov9655_init[i].val) < 0) {
- err("OV9655 sensor initialization failed");
+ pr_err("OV9655 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1318,7 +1320,7 @@ static int soi968_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
if (i2c_w1(gspca_dev, soi968_init[i].reg,
soi968_init[i].val) < 0) {
- err("SOI968 sensor initialization failed");
+ pr_err("SOI968 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1338,7 +1340,7 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) {
if (i2c_w1(gspca_dev, ov7660_init[i].reg,
ov7660_init[i].val) < 0) {
- err("OV7660 sensor initialization failed");
+ pr_err("OV7660 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1355,7 +1357,7 @@ static int ov7670_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) {
if (i2c_w1(gspca_dev, ov7670_init[i].reg,
ov7670_init[i].val) < 0) {
- err("OV7670 sensor initialization failed");
+ pr_err("OV7670 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1379,14 +1381,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
if (i2c_w2(gspca_dev, mt9v011_init[i].reg,
mt9v011_init[i].val) < 0) {
- err("MT9V011 sensor initialization failed");
+ pr_err("MT9V011 sensor initialization failed\n");
return -ENODEV;
}
}
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V011;
- info("MT9V011 sensor detected");
+ pr_info("MT9V011 sensor detected\n");
return 0;
}
@@ -1397,7 +1399,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
if (i2c_w2(gspca_dev, mt9v111_init[i].reg,
mt9v111_init[i].val) < 0) {
- err("MT9V111 sensor initialization failed");
+ pr_err("MT9V111 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1407,7 +1409,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V111;
- info("MT9V111 sensor detected");
+ pr_info("MT9V111 sensor detected\n");
return 0;
}
@@ -1422,14 +1424,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) {
if (i2c_w2(gspca_dev, mt9v112_init[i].reg,
mt9v112_init[i].val) < 0) {
- err("MT9V112 sensor initialization failed");
+ pr_err("MT9V112 sensor initialization failed\n");
return -ENODEV;
}
}
sd->hstart = 6;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V112;
- info("MT9V112 sensor detected");
+ pr_info("MT9V112 sensor detected\n");
return 0;
}
@@ -1443,7 +1445,7 @@ static int mt9m112_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) {
if (i2c_w2(gspca_dev, mt9m112_init[i].reg,
mt9m112_init[i].val) < 0) {
- err("MT9M112 sensor initialization failed");
+ pr_err("MT9M112 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1461,7 +1463,7 @@ static int mt9m111_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
if (i2c_w2(gspca_dev, mt9m111_init[i].reg,
mt9m111_init[i].val) < 0) {
- err("MT9M111 sensor initialization failed");
+ pr_err("MT9M111 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1485,20 +1487,20 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
switch (id) {
case 0x8411:
case 0x8421:
- info("MT9M001 color sensor detected");
+ pr_info("MT9M001 color sensor detected\n");
break;
case 0x8431:
- info("MT9M001 mono sensor detected");
+ pr_info("MT9M001 mono sensor detected\n");
break;
default:
- err("No MT9M001 chip detected, ID = %x\n", id);
+ pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
return -ENODEV;
}
for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
if (i2c_w2(gspca_dev, mt9m001_init[i].reg,
mt9m001_init[i].val) < 0) {
- err("MT9M001 sensor initialization failed");
+ pr_err("MT9M001 sensor initialization failed\n");
return -ENODEV;
}
}
@@ -1517,7 +1519,7 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) {
if (i2c_w1(gspca_dev, hv7131r_init[i].reg,
hv7131r_init[i].val) < 0) {
- err("HV7131R Sensor initialization failed");
+ pr_err("HV7131R Sensor initialization failed\n");
return -ENODEV;
}
}
@@ -2103,7 +2105,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
value = bridge_init[i][1];
if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) {
- err("Device initialization failed");
+ pr_err("Device initialization failed\n");
return -ENODEV;
}
}
@@ -2114,7 +2116,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x1006, 0x20);
if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) {
- err("Device initialization failed");
+ pr_err("Device initialization failed\n");
return -ENODEV;
}
@@ -2122,27 +2124,27 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_OV9650:
if (ov9650_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV9650 sensor detected");
+ pr_info("OV9650 sensor detected\n");
break;
case SENSOR_OV9655:
if (ov9655_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV9655 sensor detected");
+ pr_info("OV9655 sensor detected\n");
break;
case SENSOR_SOI968:
if (soi968_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("SOI968 sensor detected");
+ pr_info("SOI968 sensor detected\n");
break;
case SENSOR_OV7660:
if (ov7660_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV7660 sensor detected");
+ pr_info("OV7660 sensor detected\n");
break;
case SENSOR_OV7670:
if (ov7670_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("OV7670 sensor detected");
+ pr_info("OV7670 sensor detected\n");
break;
case SENSOR_MT9VPRB:
if (mt9v_init_sensor(gspca_dev) < 0)
@@ -2151,12 +2153,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_MT9M111:
if (mt9m111_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("MT9M111 sensor detected");
+ pr_info("MT9M111 sensor detected\n");
break;
case SENSOR_MT9M112:
if (mt9m112_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("MT9M112 sensor detected");
+ pr_info("MT9M112 sensor detected\n");
break;
case SENSOR_MT9M001:
if (mt9m001_init_sensor(gspca_dev) < 0)
@@ -2165,10 +2167,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
case SENSOR_HV7131R:
if (hv7131r_init_sensor(gspca_dev) < 0)
return -ENODEV;
- info("HV7131R sensor detected");
+ pr_info("HV7131R sensor detected\n");
break;
default:
- info("Unsupported Sensor");
+ pr_info("Unsupported Sensor\n");
return -ENODEV;
}
@@ -2263,19 +2265,19 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (mode & SCALE_MASK) {
case SCALE_1280x1024:
scale = 0xc0;
- info("Set 1280x1024");
+ pr_info("Set 1280x1024\n");
break;
case SCALE_640x480:
scale = 0x80;
- info("Set 640x480");
+ pr_info("Set 640x480\n");
break;
case SCALE_320x240:
scale = 0x90;
- info("Set 320x240");
+ pr_info("Set 320x240\n");
break;
case SCALE_160x120:
scale = 0xa0;
- info("Set 160x120");
+ pr_info("Set 160x120\n");
break;
}
@@ -2513,7 +2515,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
{USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
- {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
{USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)},
{USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
{USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index c477ad11f103..c746bf19ca14 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -1,7 +1,7 @@
/*
* Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
*
- * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
* Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sonixj"
#include <linux/input.h>
@@ -136,7 +138,7 @@ static void setillum(struct gspca_dev *gspca_dev);
static void setfreq(struct gspca_dev *gspca_dev);
static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -157,7 +159,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
#define CONTRAST_MAX 127
.maximum = CONTRAST_MAX,
.step = 1,
- .default_value = 63,
+ .default_value = 20,
},
.set_control = setcontrast
},
@@ -737,7 +739,7 @@ static const u8 mi0360_sensor_init[][8] = {
{0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
- {0xd1, 0x5d, 0x2f, 0xf7, 0xB0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
{0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
{0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
@@ -1277,7 +1279,7 @@ static const u8 soi768_sensor_param1[][8] = {
/* global gain ? : 07 - change with 0x15 at the end */
{0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
{0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
/* exposure ? : 0200 - change with 0x1e at the end */
{}
};
@@ -1395,7 +1397,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
return;
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -1408,7 +1410,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
500);
PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1432,7 +1434,7 @@ static void reg_w1(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w1 err %d", ret);
+ pr_err("reg_w1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1449,7 +1451,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
value, buffer[0], buffer[1]);
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_w: buffer overflow");
+ pr_err("reg_w: buffer overflow\n");
return;
}
#endif
@@ -1462,7 +1464,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1502,7 +1504,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
gspca_dev->usb_buf, 8,
500);
if (ret < 0) {
- err("i2c_w1 err %d", ret);
+ pr_err("i2c_w1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1527,7 +1529,7 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
500);
msleep(2);
if (ret < 0) {
- err("i2c_w8 err %d", ret);
+ pr_err("i2c_w8 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -1591,7 +1593,7 @@ static void hv7131r_probe(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor HV7131R found");
return;
}
- warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x",
+ pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
gspca_dev->usb_buf[2]);
}
@@ -1710,7 +1712,7 @@ static void ov7648_probe(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_PO1030;
return;
}
- err("Unknown sensor %04x", val);
+ pr_err("Unknown sensor %04x\n", val);
}
/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
@@ -1748,7 +1750,7 @@ static void po2030n_probe(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor po2030n");
/* sd->sensor = SENSOR_PO2030N; */
} else {
- err("Unknown sensor ID %04x", val);
+ pr_err("Unknown sensor ID %04x\n", val);
}
}
@@ -2006,8 +2008,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
case SENSOR_OM6802:
expo = brightness << 2;
sd->exposure = setexposure(gspca_dev, expo);
- k2 = brightness >> 3;
- break;
+ return; /* Y offset already set */
}
reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
@@ -2019,13 +2020,13 @@ static void setcontrast(struct gspca_dev *gspca_dev)
u8 k2;
u8 contrast[6];
- k2 = sd->ctrls[CONTRAST].val * 0x30 / (CONTRAST_MAX + 1)
- + 0x10; /* 10..40 */
+ k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
+ + 37; /* 37..73 */
contrast[0] = (k2 + 1) / 2; /* red */
contrast[1] = 0;
contrast[2] = k2; /* green */
contrast[3] = 0;
- contrast[4] = (k2 + 1) / 5; /* blue */
+ contrast[4] = k2 / 5; /* blue */
contrast[5] = 0;
reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
}
@@ -2507,9 +2508,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case SENSOR_HV7131R:
case SENSOR_MI0360:
- if (mode)
- reg01 |= SYS_SEL_48M; /* 320x240: clk 48Mhz */
- else
+ if (!mode)
reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */
reg17 &= ~MCK_SIZE_MASK;
reg17 |= 0x01; /* clock / 1 */
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c
index 76c006b2bc83..695673106e76 100644
--- a/drivers/media/video/gspca/spca1528.c
+++ b/drivers/media/video/gspca/spca1528.c
@@ -1,7 +1,7 @@
/*
* spca1528 subdriver
*
- * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr)
+ * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr)
*
* 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
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca1528"
#include "gspca.h"
@@ -171,7 +173,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index,
gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -193,7 +195,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
value, index,
NULL, 0, 500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -217,21 +219,23 @@ static void reg_wb(struct gspca_dev *gspca_dev,
value, index,
gspca_dev->usb_buf, 1, 500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
static void wait_status_0(struct gspca_dev *gspca_dev)
{
- int i;
+ int i, w;
- i = 20;
+ i = 16;
+ w = 0;
do {
reg_r(gspca_dev, 0x21, 0x0000, 1);
if (gspca_dev->usb_buf[0] == 0)
return;
- msleep(30);
+ w += 15;
+ msleep(w);
} while (--i > 0);
PDEBUG(D_ERR, "wait_status_0 timeout");
gspca_dev->usb_err = -ETIME;
@@ -307,8 +311,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->color = COLOR_DEF;
sd->sharpness = SHARPNESS_DEF;
- gspca_dev->nbalt = 4; /* use alternate setting 3 */
-
return 0;
}
@@ -347,8 +349,12 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode);
reg_r(gspca_dev, 0x25, 0x0004, 1);
- reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06);
+ reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */
reg_r(gspca_dev, 0x27, 0x0000, 1);
+
+/* not useful..
+ gspca_dev->alt = 4; * use alternate setting 3 */
+
return gspca_dev->usb_err;
}
@@ -361,8 +367,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- /* the JPEG quality seems to be 82% */
- jpeg_set_qual(sd->jpeg_hdr, 82);
+ /* the JPEG quality shall be 85% */
+ jpeg_set_qual(sd->jpeg_hdr, 85);
/* set the controls */
setbrightness(gspca_dev);
@@ -377,7 +383,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* start the capture */
wait_status_0(gspca_dev);
- reg_w(gspca_dev, 0x31, 0x0000, 0x0004);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */
wait_status_1(gspca_dev);
wait_status_0(gspca_dev);
msleep(200);
@@ -390,7 +396,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{
/* stop the capture */
wait_status_0(gspca_dev);
- reg_w(gspca_dev, 0x31, 0x0000, 0x0000);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */
wait_status_1(gspca_dev);
wait_status_0(gspca_dev);
}
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index 3e76951e3c19..bb82c94ece14 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca500"
#include "gspca.h"
@@ -396,7 +398,7 @@ static int reg_w(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -418,7 +420,7 @@ static int reg_r_12(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, length,
500); /* timeout */
if (ret < 0) {
- err("reg_r_12 err %d", ret);
+ pr_err("reg_r_12 err %d\n", ret);
return ret;
}
return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index f7ef282cc600..7aaac72aee91 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca501"
#include "gspca.h"
@@ -1852,7 +1854,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x",
req, index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index e5bf865147d7..16722dc60394 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca505"
#include "gspca.h"
@@ -578,7 +580,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
req, index, value, ret);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -685,8 +687,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
return ret;
}
if (ret != 0x0101) {
- err("After vector read returns 0x%04x should be 0x0101",
- ret);
+ pr_err("After vector read returns 0x%04x should be 0x0101\n",
+ ret);
}
ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 9d0b46027b93..a44fe3d25965 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca508"
#include "gspca.h"
@@ -1275,7 +1277,7 @@ static int reg_write(struct usb_device *dev,
PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x",
index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
return ret;
}
@@ -1297,7 +1299,7 @@ static int reg_read(struct gspca_dev *gspca_dev,
PDEBUG(D_USBI, "reg read i:%04x --> %02x",
index, gspca_dev->usb_buf[0]);
if (ret < 0) {
- err("reg_read err %d", ret);
+ pr_err("reg_read err %d\n", ret);
return ret;
}
return gspca_dev->usb_buf[0];
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index e836e778dfb6..c82fd53cef95 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "spca561"
#include <linux/input.h>
@@ -315,7 +317,7 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
value, index, NULL, 0, 500);
PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
if (ret < 0)
- err("reg write: error %d", ret);
+ pr_err("reg write: error %d\n", ret);
}
static void write_vector(struct gspca_dev *gspca_dev,
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 5ba96aff2252..df805f798282 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -33,6 +33,8 @@
* drivers.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq905"
#include <linux/workqueue.h>
@@ -123,8 +125,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -135,8 +136,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
SQ905_PING, 0, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed 2 (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
return ret;
}
@@ -158,7 +158,7 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev)
SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)", __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -186,7 +186,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
if (need_lock)
mutex_unlock(&gspca_dev->usb_lock);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)", __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
ret = usb_bulk_msg(gspca_dev->dev,
@@ -195,8 +195,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
/* successful, it returns 0, otherwise negative */
if (ret < 0 || act_len != size) {
- err("bulk read fail (%d) len %d/%d",
- ret, act_len, size);
+ pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size);
return -EIO;
}
return 0;
@@ -226,7 +225,7 @@ static void sq905_dostream(struct work_struct *work)
buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
}
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index 457563b7a71b..c2c056056e08 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -27,6 +27,8 @@
* and may contain code fragments from it.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq905c"
#include <linux/workqueue.h>
@@ -95,8 +97,7 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
command, index, NULL, 0,
SQ905C_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -115,8 +116,7 @@ static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
command, index, gspca_dev->usb_buf, size,
SQ905C_CMD_TIMEOUT);
if (ret < 0) {
- err("%s: usb_control_msg failed (%d)",
- __func__, ret);
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
return ret;
}
@@ -146,7 +146,7 @@ static void sq905c_dostream(struct work_struct *work)
buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
}
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
index 8215d5dcd456..e4255b4905e7 100644
--- a/drivers/media/video/gspca/sq930x.c
+++ b/drivers/media/video/gspca/sq930x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sq930x"
#include "gspca.h"
@@ -468,7 +470,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
value, 0, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_r %04x failed %d", value, ret);
+ pr_err("reg_r %04x failed %d\n", value, ret);
gspca_dev->usb_err = ret;
}
}
@@ -488,7 +490,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
500);
msleep(30);
if (ret < 0) {
- err("reg_w %04x %04x failed %d", value, index, ret);
+ pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -511,7 +513,7 @@ static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
1000);
msleep(30);
if (ret < 0) {
- err("reg_wb %04x %04x failed %d", value, index, ret);
+ pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -556,7 +558,7 @@ static void i2c_write(struct sd *sd,
gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
500);
if (ret < 0) {
- err("i2c_write failed %d", ret);
+ pr_err("i2c_write failed %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -575,7 +577,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if ((batchsize - 1) * 3 > USB_BUF_SZ) {
- err("Bug: usb_buf overflow");
+ pr_err("Bug: usb_buf overflow\n");
gspca_dev->usb_err = -ENOMEM;
return;
}
@@ -612,7 +614,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
500);
if (ret < 0) {
- err("ucbus_write failed %d", ret);
+ pr_err("ucbus_write failed %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
@@ -688,7 +690,7 @@ static void cmos_probe(struct gspca_dev *gspca_dev)
break;
}
if (i >= ARRAY_SIZE(probe_order)) {
- err("Unknown sensor");
+ pr_err("Unknown sensor\n");
gspca_dev->usb_err = -EINVAL;
return;
}
@@ -696,7 +698,8 @@ static void cmos_probe(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV9630:
- err("Sensor %s not yet treated", sensor_tb[sd->sensor].name);
+ pr_err("Sensor %s not yet treated\n",
+ sensor_tb[sd->sensor].name);
gspca_dev->usb_err = -EINVAL;
break;
}
@@ -1091,7 +1094,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
gspca_dev->cam.bulk_nurbs = 1;
ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
if (ret < 0)
- err("sd_dq_callback() err %d", ret);
+ pr_err("sd_dq_callback() err %d\n", ret);
/* wait a little time, otherwise the webcam crashes */
msleep(100);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 763747700f10..42a7a28a6c8b 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "stk014"
#include "gspca.h"
@@ -137,7 +139,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
return 0;
}
@@ -162,7 +164,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -192,7 +194,7 @@ static void rcv_val(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("rcv_val err %d", ret);
+ pr_err("rcv_val err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -235,7 +237,7 @@ static void snd_val(struct gspca_dev *gspca_dev,
&alen,
500); /* timeout in milliseconds */
if (ret < 0) {
- err("snd_val err %d", ret);
+ pr_err("snd_val err %d\n", ret);
gspca_dev->usb_err = ret;
} else {
if (ads == 0x003f08) {
@@ -315,7 +317,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ret = reg_r(gspca_dev, 0x0740);
if (gspca_dev->usb_err >= 0) {
if (ret != 0xff) {
- err("init reg: 0x%02x", ret);
+ pr_err("init reg: 0x%02x\n", ret);
gspca_dev->usb_err = -EIO;
}
}
@@ -349,8 +351,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
gspca_dev->iface,
gspca_dev->alt);
if (ret < 0) {
- err("set intf %d %d failed",
- gspca_dev->iface, gspca_dev->alt);
+ pr_err("set intf %d %d failed\n",
+ gspca_dev->iface, gspca_dev->alt);
gspca_dev->usb_err = ret;
goto out;
}
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
index e2ef41cf72d7..4dcc7e37f9fd 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/video/gspca/stv0680.c
@@ -27,6 +27,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "stv0680"
#include "gspca.h"
@@ -79,7 +81,7 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
val, 0, gspca_dev->usb_buf, size, 500);
if ((ret < 0) && (req != 0x0a))
- err("usb_control_msg error %i, request = 0x%x, error = %i",
+ pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n",
set, req, ret);
return ret;
@@ -236,7 +238,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
- err("Could not get descriptor 0100.");
+ pr_err("Could not get descriptor 0100\n");
return stv0680_handle_error(gspca_dev, -EIO);
}
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
index 2f3c3a606ce4..5b318faf9aa8 100644
--- a/drivers/media/video/gspca/stv06xx/Makefile
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \
stv06xx_pb0100.o \
stv06xx_st6422.o
-EXTRA_CFLAGS += -Idrivers/media/video/gspca
+ccflags-y += -Idrivers/media/video/gspca
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index abf1658fa33e..b1fca7db1015 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -27,6 +27,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/input.h>
#include "stv06xx_sensor.h"
@@ -189,7 +191,7 @@ int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
STV06XX_URB_MSG_TIMEOUT);
if (err < 0) {
- err("I2C: Read error writing address: %d", err);
+ pr_err("I2C: Read error writing address: %d\n", err);
return err;
}
@@ -213,14 +215,14 @@ static void stv06xx_dump_bridge(struct sd *sd)
int i;
u8 data, buf;
- info("Dumping all stv06xx bridge registers");
+ pr_info("Dumping all stv06xx bridge registers\n");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
- info("Read 0x%x from address 0x%x", data, i);
+ pr_info("Read 0x%x from address 0x%x\n", data, i);
}
- info("Testing stv06xx bridge registers for writability");
+ pr_info("Testing stv06xx bridge registers for writability\n");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
buf = data;
@@ -228,12 +230,12 @@ static void stv06xx_dump_bridge(struct sd *sd)
stv06xx_write_bridge(sd, i, 0xff);
stv06xx_read_bridge(sd, i, &data);
if (data == 0xff)
- info("Register 0x%x is read/write", i);
+ pr_info("Register 0x%x is read/write\n", i);
else if (data != buf)
- info("Register 0x%x is read/write,"
- " but only partially", i);
+ pr_info("Register 0x%x is read/write, but only partially\n",
+ i);
else
- info("Register 0x%x is read-only", i);
+ pr_info("Register 0x%x is read-only\n", i);
stv06xx_write_bridge(sd, i, buf);
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
index e0f63c51f40d..d270a5981afe 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -37,6 +37,8 @@
#define STV_ISOC_ENDPOINT_ADDR 0x81
+#define STV_R 0x0509
+
#define STV_REG23 0x0423
/* Control registers of the STV0600 ASIC */
@@ -61,7 +63,9 @@
/* Refers to the CIF 352x288 and QCIF 176x144 */
/* 1: 288 lines, 2: 144 lines */
-#define STV_Y_CTRL 0x15c3
+#define STV_Y_CTRL 0x15c3
+
+#define STV_RESET 0x1620
/* 0xa: 352 columns, 0x6: 176 columns */
#define STV_X_CTRL 0x1680
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
index b8156855f2b7..a8698b7a7566 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -28,6 +28,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_hdcs.h"
static const struct ctrl hdcs1x00_ctrl[] = {
@@ -428,7 +430,7 @@ static int hdcs_probe_1x00(struct sd *sd)
if (ret < 0 || sensor != 0x08)
return -ENODEV;
- info("HDCS-1000/1100 sensor detected");
+ pr_info("HDCS-1000/1100 sensor detected\n");
sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
@@ -487,7 +489,7 @@ static int hdcs_probe_1020(struct sd *sd)
if (ret < 0 || sensor != 0x10)
return -ENODEV;
- info("HDCS-1020 sensor detected");
+ pr_info("HDCS-1020 sensor detected\n");
sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
@@ -601,11 +603,11 @@ static int hdcs_dump(struct sd *sd)
{
u16 reg, val;
- info("Dumping sensor registers:");
+ pr_info("Dumping sensor registers:\n");
for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
stv06xx_read_sensor(sd, reg, &val);
- info("reg 0x%02x = 0x%02x", reg, val);
+ pr_info("reg 0x%02x = 0x%02x\n", reg, val);
}
return 0;
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
index 75a5b9c2f15f..26f14fc4a135 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -44,6 +44,8 @@
* PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_pb0100.h"
static const struct ctrl pb0100_ctrl[] = {
@@ -190,7 +192,7 @@ static int pb0100_probe(struct sd *sd)
if (!sensor_settings)
return -ENOMEM;
- info("Photobit pb0100 sensor detected");
+ pr_info("Photobit pb0100 sensor detected\n");
sd->gspca_dev.cam.cam_mode = pb0100_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
index 8a456de4970a..9940e035b3ab 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
@@ -26,6 +26,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_st6422.h"
/* controls */
@@ -136,7 +138,7 @@ static int st6422_probe(struct sd *sd)
if (sd->bridge != BRIDGE_ST6422)
return -ENODEV;
- info("st6422 sensor detected");
+ pr_info("st6422 sensor detected\n");
sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL);
if (!sensor_settings)
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
index f8398434c328..a5c69d9ebdd4 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -27,6 +27,8 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stv06xx_vv6410.h"
static struct v4l2_pix_format vv6410_mode[] = {
@@ -112,7 +114,7 @@ static int vv6410_probe(struct sd *sd)
return -ENODEV;
if (data == 0x19) {
- info("vv6410 sensor detected");
+ pr_info("vv6410 sensor detected\n");
sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
GFP_KERNEL);
@@ -138,18 +140,7 @@ static int vv6410_init(struct sd *sd)
s32 *sensor_settings = sd->sensor_priv;
for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
- /* if NULL then len contains single value */
- if (stv_bridge_init[i].data == NULL) {
- err = stv06xx_write_bridge(sd,
- stv_bridge_init[i].start,
- stv_bridge_init[i].len);
- } else {
- int j;
- for (j = 0; j < stv_bridge_init[i].len; j++)
- err = stv06xx_write_bridge(sd,
- stv_bridge_init[i].start + j,
- stv_bridge_init[i].data[j]);
- }
+ stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
}
if (err < 0)
@@ -183,15 +174,6 @@ static int vv6410_start(struct sd *sd)
struct cam *cam = &sd->gspca_dev.cam;
u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
- if (priv & VV6410_CROP_TO_QVGA) {
- PDEBUG(D_CONF, "Cropping to QVGA");
- stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
- stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
- } else {
- stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
- stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
- }
-
if (priv & VV6410_SUBSAMPLE) {
PDEBUG(D_CONF, "Enabling subsampling");
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
@@ -201,8 +183,8 @@ static int vv6410_start(struct sd *sd)
} else {
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
- stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
}
/* Turn on LED */
@@ -242,11 +224,11 @@ static int vv6410_dump(struct sd *sd)
u8 i;
int err = 0;
- info("Dumping all vv6410 sensor registers");
+ pr_info("Dumping all vv6410 sensor registers\n");
for (i = 0; i < 0xff && !err; i++) {
u16 data;
err = stv06xx_read_sensor(sd, i, &data);
- info("Register 0x%x contained 0x%x", i, data);
+ pr_info("Register 0x%x contained 0x%x\n", i, data);
}
return (err < 0) ? err : 0;
}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
index 7fe3587f5f71..a25b8873f2e6 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -211,49 +211,49 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
/* If NULL, only single value to write, stored in len */
struct stv_init {
- const u8 *data;
- u16 start;
- u8 len;
-};
-
-static const u8 x1500[] = { /* 0x1500 - 0x150f */
- 0x0b, 0xa7, 0xb7, 0x00, 0x00
-};
-
-static const u8 x1536[] = { /* 0x1536 - 0x153b */
- 0x02, 0x00, 0x60, 0x01, 0x20, 0x01
+ u16 addr;
+ u8 data;
};
static const struct stv_init stv_bridge_init[] = {
/* This reg is written twice. Some kind of reset? */
- {NULL, 0x1620, 0x80},
- {NULL, 0x1620, 0x00},
- {NULL, 0x1443, 0x00},
- {NULL, 0x1423, 0x04},
- {x1500, 0x1500, ARRAY_SIZE(x1500)},
- {x1536, 0x1536, ARRAY_SIZE(x1536)},
+ {STV_RESET, 0x80},
+ {STV_RESET, 0x00},
+ {STV_SCAN_RATE, 0x00},
+ {STV_I2C_FLUSH, 0x04},
+ {STV_REG00, 0x0b},
+ {STV_REG01, 0xa7},
+ {STV_REG02, 0xb7},
+ {STV_REG03, 0x00},
+ {STV_REG04, 0x00},
+ {0x1536, 0x02},
+ {0x1537, 0x00},
+ {0x1538, 0x60},
+ {0x1539, 0x01},
+ {0x153a, 0x20},
+ {0x153b, 0x01},
};
static const u8 vv6410_sensor_init[][2] = {
/* Setup registers */
- {VV6410_SETUP0, VV6410_SOFT_RESET},
- {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
/* Use shuffled read-out mode */
- {VV6410_SETUP1, BIT(6)},
- /* All modes to 1 */
- {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
- {VV6410_PINMAPPING, 0x00},
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
/* Pre-clock generator divide off */
- {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
- {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
/* System registers */
/* Enable voltage doubler */
- {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
- {VV6410_AT0, 0x00},
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
/* Power up audio, differential */
- {VV6410_AT1, BIT(4)|BIT(0)},
+ {VV6410_AT1, BIT(4) | BIT(0)},
};
#endif
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index 6ec232902183..c8909772435a 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "sunplus"
#include "gspca.h"
@@ -325,7 +327,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
- err("reg_r: buffer overflow");
+ pr_err("reg_r: buffer overflow\n");
return;
}
#endif
@@ -340,7 +342,7 @@ static void reg_r(struct gspca_dev *gspca_dev,
len ? gspca_dev->usb_buf : NULL, len,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -365,7 +367,7 @@ static void reg_w_1(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_w_1 err %d", ret);
+ pr_err("reg_w_1 err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -385,7 +387,7 @@ static void reg_w_riv(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
if (ret < 0) {
- err("reg_w_riv err %d", ret);
+ pr_err("reg_w_riv err %d\n", ret);
gspca_dev->usb_err = ret;
return;
}
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index d1d733b9359b..90f0877eb599 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -26,6 +26,8 @@
* Costantino Leandro
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "t613"
#include <linux/slab.h>
@@ -572,7 +574,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
tmpbuf = kmemdup(buffer, len, GFP_KERNEL);
if (!tmpbuf) {
- err("Out of memory");
+ pr_err("Out of memory\n");
return;
}
usb_control_msg(gspca_dev->dev,
@@ -598,7 +600,7 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
} else {
p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
if (!tmpbuf) {
- err("Out of memory");
+ pr_err("Out of memory\n");
return;
}
}
@@ -652,7 +654,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
}
byte = reg_r(gspca_dev, 0x0063);
if (byte != 0x17) {
- err("Bad sensor reset %02x", byte);
+ pr_err("Bad sensor reset %02x\n", byte);
/* continue? */
}
@@ -890,7 +892,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_OM6802;
break;
default:
- err("unknown sensor %04x", sensor_id);
+ pr_err("unknown sensor %04x\n", sensor_id);
return -EINVAL;
}
@@ -905,7 +907,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
break; /* OK */
}
if (i < 0) {
- err("Bad sensor reset %02x", test_byte);
+ pr_err("Bad sensor reset %02x\n", test_byte);
return -EIO;
}
reg_w_buf(gspca_dev, n2, sizeof n2);
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
new file mode 100644
index 000000000000..29596c59837f
--- /dev/null
+++ b/drivers/media/video/gspca/topro.c
@@ -0,0 +1,4989 @@
+/*
+ * Topro TP6800/6810 webcam driver.
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
+ * Copyright (C) 2008 Thomas Champagne (lafeuil@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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+
+MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+
+/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+1,
+#define JPEG_QT1_OFFSET 72
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+ /* Define Huffman table (thanks to Thomas Kaiser) */
+ 0xff, 0xc4, 0x01, 0x5e,
+ 0x00, 0x00, 0x02, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
+ 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
+ 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
+ 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
+ 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
+ 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
+ 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
+ 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
+ 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
+ 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
+ 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
+ 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
+ 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
+ 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
+ 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
+ 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+#define JPEG_HEIGHT_OFFSET 493
+ 0x01, 0xe0, /* height */
+ 0x02, 0x80, /* width */
+ 0x03, /* component number */
+ 0x01,
+ 0x21, /* samples Y = jpeg 422 */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+#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];
+
+ u8 framerate;
+ u8 quality; /* webcam current JPEG quality (0..16) */
+ s8 ag_cnt; /* autogain / start counter for tp6810 */
+#define AG_CNT_START 13 /* check gain every N frames */
+
+ u8 bridge;
+ u8 sensor;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+enum bridges {
+ BRIDGE_TP6800,
+ BRIDGE_TP6810,
+};
+
+enum sensors {
+ SENSOR_CX0342,
+ SENSOR_SOI763A, /* ~= ov7630 / ov7648 */
+ NSENSORS
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG}
+};
+
+/*
+ * JPEG quality
+ * index: webcam compression
+ * value: JPEG quality in %
+ */
+static const u8 jpeg_q[17] = {
+ 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
+};
+
+#define BULK_OUT_SIZE 0x20
+#if BULK_OUT_SIZE > USB_BUF_SZ
+#error "USB buffer too small"
+#endif
+
+static const u8 rates[] = {30, 20, 15, 10, 7, 5};
+static const struct framerates framerates[] = {
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ },
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ }
+};
+static const u8 rates_6810[] = {30, 15, 10, 7, 5};
+static const struct framerates framerates_6810[] = {
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ },
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ }
+};
+
+/*
+ * webcam quality in %
+ * the last value is the ultra fine quality
+ */
+
+/* TP6800 register offsets */
+#define TP6800_R10_SIF_TYPE 0x10
+#define TP6800_R11_SIF_CONTROL 0x11
+#define TP6800_R12_SIF_ADDR_S 0x12
+#define TP6800_R13_SIF_TX_DATA 0x13
+#define TP6800_R14_SIF_RX_DATA 0x14
+#define TP6800_R15_GPIO_PU 0x15
+#define TP6800_R16_GPIO_PD 0x16
+#define TP6800_R17_GPIO_IO 0x17
+#define TP6800_R18_GPIO_DATA 0x18
+#define TP6800_R19_SIF_ADDR_S2 0x19
+#define TP6800_R1A_SIF_TX_DATA2 0x1a
+#define TP6800_R1B_SIF_RX_DATA2 0x1b
+#define TP6800_R21_ENDP_1_CTL 0x21
+#define TP6800_R2F_TIMING_CFG 0x2f
+#define TP6800_R30_SENSOR_CFG 0x30
+#define TP6800_R31_PIXEL_START 0x31
+#define TP6800_R32_PIXEL_END_L 0x32
+#define TP6800_R33_PIXEL_END_H 0x33
+#define TP6800_R34_LINE_START 0x34
+#define TP6800_R35_LINE_END_L 0x35
+#define TP6800_R36_LINE_END_H 0x36
+#define TP6800_R37_FRONT_DARK_ST 0x37
+#define TP6800_R38_FRONT_DARK_END 0x38
+#define TP6800_R39_REAR_DARK_ST_L 0x39
+#define TP6800_R3A_REAR_DARK_ST_H 0x3a
+#define TP6800_R3B_REAR_DARK_END_L 0x3b
+#define TP6800_R3C_REAR_DARK_END_H 0x3c
+#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d
+#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e
+#define TP6800_R3F_FRAME_RATE 0x3f
+#define TP6800_R50 0x50
+#define TP6800_R51 0x51
+#define TP6800_R52 0x52
+#define TP6800_R53 0x53
+#define TP6800_R54_DARK_CFG 0x54
+#define TP6800_R55_GAMMA_R 0x55
+#define TP6800_R56_GAMMA_G 0x56
+#define TP6800_R57_GAMMA_B 0x57
+#define TP6800_R5C_EDGE_THRLD 0x5c
+#define TP6800_R5D_DEMOSAIC_CFG 0x5d
+#define TP6800_R78_FORMAT 0x78
+#define TP6800_R79_QUALITY 0x79
+#define TP6800_R7A_BLK_THRLD 0x7a
+
+/* CX0342 register offsets */
+
+#define CX0342_SENSOR_ID 0x00
+#define CX0342_VERSION_NO 0x01
+#define CX0342_ORG_X_L 0x02
+#define CX0342_ORG_X_H 0x03
+#define CX0342_ORG_Y_L 0x04
+#define CX0342_ORG_Y_H 0x05
+#define CX0342_STOP_X_L 0x06
+#define CX0342_STOP_X_H 0x07
+#define CX0342_STOP_Y_L 0x08
+#define CX0342_STOP_Y_H 0x09
+#define CX0342_FRAME_WIDTH_L 0x0a
+#define CX0342_FRAME_WIDTH_H 0x0b
+#define CX0342_FRAME_HEIGH_L 0x0c
+#define CX0342_FRAME_HEIGH_H 0x0d
+#define CX0342_EXPO_LINE_L 0x10
+#define CX0342_EXPO_LINE_H 0x11
+#define CX0342_EXPO_CLK_L 0x12
+#define CX0342_EXPO_CLK_H 0x13
+#define CX0342_RAW_GRGAIN_L 0x14
+#define CX0342_RAW_GRGAIN_H 0x15
+#define CX0342_RAW_GBGAIN_L 0x16
+#define CX0342_RAW_GBGAIN_H 0x17
+#define CX0342_RAW_RGAIN_L 0x18
+#define CX0342_RAW_RGAIN_H 0x19
+#define CX0342_RAW_BGAIN_L 0x1a
+#define CX0342_RAW_BGAIN_H 0x1b
+#define CX0342_GLOBAL_GAIN 0x1c
+#define CX0342_SYS_CTRL_0 0x20
+#define CX0342_SYS_CTRL_1 0x21
+#define CX0342_SYS_CTRL_2 0x22
+#define CX0342_BYPASS_MODE 0x23
+#define CX0342_SYS_CTRL_3 0x24
+#define CX0342_TIMING_EN 0x25
+#define CX0342_OUTPUT_CTRL 0x26
+#define CX0342_AUTO_ADC_CALIB 0x27
+#define CX0342_SYS_CTRL_4 0x28
+#define CX0342_ADCGN 0x30
+#define CX0342_SLPCR 0x31
+#define CX0342_SLPFN_LO 0x32
+#define CX0342_ADC_CTL 0x33
+#define CX0342_LVRST_BLBIAS 0x34
+#define CX0342_VTHSEL 0x35
+#define CX0342_RAMP_RIV 0x36
+#define CX0342_LDOSEL 0x37
+#define CX0342_CLOCK_GEN 0x40
+#define CX0342_SOFT_RESET 0x41
+#define CX0342_PLL 0x42
+#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43
+#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44
+#define CX0342_DR_ENH_PULSE_POS_L 0x45
+#define CX0342_DR_ENH_PULSE_POS_H 0x46
+#define CX0342_DR_ENH_PULSE_WIDTH 0x47
+#define CX0342_AS_CURRENT_CNT_L 0x48
+#define CX0342_AS_CURRENT_CNT_H 0x49
+#define CX0342_AS_PREVIOUS_CNT_L 0x4a
+#define CX0342_AS_PREVIOUS_CNT_H 0x4b
+#define CX0342_SPV_VALUE_L 0x4c
+#define CX0342_SPV_VALUE_H 0x4d
+#define CX0342_GPXLTHD_L 0x50
+#define CX0342_GPXLTHD_H 0x51
+#define CX0342_RBPXLTHD_L 0x52
+#define CX0342_RBPXLTHD_H 0x53
+#define CX0342_PLANETHD_L 0x54
+#define CX0342_PLANETHD_H 0x55
+#define CX0342_ROWDARK_TH 0x56
+#define CX0342_ROWDARK_TOL 0x57
+#define CX0342_RB_GAP_L 0x58
+#define CX0342_RB_GAP_H 0x59
+#define CX0342_G_GAP_L 0x5a
+#define CX0342_G_GAP_H 0x5b
+#define CX0342_AUTO_ROW_DARK 0x60
+#define CX0342_MANUAL_DARK_VALUE 0x61
+#define CX0342_GB_DARK_OFFSET 0x62
+#define CX0342_GR_DARK_OFFSET 0x63
+#define CX0342_RED_DARK_OFFSET 0x64
+#define CX0342_BLUE_DARK_OFFSET 0x65
+#define CX0342_DATA_SCALING_MULTI 0x66
+#define CX0342_AUTOD_Q_FRAME 0x67
+#define CX0342_AUTOD_ALLOW_VARI 0x68
+#define CX0342_AUTO_DARK_VALUE_L 0x69
+#define CX0342_AUTO_DARK_VALUE_H 0x6a
+#define CX0342_IO_CTRL_0 0x70
+#define CX0342_IO_CTRL_1 0x71
+#define CX0342_IO_CTRL_2 0x72
+#define CX0342_IDLE_CTRL 0x73
+#define CX0342_TEST_MODE 0x74
+#define CX0342_FRAME_FIX_DATA_TEST 0x75
+#define CX0342_FRAME_CNT_TEST 0x76
+#define CX0342_RST_OVERFLOW_L 0x80
+#define CX0342_RST_OVERFLOW_H 0x81
+#define CX0342_RST_UNDERFLOW_L 0x82
+#define CX0342_RST_UNDERFLOW_H 0x83
+#define CX0342_DATA_OVERFLOW_L 0x84
+#define CX0342_DATA_OVERFLOW_H 0x85
+#define CX0342_DATA_UNDERFLOW_L 0x86
+#define CX0342_DATA_UNDERFLOW_H 0x87
+#define CX0342_CHANNEL_0_0_L_irst 0x90
+#define CX0342_CHANNEL_0_0_H_irst 0x91
+#define CX0342_CHANNEL_0_1_L_irst 0x92
+#define CX0342_CHANNEL_0_1_H_irst 0x93
+#define CX0342_CHANNEL_0_2_L_irst 0x94
+#define CX0342_CHANNEL_0_2_H_irst 0x95
+#define CX0342_CHANNEL_0_3_L_irst 0x96
+#define CX0342_CHANNEL_0_3_H_irst 0x97
+#define CX0342_CHANNEL_0_4_L_irst 0x98
+#define CX0342_CHANNEL_0_4_H_irst 0x99
+#define CX0342_CHANNEL_0_5_L_irst 0x9a
+#define CX0342_CHANNEL_0_5_H_irst 0x9b
+#define CX0342_CHANNEL_0_6_L_irst 0x9c
+#define CX0342_CHANNEL_0_6_H_irst 0x9d
+#define CX0342_CHANNEL_0_7_L_irst 0x9e
+#define CX0342_CHANNEL_0_7_H_irst 0x9f
+#define CX0342_CHANNEL_1_0_L_itx 0xa0
+#define CX0342_CHANNEL_1_0_H_itx 0xa1
+#define CX0342_CHANNEL_1_1_L_itx 0xa2
+#define CX0342_CHANNEL_1_1_H_itx 0xa3
+#define CX0342_CHANNEL_1_2_L_itx 0xa4
+#define CX0342_CHANNEL_1_2_H_itx 0xa5
+#define CX0342_CHANNEL_1_3_L_itx 0xa6
+#define CX0342_CHANNEL_1_3_H_itx 0xa7
+#define CX0342_CHANNEL_1_4_L_itx 0xa8
+#define CX0342_CHANNEL_1_4_H_itx 0xa9
+#define CX0342_CHANNEL_1_5_L_itx 0xaa
+#define CX0342_CHANNEL_1_5_H_itx 0xab
+#define CX0342_CHANNEL_1_6_L_itx 0xac
+#define CX0342_CHANNEL_1_6_H_itx 0xad
+#define CX0342_CHANNEL_1_7_L_itx 0xae
+#define CX0342_CHANNEL_1_7_H_itx 0xaf
+#define CX0342_CHANNEL_2_0_L_iwl 0xb0
+#define CX0342_CHANNEL_2_0_H_iwl 0xb1
+#define CX0342_CHANNEL_2_1_L_iwl 0xb2
+#define CX0342_CHANNEL_2_1_H_iwl 0xb3
+#define CX0342_CHANNEL_2_2_L_iwl 0xb4
+#define CX0342_CHANNEL_2_2_H_iwl 0xb5
+#define CX0342_CHANNEL_2_3_L_iwl 0xb6
+#define CX0342_CHANNEL_2_3_H_iwl 0xb7
+#define CX0342_CHANNEL_2_4_L_iwl 0xb8
+#define CX0342_CHANNEL_2_4_H_iwl 0xb9
+#define CX0342_CHANNEL_2_5_L_iwl 0xba
+#define CX0342_CHANNEL_2_5_H_iwl 0xbb
+#define CX0342_CHANNEL_2_6_L_iwl 0xbc
+#define CX0342_CHANNEL_2_6_H_iwl 0xbd
+#define CX0342_CHANNEL_2_7_L_iwl 0xbe
+#define CX0342_CHANNEL_2_7_H_iwl 0xbf
+#define CX0342_CHANNEL_3_0_L_ensp 0xc0
+#define CX0342_CHANNEL_3_0_H_ensp 0xc1
+#define CX0342_CHANNEL_3_1_L_ensp 0xc2
+#define CX0342_CHANNEL_3_1_H_ensp 0xc3
+#define CX0342_CHANNEL_3_2_L_ensp 0xc4
+#define CX0342_CHANNEL_3_2_H_ensp 0xc5
+#define CX0342_CHANNEL_3_3_L_ensp 0xc6
+#define CX0342_CHANNEL_3_3_H_ensp 0xc7
+#define CX0342_CHANNEL_3_4_L_ensp 0xc8
+#define CX0342_CHANNEL_3_4_H_ensp 0xc9
+#define CX0342_CHANNEL_3_5_L_ensp 0xca
+#define CX0342_CHANNEL_3_5_H_ensp 0xcb
+#define CX0342_CHANNEL_3_6_L_ensp 0xcc
+#define CX0342_CHANNEL_3_6_H_ensp 0xcd
+#define CX0342_CHANNEL_3_7_L_ensp 0xce
+#define CX0342_CHANNEL_3_7_H_ensp 0xcf
+#define CX0342_CHANNEL_4_0_L_sela 0xd0
+#define CX0342_CHANNEL_4_0_H_sela 0xd1
+#define CX0342_CHANNEL_4_1_L_sela 0xd2
+#define CX0342_CHANNEL_4_1_H_sela 0xd3
+#define CX0342_CHANNEL_5_0_L_intla 0xe0
+#define CX0342_CHANNEL_5_0_H_intla 0xe1
+#define CX0342_CHANNEL_5_1_L_intla 0xe2
+#define CX0342_CHANNEL_5_1_H_intla 0xe3
+#define CX0342_CHANNEL_5_2_L_intla 0xe4
+#define CX0342_CHANNEL_5_2_H_intla 0xe5
+#define CX0342_CHANNEL_5_3_L_intla 0xe6
+#define CX0342_CHANNEL_5_3_H_intla 0xe7
+#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
+#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
+#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2
+#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3
+#define CX0342_SENSOR_HEIGHT_L 0xfb
+#define CX0342_SENSOR_HEIGHT_H 0xfc
+#define CX0342_SENSOR_WIDTH_L 0xfd
+#define CX0342_SENSOR_WIDTH_H 0xfe
+#define CX0342_VSYNC_HSYNC_READ 0xff
+
+struct cmd {
+ u8 reg;
+ u8 val;
+};
+
+static const u8 DQT[17][130] = {
+ /* Define quantization table (thanks to Thomas Kaiser) */
+ { /* Quality 0 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
+ 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ },
+ { /* Quality 1 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x01,
+ 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
+ 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ },
+ { /* Quality 2 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x01,
+ 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
+ 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ },
+ { /* Quality 3 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
+ 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x01,
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ },
+ { /* Quality 4 */
+ 0x00,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
+ 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01,
+ 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
+ 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ },
+ { /* Quality 5 */
+ 0x00,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x01,
+ 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
+ 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ },
+ { /* Quality 6 */
+ 0x00,
+ 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x01,
+ 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
+ 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ },
+ { /* Quality 7 */
+ 0x00,
+ 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
+ 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x01,
+ 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+ 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ },
+ { /* Quality 8 */
+ 0x00,
+ 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x01,
+ 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
+ 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ },
+ { /* Quality 9 */
+ 0x00,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x01,
+ 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+ 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 10 */
+ 0x00,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01,
+ 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
+ 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 11 */
+ 0x00,
+ 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
+ 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x01,
+ 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
+ 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 12 */
+ 0x00,
+ 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
+ 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x01,
+ 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
+ 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 13 */
+ 0x00,
+ 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x01,
+ 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
+ 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 14 */
+ 0x00,
+ 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x01,
+ 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
+ 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 15 */
+ 0x00,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
+ 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x01,
+ 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
+ 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 16-31 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ }
+};
+
+static const struct cmd tp6810_cx_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x37},
+ {TP6800_R30_SENSOR_CFG, 0x10},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {0x4b, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x83},
+ {TP6800_R79_QUALITY, 0x05},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {0x7c, 0x04},
+ {0x25, 0x14},
+ {0x26, 0x0f},
+ {0x7b, 0x10},
+};
+
+static const struct cmd tp6810_ov_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x86},
+ {0x25, 0x18},
+ {0x26, 0x0f},
+ {0x7b, 0x90},
+};
+
+static const struct cmd tp6810_bridge_start[] = {
+ {0x59, 0x88},
+ {0x5a, 0x0f},
+ {0x5b, 0x4e},
+ {TP6800_R5C_EDGE_THRLD, 0x63},
+ {TP6800_R5D_DEMOSAIC_CFG, 0x00},
+ {0x03, 0x7f},
+ {0x04, 0x80},
+ {0x06, 0x00},
+ {0x00, 0x00},
+};
+
+static const struct cmd tp6810_late_start[] = {
+ {0x7d, 0x01},
+ {0xb0, 0x04},
+ {0xb1, 0x04},
+ {0xb2, 0x04},
+ {0xb3, 0x04},
+ {0xb4, 0x04},
+ {0xb5, 0x04},
+ {0xb6, 0x08},
+ {0xb7, 0x08},
+ {0xb8, 0x04},
+ {0xb9, 0x04},
+ {0xba, 0x04},
+ {0xbb, 0x04},
+ {0xbc, 0x04},
+ {0xbd, 0x08},
+ {0xbe, 0x08},
+ {0xbf, 0x08},
+ {0xc0, 0x04},
+ {0xc1, 0x04},
+ {0xc2, 0x08},
+ {0xc3, 0x08},
+ {0xc4, 0x08},
+ {0xc5, 0x08},
+ {0xc6, 0x08},
+ {0xc7, 0x13},
+ {0xc8, 0x04},
+ {0xc9, 0x08},
+ {0xca, 0x08},
+ {0xcb, 0x08},
+ {0xcc, 0x08},
+ {0xcd, 0x08},
+ {0xce, 0x13},
+ {0xcf, 0x13},
+ {0xd0, 0x08},
+ {0xd1, 0x08},
+ {0xd2, 0x08},
+ {0xd3, 0x08},
+ {0xd4, 0x08},
+ {0xd5, 0x13},
+ {0xd6, 0x13},
+ {0xd7, 0x13},
+ {0xd8, 0x08},
+ {0xd9, 0x08},
+ {0xda, 0x08},
+ {0xdb, 0x08},
+ {0xdc, 0x13},
+ {0xdd, 0x13},
+ {0xde, 0x13},
+ {0xdf, 0x13},
+ {0xe0, 0x08},
+ {0xe1, 0x08},
+ {0xe2, 0x08},
+ {0xe3, 0x13},
+ {0xe4, 0x13},
+ {0xe5, 0x13},
+ {0xe6, 0x13},
+ {0xe7, 0x13},
+ {0xe8, 0x08},
+ {0xe9, 0x08},
+ {0xea, 0x13},
+ {0xeb, 0x13},
+ {0xec, 0x13},
+ {0xed, 0x13},
+ {0xee, 0x13},
+ {0xef, 0x13},
+ {0x7d, 0x02},
+
+ /* later after isoc start */
+ {0x7d, 0x08},
+ {0x7d, 0x00},
+};
+
+static const struct cmd cx0342_timing_seq[] = {
+ {CX0342_CHANNEL_0_1_L_irst, 0x20},
+ {CX0342_CHANNEL_0_2_L_irst, 0x24},
+ {CX0342_CHANNEL_0_2_H_irst, 0x00},
+ {CX0342_CHANNEL_0_3_L_irst, 0x2f},
+ {CX0342_CHANNEL_0_3_H_irst, 0x00},
+ {CX0342_CHANNEL_1_0_L_itx, 0x02},
+ {CX0342_CHANNEL_1_0_H_itx, 0x00},
+ {CX0342_CHANNEL_1_1_L_itx, 0x20},
+ {CX0342_CHANNEL_1_1_H_itx, 0x00},
+ {CX0342_CHANNEL_1_2_L_itx, 0xe4},
+ {CX0342_CHANNEL_1_2_H_itx, 0x00},
+ {CX0342_CHANNEL_1_3_L_itx, 0xee},
+ {CX0342_CHANNEL_1_3_H_itx, 0x00},
+ {CX0342_CHANNEL_2_0_L_iwl, 0x30},
+ {CX0342_CHANNEL_2_0_H_iwl, 0x00},
+ {CX0342_CHANNEL_3_0_L_ensp, 0x34},
+ {CX0342_CHANNEL_3_1_L_ensp, 0xe2},
+ {CX0342_CHANNEL_3_1_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_2_L_ensp, 0xf6},
+ {CX0342_CHANNEL_3_2_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_3_L_ensp, 0xf4},
+ {CX0342_CHANNEL_3_3_H_ensp, 0x02},
+ {CX0342_CHANNEL_4_0_L_sela, 0x26},
+ {CX0342_CHANNEL_4_0_H_sela, 0x00},
+ {CX0342_CHANNEL_4_1_L_sela, 0xe2},
+ {CX0342_CHANNEL_4_1_H_sela, 0x00},
+ {CX0342_CHANNEL_5_0_L_intla, 0x26},
+ {CX0342_CHANNEL_5_1_L_intla, 0x29},
+ {CX0342_CHANNEL_5_2_L_intla, 0xf0},
+ {CX0342_CHANNEL_5_2_H_intla, 0x00},
+ {CX0342_CHANNEL_5_3_L_intla, 0xf3},
+ {CX0342_CHANNEL_5_3_H_intla, 0x00},
+ {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
+ {CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
+ {CX0342_TIMING_EN, 0x01},
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+ int height,
+ int width)
+{
+ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+}
+
+/* set the JPEG quality for sensor soi763a */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+ int quality)
+{
+ int i, sc;
+
+ if (quality < 50)
+ sc = 5000 / quality;
+ else
+ sc = 200 - quality * 2;
+ for (i = 0; i < 64; i++) {
+ jpeg_hdr[JPEG_QT0_OFFSET + i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ jpeg_hdr[JPEG_QT1_OFFSET + i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x0e,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* the returned value is in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev, u8 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x0d,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1, 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ reg_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return 0;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1; /* error */
+}
+
+static void i2c_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ i2c_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int v;
+
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
+ v = gspca_dev->usb_buf[0];
+ if (sd->bridge == BRIDGE_TP6800)
+ return v;
+ if (len > 1) {
+ reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
+ v |= (gspca_dev->usb_buf[0] << 8);
+ }
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return v;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+ u8 tag,
+ const u8 *data,
+ int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int count, actual_count, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (;;) {
+ count = length > BULK_OUT_SIZE - 1
+ ? BULK_OUT_SIZE - 1 : length;
+ gspca_dev->usb_buf[0] = tag;
+ memcpy(&gspca_dev->usb_buf[1], data, count);
+ ret = usb_bulk_msg(dev,
+ usb_sndbulkpipe(dev, 3),
+ gspca_dev->usb_buf, count + 1,
+ &actual_count, 500);
+ if (ret < 0) {
+ pr_err("bulk write error %d tag=%02x\n",
+ ret, tag);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ length -= count;
+ if (length <= 0)
+ break;
+ data += count;
+ }
+}
+
+static int probe_6810(struct gspca_dev *gspca_dev)
+{
+ u8 gpio;
+ int ret;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ gpio = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return SENSOR_SOI763A;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return -2;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -3;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -4;
+
+ ret = i2c_r(gspca_dev, 0x01, 1);
+ if (ret > 0)
+ return -5;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x00, 2);
+ if (ret > 0)
+ return -6;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x36, 2);
+ if (ret > 0)
+ return -7;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
+ if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
+ return -8;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return SENSOR_CX0342;
+ return -9;
+}
+
+static void cx0342_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x48},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x28},
+ {CX0342_RBPXLTHD_L, 0x28},
+ {CX0342_PLANETHD_L, 0x50},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_TIMING_EN, 0x01},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+}
+
+static void soi763a_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x54},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x00, 0x00},
+ {0x01, 0x80},
+ {0x02, 0x80},
+ {0x03, 0x90},
+ {0x04, 0x20},
+ {0x05, 0x20},
+ {0x06, 0x80},
+ {0x07, 0x00},
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x76}, /* 7630 = soi673a */
+ {0x0b, 0x30},
+ {0x0c, 0x20},
+ {0x0d, 0x20},
+ {0x0e, 0xff},
+ {0x0f, 0xff},
+ {0x10, 0x41},
+ {0x15, 0x14},
+ {0x11, 0x40},
+ {0x12, 0x48},
+ {0x13, 0x80},
+ {0x14, 0x80},
+ {0x16, 0x03},
+ {0x28, 0xb0},
+ {0x71, 0x20},
+ {0x75, 0x8e},
+ {0x17, 0x1b},
+ {0x18, 0xbd},
+ {0x19, 0x05},
+ {0x1a, 0xf6},
+ {0x1b, 0x04},
+ {0x1c, 0x7f}, /* omnivision */
+ {0x1d, 0xa2},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x45},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0xa0},
+ {0x27, 0x9a},
+ {0x29, 0x30},
+ {0x2a, 0x80},
+ {0x2b, 0x00},
+ {0x2c, 0xac},
+ {0x2d, 0x05},
+ {0x2e, 0x80},
+ {0x2f, 0x3c},
+ {0x30, 0x22},
+ {0x31, 0x00},
+ {0x32, 0x86},
+ {0x33, 0x08},
+ {0x34, 0xff},
+ {0x35, 0xff},
+ {0x36, 0xff},
+ {0x37, 0xff},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0xfe},
+ {0x3b, 0xfe},
+ {0x3c, 0xfe},
+ {0x3d, 0xfe},
+ {0x3e, 0xfe},
+ {0x3f, 0x71},
+ {0x40, 0xff},
+ {0x41, 0xff},
+ {0x42, 0xff},
+ {0x43, 0xff},
+ {0x44, 0xff},
+ {0x45, 0xff},
+ {0x46, 0xff},
+ {0x47, 0xff},
+ {0x48, 0xff},
+ {0x49, 0xff},
+ {0x4a, 0xfe},
+ {0x4b, 0xff},
+ {0x4c, 0x00},
+ {0x4d, 0x00},
+ {0x4e, 0xff},
+ {0x4f, 0xff},
+ {0x50, 0xff},
+ {0x51, 0xff},
+ {0x52, 0xff},
+ {0x53, 0xff},
+ {0x54, 0xff},
+ {0x55, 0xff},
+ {0x56, 0xff},
+ {0x57, 0xff},
+ {0x58, 0xff},
+ {0x59, 0xff},
+ {0x5a, 0xff},
+ {0x5b, 0xfe},
+ {0x5c, 0xff},
+ {0x5d, 0x8f},
+ {0x5e, 0xff},
+ {0x5f, 0x8f},
+ {0x60, 0xa2},
+ {0x61, 0x4a},
+ {0x62, 0xf3},
+ {0x63, 0x75},
+ {0x64, 0xf0},
+ {0x65, 0x00},
+ {0x66, 0x55},
+ {0x67, 0x92},
+ {0x68, 0xa0},
+ {0x69, 0x4a},
+ {0x6a, 0x22},
+ {0x6b, 0x00},
+ {0x6c, 0x33},
+ {0x6d, 0x44},
+ {0x6e, 0x22},
+ {0x6f, 0x84},
+ {0x70, 0x0b},
+ {0x72, 0x10},
+ {0x73, 0x50},
+ {0x74, 0x21},
+ {0x76, 0x00},
+ {0x77, 0xa5},
+ {0x78, 0x80},
+ {0x79, 0x80},
+ {0x7a, 0x80},
+ {0x7b, 0xe2},
+ {0x7c, 0x00},
+ {0x7d, 0xf7},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+}
+
+/* set the gain and exposure */
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ int expo;
+
+ expo = (sd->ctrls[EXPOSURE].val << 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);
+ 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);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
+ sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
+ return;
+ }
+
+ /* soi763a */
+ i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
+ sd->ctrls[EXPOSURE].val);
+/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
+ i2c_w(gspca_dev, 0x00, /* gain */
+ sd->ctrls[GAIN].val);
+}
+
+/* set the JPEG quantization tables */
+static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* update the jpeg quantization tables */
+ PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
+ sd->quality = q;
+ if (q > 16)
+ q = 16;
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
+ else
+ memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
+ DQT[q], sizeof DQT[0]);
+}
+
+/* set the JPEG compression quality factor */
+static void setquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 q;
+
+ q = sd->ctrls[QUALITY].val;
+ if (q != 16)
+ q = 15 - q;
+
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, q);
+
+ /* auto quality */
+ if (q == 15 && sd->bridge == BRIDGE_TP6810) {
+ msleep(4);
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
+ }
+}
+
+static const u8 color_null[18] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8 color_gain[NSENSORS][18] = {
+[SENSOR_CX0342] =
+ {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */
+ 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+[SENSOR_SOI763A] =
+ {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */
+ 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+};
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ 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 */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 1 - from tp6810 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
+ 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
+ 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
+ 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
+ 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
+ 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
+ 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
+ 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
+ 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
+ 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
+ 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
+ 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
+ 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+ 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
+ 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+ 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
+ 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
+ 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
+ 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
+ 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
+ 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
+ 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
+ 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
+ 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
+ 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
+ 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
+ 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
+ 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 2 */
+ {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
+ 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
+ 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
+ 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
+ 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
+ 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
+ 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
+ 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
+ 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
+ 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+ 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
+ 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
+ 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
+ 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
+ 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
+ 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 3 - from tp6810 + cx0342 */
+ {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
+ 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
+ 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
+ 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
+ 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
+ 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
+ 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
+ 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
+ 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
+ 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
+ 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
+ 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
+ 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
+ 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
+ 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
+ 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 4 - from tp6800 + soi763a */
+ {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
+ 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
+ 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
+ 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
+ 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
+ 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
+ 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
+ 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
+ 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
+ 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
+ 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
+ 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
+ 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
+ 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
+ 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
+ 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
+ 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
+ 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
+ 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 5 */
+ {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
+ 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
+ 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
+ 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
+ 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
+ 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
+ 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
+ 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
+ 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
+ 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
+ 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
+ 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
+ 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
+ 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
+ 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
+ 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
+ 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
+ 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
+ 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
+ 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
+ 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
+ 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
+ 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
+ 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
+ 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
+ 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ },
+ };
+
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ 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);
+ if (sd->bridge == BRIDGE_TP6810) {
+ int i;
+
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R55_GAMMA_R,
+ gamma_tb[gamma][0][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R56_GAMMA_G,
+ gamma_tb[gamma][1][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R57_GAMMA_B,
+ gamma_tb[gamma][2][i]);
+ reg_w(gspca_dev, 0x02, 0x28);
+ }
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+/* msleep(50); */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ val = sd->ctrls[SHARPNESS].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;
+ reg_w(gspca_dev, 0x59, val);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ 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));
+ }
+}
+
+/* set the resolution for sensor cx0342 */
+static void set_resolution(struct gspca_dev *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);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
+ } else {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ }
+ 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);
+}
+
+/* convert the frame rate to a tp68x0 value */
+static int get_fr_idx(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
+ if (sd->framerate >= rates[i])
+ break;
+ }
+ i = 6 - i; /* 1 = 5fps .. 6 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 6 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 0x86; /* 15 fps */
+ } else {
+ for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
+ if (sd->framerate >= rates_6810[i])
+ break;
+ }
+ i = 7 - i; /* 3 = 5fps .. 7 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 7 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 6; /* 15 fps */
+ i |= 0x80; /* clock * 1 */
+ }
+ return i;
+}
+
+static void setframerate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 fr_idx;
+
+ fr_idx = get_fr_idx(gspca_dev);
+
+ if (sd->bridge == BRIDGE_TP6810) {
+ reg_r(gspca_dev, 0x7b);
+ reg_w(gspca_dev, 0x7b,
+ sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
+ if (sd->ctrls[EXPOSURE].val >= 128)
+ fr_idx = 0xf0; /* lower frame rate */
+ }
+
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
+
+ if (sd->sensor == SENSOR_CX0342)
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev)
+{
+ 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)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ 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;
+ }
+ 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)
+{
+ 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);
+}
+
+/* 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;
+
+ sd->bridge = id->driver_info;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
+ framerates : framerates_6810;
+
+ sd->framerate = 30; /* default: 30 fps */
+ gspca_dev->cam.ctrls = sd->ctrls;
+ return 0;
+}
+
+/* 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;
+ static const struct cmd tp6800_preinit[] = {
+ {TP6800_R10_SIF_TYPE, 0x01}, /* sif */
+ {TP6800_R11_SIF_CONTROL, 0x01},
+ {TP6800_R15_GPIO_PU, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x9f},
+ {TP6800_R17_GPIO_IO, 0x80},
+ {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */
+ };
+ static const struct cmd tp6810_preinit[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R15_GPIO_PU, 0x6f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w_buf(gspca_dev, tp6800_preinit,
+ ARRAY_SIZE(tp6800_preinit));
+ else
+ reg_w_buf(gspca_dev, tp6810_preinit,
+ ARRAY_SIZE(tp6810_preinit));
+ msleep(15);
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
+/* values:
+ * 0x80: snapshot button
+ * 0x40: LED
+ * 0x20: (bridge / sensor) reset for tp6810 ?
+ * 0x07: sensor type ?
+ */
+
+ /* guess the sensor type */
+ if (force_sensor >= 0) {
+ sd->sensor = force_sensor;
+ } else {
+ if (sd->bridge == BRIDGE_TP6800) {
+/*fixme: not sure this is working*/
+ switch (gspca_dev->usb_buf[0] & 0x07) {
+ case 0:
+ sd->sensor = SENSOR_SOI763A;
+ break;
+ case 1:
+ sd->sensor = SENSOR_CX0342;
+ break;
+ }
+ } else {
+ int sensor;
+
+ sensor = probe_6810(gspca_dev);
+ if (sensor < 0) {
+ pr_warn("Unknown sensor %d - forced to soi763a\n",
+ -sensor);
+ sensor = SENSOR_SOI763A;
+ }
+ sd->sensor = sensor;
+ }
+ }
+ 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;
+}
+
+/* This function is called before choosing the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd cx_sensor_init[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x37},
+ {CX0342_EXPO_LINE_H, 0x01},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd cx_bridge_init[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd ov_sensor_init[] = {
+ {0x10, 0x75}, /* exposure */
+ {0x76, 0x03},
+ {0x00, 0x00}, /* gain */
+ };
+ static const struct cmd ov_bridge_init[] = {
+ {0x7b, 0x90},
+ {TP6800_R3F_FRAME_RATE, 0x87},
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ if (sd->sensor == SENSOR_CX0342) {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, cx_sensor_init,
+ ARRAY_SIZE(cx_sensor_init));
+ reg_w_buf(gspca_dev, cx_bridge_init,
+ ARRAY_SIZE(cx_bridge_init));
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ } else {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
+ i2c_w_buf(gspca_dev, ov_sensor_init,
+ ARRAY_SIZE(ov_sensor_init));
+ reg_r(gspca_dev, 0x7b);
+ reg_w_buf(gspca_dev, ov_bridge_init,
+ ARRAY_SIZE(ov_bridge_init));
+ }
+ reg_w(gspca_dev, TP6800_R78_FORMAT,
+ gspca_dev->curr_mode ? 0x00 : 0x01);
+ return gspca_dev->usb_err;
+}
+
+static void set_led(struct gspca_dev *gspca_dev, int on)
+{
+ u8 data;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ data = gspca_dev->usb_buf[0];
+ if (on)
+ data &= ~0x40;
+ else
+ data |= 0x40;
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
+}
+
+static void cx0342_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+/*fixme: is this usefull?*/
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R31_PIXEL_START, 0x0b},
+ {TP6800_R32_PIXEL_END_L, 0x8a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x00},
+ {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x16},
+ {CX0342_RBPXLTHD_L, 0x16},
+ {CX0342_PLANETHD_L, 0xc0},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ 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);
+ set_led(gspca_dev, 1);
+ set_resolution(gspca_dev);
+}
+
+static void cx0342_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd sensor_init_2[] = {
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_2[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x08},
+ {TP6800_R32_PIXEL_END_L, 0x87},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd sensor_init_3[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_5[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd sensor_init_4[] = {
+ {CX0342_EXPO_LINE_L, 0xd3},
+ {CX0342_EXPO_LINE_H, 0x01},
+/*fixme: gains, but 00..80 only*/
+ {CX0342_RAW_GRGAIN_L, 0x40},
+ {CX0342_RAW_GBGAIN_L, 0x40},
+ {CX0342_RAW_RGAIN_L, 0x40},
+ {CX0342_RAW_BGAIN_L, 0x40},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd sensor_init_5[] = {
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+ setsharpness(gspca_dev);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
+
+ set_led(gspca_dev, 1);
+/* setquality(gspca_dev); */
+}
+
+static void soi763a_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+
+ {TP6800_R2F_TIMING_CFG, 0x46},
+ {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */
+ {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
+
+ {TP6800_R31_PIXEL_START, 0x1b},
+ {TP6800_R32_PIXEL_END_L, 0x9a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0f},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x01}, /* qvga */
+ {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */
+ {TP6800_R1A_SIF_TX_DATA2, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x12, 0x48}, /* mirror - RGB */
+ {0x13, 0xa0}, /* clock - no AGC nor AEC */
+ {0x03, 0xa4}, /* saturation */
+ {0x04, 0x30}, /* hue */
+ {0x05, 0x88}, /* contrast */
+ {0x06, 0x60}, /* brightness */
+ {0x10, 0x41}, /* AEC */
+ {0x11, 0x40}, /* clock rate */
+ {0x13, 0xa0},
+ {0x14, 0x00}, /* 640x480 */
+ {0x15, 0x14},
+ {0x1f, 0x41},
+ {0x20, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0x00},
+ {0x27, 0xe2},
+ {0x28, 0xb0},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2d, 0x81},
+ {0x2f, 0x9d},
+ {0x60, 0x80},
+ {0x61, 0x00},
+ {0x62, 0x88},
+ {0x63, 0x11},
+ {0x64, 0x89},
+ {0x65, 0x00},
+ {0x67, 0x94},
+ {0x68, 0x7a},
+ {0x69, 0x0f},
+ {0x6c, 0x80},
+ {0x6d, 0x80},
+ {0x6e, 0x80},
+ {0x6f, 0xff},
+ {0x71, 0x20},
+ {0x74, 0x20},
+ {0x75, 0x86},
+ {0x77, 0xb5},
+ {0x17, 0x18}, /* H href start */
+ {0x18, 0xbf}, /* H href end */
+ {0x19, 0x03}, /* V start */
+ {0x1a, 0xf8}, /* V end */
+ {0x01, 0x80}, /* blue gain */
+ {0x02, 0x80}, /* red gain */
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+
+ setsharpness(gspca_dev);
+
+ 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);
+}
+
+static void soi763a_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd bridge_init_2[] = {
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x20},
+ {TP6800_R32_PIXEL_END_L, 0x9f},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x13},
+ {TP6800_R35_LINE_END_L, 0xf8},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd bridge_init_6[] = {
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x5f},
+ {0x0b, 0x80},
+ };
+
+ 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);
+ 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));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4f, 0x00);
+ reg_w(gspca_dev, 0x4e, 0x7c);
+ }
+
+ reg_w(gspca_dev, 0x00, 0x00);
+
+ setsharpness(gspca_dev);
+ 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);
+ reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
+ set_dqt(gspca_dev, sd->quality);
+ if (sd->bridge == BRIDGE_TP6800) {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6800_start(gspca_dev);
+ else
+ soi763a_6800_start(gspca_dev);
+ } else {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6810_start(gspca_dev);
+ else
+ soi763a_6810_start(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_late_start,
+ ARRAY_SIZE(tp6810_late_start));
+ 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);
+ }
+
+ setframerate(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* the start of frame contains:
+ * ff d8
+ * ff fe
+ * width / 16
+ * height / 8
+ * quality
+ */
+ if (sd->bridge == BRIDGE_TP6810) {
+ if (*data != 0x5a) {
+/*fixme: don't discard the whole frame..*/
+ if (*data == 0xaa || *data == 0x00)
+ return;
+ if (*data > 0xc0) {
+ PDEBUG(D_FRAM, "bad frame");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+ data++;
+ len--;
+ if (*data == 0xff && data[1] == 0xd8) {
+/*fixme: there may be information in the 4 high bits*/
+ if ((data[6] & 0x0f) != sd->quality)
+ set_dqt(gspca_dev, data[6] & 0x0f);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 7, len - 7);
+ } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ }
+ return;
+ }
+
+ switch (*data) {
+ case 0x55:
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+
+ if (len < 8
+ || data[1] != 0xff || data[2] != 0xd8
+ || data[3] != 0xff || data[4] != 0xfe) {
+
+ /* Have only seen this with corrupt frames */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ if (data[7] != sd->quality)
+ set_dqt(gspca_dev, data[7]);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 8, len - 8);
+ break;
+ case 0xaa:
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ case 0xcc:
+ if (data[1] != 0xff || data[2] != 0xd8)
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 1, len - 1);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ }
+}
+
+/* -- do autogain -- */
+/* gain setting is done in setexposure() for tp6810 */
+static void setgain(struct gspca_dev *gspca_dev) {}
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt bridge
+#define exp_too_high_cnt sensor
+
+#include "autogain_functions.h"
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, alen;
+ int luma, expo;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt > 5)
+ return;
+ switch (sd->ag_cnt) {
+/* case 5: */
+ default:
+ reg_w(gspca_dev, 0x7d, 0x00);
+ break;
+ case 4:
+ reg_w(gspca_dev, 0x27, 0xb0);
+ break;
+ case 3:
+ reg_w(gspca_dev, 0x0c, 0x01);
+ break;
+ case 2:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ /* values not used (unknown) */
+ break;
+ case 1:
+ reg_w(gspca_dev, 0x27, 0xd0);
+ break;
+ case 0:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
+ (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
+ (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
+ (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
+ (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
+ (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
+ (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
+ (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
+ / 8;
+ if (gspca_dev->width == 640)
+ luma /= 4;
+ reg_w(gspca_dev, 0x7d, 0x00);
+
+ expo = sd->ctrls[EXPOSURE].val;
+ ret = auto_gain_n_exposure(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);
+ }
+ break;
+ }
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80) {
+ if (sd->bridge == BRIDGE_TP6800)
+ fr = rates[6 - (i & 0x07)];
+ else
+ fr = rates_6810[7 - (i & 0x07)];
+ } else {
+ fr = rates[6 - i];
+ }
+ tpf->denominator = fr;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ sd->framerate = tpf->denominator / tpf->numerator;
+ if (gspca_dev->streaming)
+ setframerate(gspca_dev);
+
+ /* Return the actual framerate */
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80)
+ fr = rates_6810[7 - (i & 0x07)];
+ else
+ fr = rates[6 - i];
+ tpf->numerator = 1;
+ tpf->denominator = fr;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ 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;
+}
+
+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 = jpeg_q[sd->quality];
+ 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 const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
+ {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 6caed734a06a..7ee2c8271dcc 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -20,6 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "vc032x"
#include "gspca.h"
@@ -3169,7 +3171,7 @@ static void reg_r_i(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- err("reg_r err %d", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -3210,7 +3212,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev,
value, index, NULL, 0,
500);
if (ret < 0) {
- err("reg_w err %d", ret);
+ pr_err("reg_w err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -3235,8 +3237,7 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev,
reg_r(gspca_dev, 0xa1, 0xb33f, 1);
if (!(gspca_dev->usb_buf[0] & 0x02)) {
- err("I2c Bus Busy Wait %02x",
- gspca_dev->usb_buf[0]);
+ pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
return 0;
}
reg_w(gspca_dev, 0xa0, address, 0xb33a);
@@ -3349,7 +3350,7 @@ static void i2c_write(struct gspca_dev *gspca_dev,
msleep(20);
} while (--retry > 0);
if (retry <= 0)
- err("i2c_write timeout");
+ pr_err("i2c_write timeout\n");
}
static void put_tab_to_reg(struct gspca_dev *gspca_dev,
@@ -3446,7 +3447,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
switch (sensor) {
case -1:
- err("Unknown sensor...");
+ pr_err("Unknown sensor...\n");
return -EINVAL;
case SENSOR_HV7131R:
PDEBUG(D_PROBE, "Find Sensor HV7131R");
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
index 84dfbab923b5..81dd4c99d025 100644
--- a/drivers/media/video/gspca/vicam.c
+++ b/drivers/media/video/gspca/vicam.c
@@ -26,6 +26,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "vicam"
#define HEADER_SIZE 64
@@ -117,7 +119,7 @@ static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, data, len, 1000);
if (ret < 0)
- err("control msg req %02X error %d", request, ret);
+ pr_err("control msg req %02X error %d\n", request, ret);
return ret;
}
@@ -189,8 +191,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
data, size, &act_len, 10000);
/* successful, it returns 0, otherwise negative */
if (ret < 0 || act_len != size) {
- err("bulk read fail (%d) len %d/%d",
- ret, act_len, size);
+ pr_err("bulk read fail (%d) len %d/%d\n",
+ ret, act_len, size);
return -EIO;
}
return 0;
@@ -216,7 +218,7 @@ static void vicam_dostream(struct work_struct *work)
HEADER_SIZE;
buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
if (!buffer) {
- err("Couldn't allocate USB buffer");
+ pr_err("Couldn't allocate USB buffer\n");
goto exit;
}
@@ -269,7 +271,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ret = request_ihex_firmware(&fw, "vicam/firmware.fw",
&gspca_dev->dev->dev);
if (ret) {
- err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
+ pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
index 4a9e622e5e1b..27d2cef0692a 100644
--- a/drivers/media/video/gspca/w996Xcf.c
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -31,6 +31,8 @@
the sensor drivers to v4l2 sub drivers, and properly split of this
driver from ov519.c */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET])
@@ -81,7 +83,7 @@ static void w9968cf_write_fsb(struct sd *sd, u16* data)
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
if (ret < 0) {
- err("Write FSB registers failed (%d)", ret);
+ pr_err("Write FSB registers failed (%d)\n", ret);
sd->gspca_dev.usb_err = ret;
}
}
@@ -108,7 +110,7 @@ static void w9968cf_write_sb(struct sd *sd, u16 value)
udelay(W9968CF_I2C_BUS_DELAY);
if (ret < 0) {
- err("Write SB reg [01] %04x failed", value);
+ pr_err("Write SB reg [01] %04x failed\n", value);
sd->gspca_dev.usb_err = ret;
}
}
@@ -135,7 +137,7 @@ static int w9968cf_read_sb(struct sd *sd)
ret = sd->gspca_dev.usb_buf[0] |
(sd->gspca_dev.usb_buf[1] << 8);
} else {
- err("Read SB reg [01] failed");
+ pr_err("Read SB reg [01] failed\n");
sd->gspca_dev.usb_err = ret;
}
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
index c089a0f6f1d0..3aed42acdb5a 100644
--- a/drivers/media/video/gspca/xirlink_cit.c
+++ b/drivers/media/video/gspca/xirlink_cit.c
@@ -27,6 +27,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "xirlink-cit"
#include <linux/input.h>
@@ -800,8 +802,8 @@ static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
value, index, NULL, 0, 1000);
if (err < 0)
- err("Failed to write a register (index 0x%04X,"
- " value 0x%02X, error %d)", index, value, err);
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
return 0;
}
@@ -816,8 +818,8 @@ static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
0x00, index, buf, 8, 1000);
if (res < 0) {
- err("Failed to read a register (index 0x%04X, error %d)",
- index, res);
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
return res;
}
@@ -1587,7 +1589,7 @@ static int cit_get_packet_size(struct gspca_dev *gspca_dev)
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
if (!alt) {
- err("Couldn't get altsetting");
+ pr_err("Couldn't get altsetting\n");
return -EIO;
}
@@ -2824,7 +2826,7 @@ static int sd_isoc_nego(struct gspca_dev *gspca_dev)
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
if (ret < 0)
- err("set alt 1 err %d", ret);
+ pr_err("set alt 1 err %d\n", ret);
return ret;
}
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 61cdd56a74a9..30ea1e479492 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MODULE_NAME "zc3xx"
#include <linux/input.h>
@@ -5666,7 +5668,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- err("reg_r_i err %d", ret);
+ pr_err("reg_r_i err %d\n", ret);
gspca_dev->usb_err = ret;
return 0;
}
@@ -5698,7 +5700,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev,
value, index, NULL, 0,
500);
if (ret < 0) {
- err("reg_w_i err %d", ret);
+ pr_err("reg_w_i err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -5724,7 +5726,7 @@ static u16 i2c_read(struct gspca_dev *gspca_dev,
msleep(20);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
- err("i2c_r status error %02x", retbyte);
+ pr_err("i2c_r status error %02x\n", retbyte);
retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
@@ -5748,7 +5750,7 @@ static u8 i2c_write(struct gspca_dev *gspca_dev,
msleep(1);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
- err("i2c_w status error %02x", retbyte);
+ pr_err("i2c_w status error %02x\n", retbyte);
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
return retbyte;
@@ -6497,7 +6499,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor GC0303");
break;
default:
- warn("Unknown sensor - set to TAS5130C");
+ pr_warn("Unknown sensor - set to TAS5130C\n");
sd->sensor = SENSOR_TAS5130C;
}
break;
@@ -6603,7 +6605,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
sd->sensor = SENSOR_OV7620; /* same sensor (?) */
break;
default:
- err("Unknown sensor %04x", sensor);
+ pr_err("Unknown sensor %04x\n", sensor);
return -EINVAL;
}
}
@@ -6970,6 +6972,7 @@ static const struct sd_desc sd_desc = {
};
static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03f0, 0x1b07)},
{USB_DEVICE(0x041e, 0x041e)},
{USB_DEVICE(0x041e, 0x4017)},
{USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
index 3baa9f613ca3..52f057f24e39 100644
--- a/drivers/media/video/hdpvr/Makefile
+++ b/drivers/media/video/hdpvr/Makefile
@@ -2,6 +2,6 @@ hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
-EXTRA_CFLAGS += -Idrivers/media/video
+ccflags-y += -Idrivers/media/video
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index cbc505a2fc29..9cb039e593db 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -21,6 +21,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
@@ -175,13 +177,14 @@ static int hexium_init_done(struct saa7146_dev *dev)
union i2c_smbus_data data;
int i = 0;
- DEB_D(("hexium_init_done called.\n"));
+ DEB_D("hexium_init_done called\n");
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_ks0127b); i++) {
data.byte = hexium_ks0127b[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
+ pr_err("hexium_init_done() failed for address 0x%02x\n",
+ i);
}
}
@@ -192,7 +195,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
{
union i2c_smbus_data data;
- DEB_D((".\n"));
+ DEB_D("\n");
data.byte = hexium_input_select[input].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
@@ -207,12 +210,13 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
union i2c_smbus_data data;
int i = 0;
- DEB_D((".\n"));
+ DEB_D("\n");
while (vdec[i].adr != -1) {
data.byte = vdec[i].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
+ pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
+ i);
return -1;
}
i++;
@@ -222,14 +226,14 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= HEXIUM_INPUTS)
return -EINVAL;
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
return 0;
}
@@ -240,7 +244,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
*input = hexium->cur_input;
- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
return 0;
}
@@ -249,7 +253,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
if (input >= HEXIUM_INPUTS)
return -EINVAL;
@@ -270,7 +274,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
if (hexium_controls[i].id == qc->id) {
*qc = hexium_controls[i];
- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
return 0;
}
}
@@ -293,7 +297,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_PRIVATE_BASE) {
vc->value = hexium->cur_bw;
- DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
+ DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value);
return 0;
}
return -EINVAL;
@@ -316,7 +320,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_PRIVATE_BASE)
hexium->cur_bw = vc->value;
- DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
+ DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw);
if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
hexium_set_standard(hexium, hexium_pal);
@@ -351,14 +355,14 @@ static struct saa7146_ext_vv vv_data;
/* this function only gets called when the probing was successful */
static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
- struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ struct hexium *hexium;
int ret;
- DEB_EE((".\n"));
+ DEB_EE("\n");
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
- printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
+ pr_err("not enough kernel memory in hexium_attach()\n");
return -ENOMEM;
}
dev->ext_priv = hexium;
@@ -371,7 +375,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
};
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(hexium);
return -EFAULT;
}
@@ -402,11 +406,11 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
vv_data.ops.vidioc_s_input = vidioc_s_input;
ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
if (ret < 0) {
- printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
+ pr_err("cannot register capture v4l2 device. skipping.\n");
return ret;
}
- printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
+ pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
hexium_num++;
return 0;
@@ -416,7 +420,7 @@ static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
@@ -508,7 +512,7 @@ static struct saa7146_extension hexium_extension = {
static int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&hexium_extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 6ad7e1c8b922..74861a4b601a 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -21,6 +21,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
@@ -209,7 +211,7 @@ static int hexium_probe(struct saa7146_dev *dev)
union i2c_smbus_data data;
int err = 0;
- DEB_EE((".\n"));
+ DEB_EE("\n");
/* there are no hexium orion cards with revision 0 saa7146s */
if (0 == dev->revision) {
@@ -218,7 +220,7 @@ static int hexium_probe(struct saa7146_dev *dev)
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
- printk("hexium_orion: hexium_probe: not enough kernel memory.\n");
+ pr_err("hexium_probe: not enough kernel memory\n");
return -ENOMEM;
}
@@ -234,7 +236,7 @@ static int hexium_probe(struct saa7146_dev *dev)
};
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(hexium);
return -EFAULT;
}
@@ -248,7 +250,7 @@ static int hexium_probe(struct saa7146_dev *dev)
/* detect newer Hexium Orion cards by subsystem ids */
if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
- printk("hexium_orion: device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs.\n");
+ pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_1SVHS_3BNC;
@@ -256,7 +258,7 @@ static int hexium_probe(struct saa7146_dev *dev)
}
if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
- printk("hexium_orion: device is a Hexium Orion w/ 4 BNC inputs.\n");
+ pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_4BNC;
@@ -266,7 +268,7 @@ static int hexium_probe(struct saa7146_dev *dev)
/* check if this is an old hexium Orion card by looking at
a saa7110 at address 0x4e */
if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
- printk("hexium_orion: device is a Hexium HV-PCI6/Orion (old).\n");
+ pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
/* we store the pointer in our private data field */
dev->ext_priv = hexium;
hexium->type = HEXIUM_HV_PCI6_ORION;
@@ -288,13 +290,13 @@ static int hexium_init_done(struct saa7146_dev *dev)
union i2c_smbus_data data;
int i = 0;
- DEB_D(("hexium_init_done called.\n"));
+ DEB_D("hexium_init_done called\n");
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_saa7110); i++) {
data.byte = hexium_saa7110[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
- printk("hexium_orion: failed for address 0x%02x\n", i);
+ pr_err("failed for address 0x%02x\n", i);
}
}
@@ -306,7 +308,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
union i2c_smbus_data data;
int i = 0;
- DEB_D((".\n"));
+ DEB_D("\n");
for (i = 0; i < 8; i++) {
int adr = hexium_input_select[input].data[i].adr;
@@ -314,7 +316,7 @@ static int hexium_set_input(struct hexium *hexium, int input)
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
return -1;
}
- printk("%d: 0x%02x => 0x%02x\n",input, adr,data.byte);
+ pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
}
return 0;
@@ -322,14 +324,14 @@ static int hexium_set_input(struct hexium *hexium, int input)
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= HEXIUM_INPUTS)
return -EINVAL;
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
return 0;
}
@@ -340,7 +342,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
*input = hexium->cur_input;
- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
return 0;
}
@@ -365,18 +367,18 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE((".\n"));
+ DEB_EE("\n");
saa7146_vv_init(dev, &vv_data);
vv_data.ops.vidioc_enum_input = vidioc_enum_input;
vv_data.ops.vidioc_g_input = vidioc_g_input;
vv_data.ops.vidioc_s_input = vidioc_s_input;
if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
- printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
+ pr_err("cannot register capture v4l2 device. skipping.\n");
return -1;
}
- printk("hexium_orion: found 'hexium orion' frame grabber-%d.\n", hexium_num);
+ pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
hexium_num++;
/* the rest */
@@ -390,7 +392,7 @@ static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
@@ -479,7 +481,7 @@ static struct saa7146_extension extension = {
static int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
index 0382ea752e6f..8775e262bb6e 100644
--- a/drivers/media/video/imx074.c
+++ b/drivers/media/video/imx074.c
@@ -12,11 +12,11 @@
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
@@ -267,6 +267,17 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd,
return 0;
}
+static int imx074_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
.s_stream = imx074_s_stream,
.s_mbus_fmt = imx074_s_fmt,
@@ -275,6 +286,7 @@ static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
.enum_mbus_fmt = imx074_enum_fmt,
.g_crop = imx074_g_crop,
.cropcap = imx074_cropcap,
+ .g_mbus_config = imx074_g_mbus_config,
};
static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
@@ -286,28 +298,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = {
.video = &imx074_subdev_video_ops,
};
-/*
- * We have to provide soc-camera operations, but we don't have anything to say
- * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
- */
-static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
-{
- return 0;
-}
-
-static int imx074_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return -EINVAL;
-}
-
-static struct soc_camera_ops imx074_ops = {
- .query_bus_param = imx074_query_bus_param,
- .set_bus_param = imx074_set_bus_param,
-};
-
-static int imx074_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int imx074_video_probe(struct i2c_client *client)
{
int ret;
u16 id;
@@ -417,17 +408,10 @@ static int imx074_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct imx074 *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "IMX074: missing platform data!\n");
return -EINVAL;
@@ -445,12 +429,10 @@ static int imx074_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
- icd->ops = &imx074_ops;
priv->fmt = &imx074_colour_fmts[0];
- ret = imx074_video_probe(icd, client);
+ ret = imx074_video_probe(client);
if (ret < 0) {
- icd->ops = NULL;
kfree(priv);
return ret;
}
@@ -461,10 +443,8 @@ static int imx074_probe(struct i2c_client *client,
static int imx074_remove(struct i2c_client *client)
{
struct imx074 *priv = to_imx074(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
if (icl->free_bus)
icl->free_bus(icl);
kfree(priv);
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index 26ce0d6eaee1..71ab76a5ab26 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 0fb75524484d..41108a9a195e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -1180,6 +1180,8 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
setup.addr = ADDR_UNSET;
setup.type = itv->options.tuner;
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (itv->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
ivtv_reset_tuner_gpio : NULL;
ivtv_call_all(itv, tuner, s_type_addr, &setup);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 3e5c090af112..ecafa697326e 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -1203,9 +1203,7 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced
cap->service_lines[f][l] = set;
}
}
- return 0;
- }
- if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
return -EINVAL;
if (itv->is_60hz) {
@@ -1215,9 +1213,16 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced
cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
cap->service_lines[0][16] = V4L2_SLICED_VPS;
}
- return 0;
+ } else {
+ return -EINVAL;
}
- return -EINVAL;
+
+ set = 0;
+ for (f = 0; f < 2; f++)
+ for (l = 0; l < 24; l++)
+ set |= cap->service_lines[f][l];
+ cap->service_set = set;
+ return 0;
}
static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index fb8e4a7a9dd2..5d21d056d6a3 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -936,7 +936,7 @@ static int __devinit m5mols_probe(struct i2c_client *client,
return -EINVAL;
}
- if (!pdata->irq) {
+ if (!client->irq) {
dev_err(&client->dev, "Interrupt not assigned\n");
return -EINVAL;
}
@@ -973,7 +973,7 @@ static int __devinit m5mols_probe(struct i2c_client *client,
init_waitqueue_head(&info->irq_waitq);
INIT_WORK(&info->work_irq, m5mols_irq_work);
- ret = request_irq(pdata->irq, m5mols_irq_handler,
+ ret = request_irq(client->irq, m5mols_irq_handler,
IRQF_TRIGGER_RISING, MODULE_NAME, sd);
if (ret) {
dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
@@ -998,7 +998,7 @@ static int __devexit m5mols_remove(struct i2c_client *client)
struct m5mols_info *info = to_m5mols(sd);
v4l2_device_unregister_subdev(sd);
- free_irq(info->pdata->irq, sd);
+ free_irq(client->irq, sd);
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
gpio_free(info->pdata->gpio_reset);
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
index 83c14514cd52..80ec64d2d6d8 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -450,7 +450,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
buf = cam->vb_bufs[frame ^ 0x1];
cam->vb_bufs[frame] = buf;
mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
set_bit(CF_SINGLE_BUFFER, &cam->flags);
singles++;
return;
@@ -461,7 +461,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
list_del_init(&buf->queue);
mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
cam->vb_bufs[frame] = buf;
clear_bit(CF_SINGLE_BUFFER, &cam->flags);
}
@@ -883,8 +883,9 @@ static int mcam_read_setup(struct mcam_camera *cam)
* Videobuf2 interface code.
*/
-static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
- unsigned int *num_planes, unsigned long sizes[],
+static int mcam_vb_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *nbufs,
+ unsigned int *num_planes, unsigned int sizes[],
void *alloc_ctxs[])
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
@@ -940,12 +941,14 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq)
/*
* These need to be called with the mutex held from vb2
*/
-static int mcam_vb_start_streaming(struct vb2_queue *vq)
+static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
- if (cam->state != S_IDLE)
+ if (cam->state != S_IDLE) {
+ INIT_LIST_HEAD(&cam->buffers);
return -EINVAL;
+ }
cam->sequence = 0;
/*
* Videobuf2 sneakily hoards all the buffers and won't
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
index d6b764541375..fb0b124b35f3 100644
--- a/drivers/media/video/marvell-ccic/mmp-driver.c
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -29,6 +29,7 @@
#include "mcam-core.h"
+MODULE_ALIAS("platform:mmp-camera");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index 166bf9349c10..12897e8a3314 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -738,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
* Queue operations
*/
-static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int m2mtest_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
struct m2mtest_q_data *q_data;
@@ -793,10 +794,24 @@ static void m2mtest_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
}
+static void m2mtest_wait_prepare(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_unlock(ctx);
+}
+
+static void m2mtest_wait_finish(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_lock(ctx);
+}
+
static struct vb2_ops m2mtest_qops = {
.queue_setup = m2mtest_queue_setup,
.buf_prepare = m2mtest_buf_prepare,
.buf_queue = m2mtest_buf_queue,
+ .wait_prepare = m2mtest_wait_prepare,
+ .wait_finish = m2mtest_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index c43c81f5f978..d0f538857285 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -426,6 +426,20 @@ static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
return 0;
}
+static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ *id &= state->detected_std;
+
+ v4l_dbg(2, msp_debug, client,
+ "detected standard: %s(0x%08Lx)\n",
+ msp_standard_std_name(state->std), state->detected_std);
+
+ return 0;
+}
+
static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
{
struct msp_state *state = to_state(sd);
@@ -616,6 +630,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {
.s_std = msp_s_std,
};
+static const struct v4l2_subdev_video_ops msp_video_ops = {
+ .querystd = msp_querystd,
+};
+
static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
.s_frequency = msp_s_frequency,
.g_tuner = msp_g_tuner,
@@ -630,6 +648,7 @@ static const struct v4l2_subdev_audio_ops msp_audio_ops = {
static const struct v4l2_subdev_ops msp_ops = {
.core = &msp_core_ops,
+ .video = &msp_video_ops,
.tuner = &msp_tuner_ops,
.audio = &msp_audio_ops,
};
@@ -664,6 +683,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
v4l2_i2c_subdev_init(sd, client, &msp_ops);
state->v4l2_std = V4L2_STD_NTSC;
+ state->detected_std = V4L2_STD_ALL;
state->audmode = V4L2_TUNER_MODE_STEREO;
state->input = -1;
state->i2s_mode = 0;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index 32a478e532f3..831e8db4368c 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -75,7 +75,7 @@ struct msp_state {
int opmode;
int std;
int mode;
- v4l2_std_id v4l2_std;
+ v4l2_std_id v4l2_std, detected_std;
int nicam_on;
int acb;
int in_scart;
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 80387e2c3eca..f8b51714f2f9 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -37,29 +37,49 @@ static struct {
int retval;
int main, second;
char *name;
+ v4l2_std_id std;
} msp_stdlist[] = {
- { 0x0000, 0, 0, "could not detect sound standard" },
- { 0x0001, 0, 0, "autodetect start" },
- { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" },
- { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" },
- { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" },
- { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" },
- { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" },
- { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), "6.5/5.74 D/K3 Dual FM-Stereo" },
- { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" },
- { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" },
- { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" },
- { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" },
- { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" },
- { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV3)" },
- { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" },
- { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" },
- { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" },
- { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" },
- { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" },
- { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" },
- { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" },
- { -1, 0, 0, NULL }, /* EOF */
+ { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL },
+ { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL },
+ { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72),
+ "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN },
+ { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875),
+ "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG },
+ { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125),
+ "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875),
+ "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK },
+ { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875),
+ "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85),
+ "5.5/5.85 B/G NICAM FM", V4L2_STD_BG },
+ { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 L NICAM AM", V4L2_STD_L },
+ { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55),
+ "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I },
+ { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM", V4L2_STD_DK },
+ { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK },
+ { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK },
+ { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Stereo", V4L2_STD_MTS },
+ { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS },
+ { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP },
+ { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7),
+ "10.7 FM-Stereo Radio", V4L2_STD_ALL },
+ { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 SAT-Mono", V4L2_STD_ALL },
+ { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20),
+ "7.02/7.20 SAT-Stereo", V4L2_STD_ALL },
+ { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2),
+ "7.2 SAT ADR", V4L2_STD_ALL },
+ { -1, 0, 0, NULL, 0 }, /* EOF */
};
static struct msp3400c_init_data_dem {
@@ -156,6 +176,16 @@ const char *msp_standard_std_name(int std)
return "unknown";
}
+static v4l2_std_id msp_standard_std(int std)
+{
+ int i;
+
+ for (i = 0; msp_stdlist[i].name != NULL; i++)
+ if (msp_stdlist[i].retval == std)
+ return msp_stdlist[i].std;
+ return V4L2_STD_ALL;
+}
+
static void msp_set_source(struct i2c_client *client, u16 src)
{
struct msp_state *state = to_state(i2c_get_clientdata(client));
@@ -479,6 +509,7 @@ int msp3400c_thread(void *data)
int count, max1, max2, val1, val2, val, i;
v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n");
@@ -579,6 +610,7 @@ restart:
state->main = msp3400c_carrier_detect_main[max1].cdo;
switch (max1) {
case 1: /* 5.5 */
+ state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H;
if (max2 == 0) {
/* B/G FM-stereo */
state->second = msp3400c_carrier_detect_55[max2].cdo;
@@ -596,6 +628,7 @@ restart:
break;
case 2: /* 6.0 */
/* PAL I NICAM */
+ state->detected_std = V4L2_STD_PAL_I;
state->second = MSP_CARRIER(6.552);
msp3400c_set_mode(client, MSP_MODE_FM_NICAM2);
state->nicam_on = 1;
@@ -607,22 +640,26 @@ restart:
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
} else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) {
/* L NICAM or AM-mono */
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_AM_NICAM);
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_L;
} else if (max2 == 0 && state->has_nicam) {
/* D/K NICAM */
state->second = msp3400c_carrier_detect_65[max2].cdo;
msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
state->nicam_on = 1;
state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
} else {
goto no_second;
}
break;
case 0: /* 4.5 */
+ state->detected_std = V4L2_STD_MN;
default:
no_second:
state->second = msp3400c_carrier_detect_main[max1].cdo;
@@ -662,6 +699,7 @@ int msp3410d_thread(void *data)
int val, i, std, count;
v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n");
@@ -743,6 +781,8 @@ restart:
msp_stdlist[8].name : "unknown", val);
state->std = val = 0x0009;
msp_write_dem(client, 0x20, val);
+ } else {
+ state->detected_std = msp_standard_std(state->std);
}
/* set stereo */
@@ -957,6 +997,7 @@ int msp34xxg_thread(void *data)
int val, i;
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n");
@@ -1013,6 +1054,7 @@ unmute:
v4l_dbg(1, msp_debug, client,
"detected standard: %s (0x%04x)\n",
msp_standard_std_name(state->std), state->std);
+ state->detected_std = msp_standard_std(state->std);
if (state->std == 9) {
/* AM NICAM mode */
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 4da9cca939c1..63ae5c61c9bf 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -13,9 +13,11 @@
#include <linux/i2c.h>
#include <linux/log2.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
/*
* mt9m001 i2c address 0x5d
@@ -84,15 +86,19 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
struct mt9m001 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
struct v4l2_rect rect; /* Sensor window */
const struct mt9m001_datafmt *fmt;
const struct mt9m001_datafmt *fmts;
int num_fmts;
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
- unsigned int gain;
- unsigned int exposure;
+ unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
- unsigned char autoexposure;
};
static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
@@ -165,54 +171,13 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9m001_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param)
- return icl->set_bus_param(icl, width_flag);
-
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- if (width_flag == SOCAM_DATAWIDTH_10)
- return 0;
-
- return -EINVAL;
-}
-
-static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- /* MT9M001 has all capture_format parameters fixed */
- unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct v4l2_rect rect = a->c;
- struct soc_camera_device *icd = client->dev.platform_data;
int ret;
const u16 hblank = 9, vblank = 25;
- unsigned int total_h;
if (mt9m001->fmts == mt9m001_colour_fmts)
/*
@@ -231,7 +196,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
soc_camera_limit_side(&rect.top, &rect.height,
MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
- total_h = rect.height + mt9m001->y_skip_top + vblank;
+ mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
/* Blanking and start values - default... */
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
@@ -240,7 +205,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
/*
* The caller provides a supported format, as verified per
- * call to icd->try_fmt()
+ * call to .try_mbus_fmt()
*/
if (!ret)
ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
@@ -251,17 +216,8 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
rect.height + mt9m001->y_skip_top - 1);
- if (!ret && mt9m001->autoexposure) {
- ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
- if (!ret) {
- const struct v4l2_queryctrl *qctrl =
- soc_camera_find_qctrl(icd->ops,
- V4L2_CID_EXPOSURE);
- mt9m001->exposure = (524 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- 1048 + qctrl->minimum;
- }
- }
+ if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
if (!ret)
mt9m001->rect = rect;
@@ -421,107 +377,48 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9m001_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9m001_ops = {
- .set_bus_param = mt9m001_set_bus_param,
- .query_bus_param = mt9m001_query_bus_param,
- .controls = mt9m001_controls,
- .num_controls = ARRAY_SIZE(mt9m001_controls),
-};
-
-static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- int data;
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ s32 min, max;
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9M001_READ_OPTIONS2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x8000);
- break;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9m001->autoexposure;
- break;
- case V4L2_CID_GAIN:
- ctrl->value = mt9m001->gain;
- break;
- case V4L2_CID_EXPOSURE:
- ctrl->value = mt9m001->exposure;
+ min = mt9m001->exposure->minimum;
+ max = mt9m001->exposure->maximum;
+ mt9m001->exposure->val =
+ (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
break;
}
return 0;
}
-static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ struct v4l2_subdev *sd = &mt9m001->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *exp = mt9m001->exposure;
int data;
- qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id);
-
- if (!qctrl)
- return -EINVAL;
-
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
else
data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
if (data < 0)
return -EIO;
- break;
+ return 0;
+
case V4L2_CID_GAIN:
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
/* See Datasheet Table 7, Gain settings. */
- if (ctrl->value <= qctrl->default_value) {
+ if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = qctrl->default_value - qctrl->minimum;
- data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
@@ -530,8 +427,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
} else {
/* Pack it into 1.125..15 variable step, register values 9..67 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = qctrl->maximum - qctrl->default_value - 1;
- unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
111 + range / 2) / range + 9;
if (gain <= 32)
@@ -547,66 +444,44 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
if (data < 0)
return -EIO;
}
+ return 0;
- /* Success */
- mt9m001->gain = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- /* mt9m001 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
- unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
range / 2) / range + 1;
dev_dbg(&client->dev,
"Setting shutter width from %d to %lu\n",
- reg_read(client, MT9M001_SHUTTER_WIDTH),
- shutter);
+ reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
return -EIO;
- mt9m001->exposure = ctrl->value;
- mt9m001->autoexposure = 0;
- }
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value) {
+ } else {
const u16 vblank = 25;
- unsigned int total_h = mt9m001->rect.height +
+
+ mt9m001->total_h = mt9m001->rect.height +
mt9m001->y_skip_top + vblank;
- if (reg_write(client, MT9M001_SHUTTER_WIDTH,
- total_h) < 0)
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
return -EIO;
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- mt9m001->exposure = (524 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- 1048 + qctrl->minimum;
- mt9m001->autoexposure = 1;
- } else
- mt9m001->autoexposure = 0;
- break;
+ }
+ return 0;
}
- return 0;
+ return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m001_video_probe(struct soc_camera_device *icd,
+static int mt9m001_video_probe(struct soc_camera_link *icl,
struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
s32 data;
unsigned long flags;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/* Enable the chip */
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&client->dev, "write: %d\n", data);
@@ -661,18 +536,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
dev_err(&client->dev, "Failed to initialise the camera\n");
/* mt9m001_init() has reset the chip, returning registers to defaults */
- mt9m001->gain = 64;
- mt9m001->exposure = 255;
-
- return ret;
+ return v4l2_ctrl_handler_setup(&mt9m001->hdl);
}
-static void mt9m001_video_remove(struct soc_camera_device *icd)
+static void mt9m001_video_remove(struct soc_camera_link *icl)
{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- dev_dbg(icd->pdev, "Video removed: %p, %p\n",
- icd->parent, icd->vdev);
if (icl->free_bus)
icl->free_bus(icl);
}
@@ -687,9 +555,12 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
+ .g_volatile_ctrl = mt9m001_g_volatile_ctrl,
+ .s_ctrl = mt9m001_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
- .g_ctrl = mt9m001_g_ctrl,
- .s_ctrl = mt9m001_s_ctrl,
.g_chip_ident = mt9m001_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m001_g_register,
@@ -710,6 +581,40 @@ static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* MT9M001 has all capture_format parameters fixed */
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ const struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
+
+ if (icl->set_bus_param)
+ return icl->set_bus_param(icl, 1 << (bps - 1));
+
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return bps == 10 ? 0 : -EINVAL;
+}
+
static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.s_stream = mt9m001_s_stream,
.s_mbus_fmt = mt9m001_s_fmt,
@@ -719,6 +624,8 @@ static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.g_crop = mt9m001_g_crop,
.cropcap = mt9m001_cropcap,
.enum_mbus_fmt = mt9m001_enum_fmt,
+ .g_mbus_config = mt9m001_g_mbus_config,
+ .s_mbus_config = mt9m001_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
@@ -735,17 +642,10 @@ static int mt9m001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9m001 *mt9m001;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9M001 driver needs platform data\n");
return -EINVAL;
@@ -762,25 +662,40 @@ static int mt9m001_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+ mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
+ &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
+ if (mt9m001->hdl.error) {
+ int err = mt9m001->hdl.error;
- /* Second stage probe - when a capture adapter is there */
- icd->ops = &mt9m001_ops;
+ kfree(mt9m001);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ /* Second stage probe - when a capture adapter is there */
mt9m001->y_skip_top = 0;
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
mt9m001->rect.top = MT9M001_ROW_SKIP;
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9m001->autoexposure = 1;
-
- ret = mt9m001_video_probe(icd, client);
+ ret = mt9m001_video_probe(icl, client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
kfree(mt9m001);
}
@@ -790,10 +705,11 @@ static int mt9m001_probe(struct i2c_client *client,
static int mt9m001_remove(struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
- mt9m001_video_remove(icd);
+ v4l2_device_unregister_subdev(&mt9m001->subdev);
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ mt9m001_video_remove(icl);
kfree(mt9m001);
return 0;
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index a357aa889fc6..f023cc092c2b 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -13,10 +13,12 @@
#include <linux/log2.h>
#include <linux/gpio.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/soc_camera.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
/*
* MT9M111, MT9M112 and MT9M131:
@@ -177,6 +179,8 @@ enum mt9m111_context {
struct mt9m111 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *gain;
int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
* from v4l2-chip-ident.h */
enum mt9m111_context context;
@@ -184,13 +188,9 @@ struct mt9m111 {
struct mutex power_lock; /* lock to protect power_count */
int power_count;
const struct mt9m111_datafmt *fmt;
- unsigned int gain;
- unsigned char autoexposure;
+ int lastpage; /* PageMap cache value */
unsigned char datawidth;
unsigned int powered:1;
- unsigned int hflip:1;
- unsigned int vflip:1;
- unsigned int autowhitebalance:1;
};
static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
@@ -202,17 +202,17 @@ static int reg_page_map_set(struct i2c_client *client, const u16 reg)
{
int ret;
u16 page;
- static int lastpage = -1; /* PageMap cache value */
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
page = (reg >> 8);
- if (page == lastpage)
+ if (page == mt9m111->lastpage)
return 0;
if (page > 2)
return -EINVAL;
ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page));
if (!ret)
- lastpage = page;
+ mt9m111->lastpage = page;
return ret;
}
@@ -362,21 +362,6 @@ static int mt9m111_reset(struct mt9m111 *mt9m111)
return ret;
}
-static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
-{
- return 0;
-}
-
static int mt9m111_make_rect(struct mt9m111 *mt9m111,
struct v4l2_rect *rect)
{
@@ -659,50 +644,6 @@ static int mt9m111_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9m111_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Verticaly",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontaly",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, { /* gain = 1/32*val (=>gain=1 if val==32) */
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 63 * 2 * 2,
- .step = 1,
- .default_value = 32,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9m111_ops = {
- .query_bus_param = mt9m111_query_bus_param,
- .set_bus_param = mt9m111_set_bus_param,
- .controls = mt9m111_controls,
- .num_controls = ARRAY_SIZE(mt9m111_controls),
-};
-
static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
@@ -743,7 +684,6 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
if (gain > 63 * 2 * 2)
return -EINVAL;
- mt9m111->gain = gain;
if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
val = (1 << 10) | (1 << 9) | (gain / 4);
else if ((gain >= 64) && (gain < 64 * 2))
@@ -757,118 +697,47 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
if (on)
- ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
- else
- ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-
- if (!ret)
- mt9m111->autoexposure = on;
-
- return ret;
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
}
static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
if (on)
- ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
- else
- ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-
- if (!ret)
- mt9m111->autowhitebalance = on;
-
- return ret;
-}
-
-static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (mt9m111->context == HIGHPOWER)
- data = reg_read(READ_MODE_B);
- else
- data = reg_read(READ_MODE_A);
-
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
- break;
- case V4L2_CID_HFLIP:
- if (mt9m111->context == HIGHPOWER)
- data = reg_read(READ_MODE_B);
- else
- data = reg_read(READ_MODE_A);
-
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
- break;
- case V4L2_CID_GAIN:
- data = mt9m111_get_global_gain(mt9m111);
- if (data < 0)
- return data;
- ctrl->value = data;
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9m111->autoexposure;
- break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = mt9m111->autowhitebalance;
- break;
- }
- return 0;
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
}
-static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- const struct v4l2_queryctrl *qctrl;
- int ret;
-
- qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
+ struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+ struct mt9m111, hdl);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- mt9m111->vflip = ctrl->value;
- ret = mt9m111_set_flip(mt9m111, ctrl->value,
+ return mt9m111_set_flip(mt9m111, ctrl->val,
MT9M111_RMB_MIRROR_ROWS);
- break;
case V4L2_CID_HFLIP:
- mt9m111->hflip = ctrl->value;
- ret = mt9m111_set_flip(mt9m111, ctrl->value,
+ return mt9m111_set_flip(mt9m111, ctrl->val,
MT9M111_RMB_MIRROR_COLS);
- break;
case V4L2_CID_GAIN:
- ret = mt9m111_set_global_gain(mt9m111, ctrl->value);
- break;
+ return mt9m111_set_global_gain(mt9m111, ctrl->val);
case V4L2_CID_EXPOSURE_AUTO:
- ret = mt9m111_set_autoexposure(mt9m111, ctrl->value);
- break;
+ return mt9m111_set_autoexposure(mt9m111, ctrl->val);
case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = mt9m111_set_autowhitebalance(mt9m111, ctrl->value);
- break;
- default:
- ret = -EINVAL;
+ return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
}
- return ret;
+ return -EINVAL;
}
static int mt9m111_suspend(struct mt9m111 *mt9m111)
{
- mt9m111->gain = mt9m111_get_global_gain(mt9m111);
+ v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
return 0;
}
@@ -878,11 +747,7 @@ static void mt9m111_restore_state(struct mt9m111 *mt9m111)
mt9m111_set_context(mt9m111, mt9m111->context);
mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
mt9m111_setup_rect(mt9m111, &mt9m111->rect);
- mt9m111_set_flip(mt9m111, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
- mt9m111_set_flip(mt9m111, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
- mt9m111_set_global_gain(mt9m111, mt9m111->gain);
- mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
- mt9m111_set_autowhitebalance(mt9m111, mt9m111->autowhitebalance);
+ v4l2_ctrl_handler_setup(&mt9m111->hdl);
}
static int mt9m111_resume(struct mt9m111 *mt9m111)
@@ -910,8 +775,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
ret = mt9m111_reset(mt9m111);
if (!ret)
ret = mt9m111_set_context(mt9m111, mt9m111->context);
- if (!ret)
- ret = mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
if (ret)
dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
return ret;
@@ -921,20 +784,12 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m111_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9m111_video_probe(struct i2c_client *client)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
s32 data;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
- mt9m111->autoexposure = 1;
- mt9m111->autowhitebalance = 1;
-
data = reg_read(CHIP_VERSION);
switch (data) {
@@ -948,17 +803,16 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
- ret = -ENODEV;
dev_err(&client->dev,
"No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
data);
- goto ei2c;
+ return -ENODEV;
}
ret = mt9m111_init(mt9m111);
-
-ei2c:
- return ret;
+ if (ret)
+ return ret;
+ return v4l2_ctrl_handler_setup(&mt9m111->hdl);
}
static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
@@ -995,9 +849,11 @@ out:
return ret;
}
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+ .s_ctrl = mt9m111_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
- .g_ctrl = mt9m111_g_ctrl,
- .s_ctrl = mt9m111_s_ctrl,
.g_chip_ident = mt9m111_g_chip_ident,
.s_power = mt9m111_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1016,6 +872,21 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
.s_mbus_fmt = mt9m111_s_fmt,
.g_mbus_fmt = mt9m111_g_fmt,
@@ -1024,6 +895,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
.g_crop = mt9m111_g_crop,
.cropcap = mt9m111_cropcap,
.enum_mbus_fmt = mt9m111_enum_fmt,
+ .g_mbus_config = mt9m111_g_mbus_config,
};
static struct v4l2_subdev_ops mt9m111_subdev_ops = {
@@ -1035,17 +907,10 @@ static int mt9m111_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9m111 *mt9m111;
- struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "mt9m111: driver needs platform data\n");
return -EINVAL;
@@ -1062,19 +927,37 @@ static int mt9m111_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+ v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+ &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+ if (mt9m111->hdl.error) {
+ int err = mt9m111->hdl.error;
- /* Second stage probe - when a capture adapter is there */
- icd->ops = &mt9m111_ops;
+ kfree(mt9m111);
+ return err;
+ }
+ /* Second stage probe - when a capture adapter is there */
mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
mt9m111->rect.width = MT9M111_MAX_WIDTH;
mt9m111->rect.height = MT9M111_MAX_HEIGHT;
mt9m111->fmt = &mt9m111_colour_fmts[0];
+ mt9m111->lastpage = -1;
- ret = mt9m111_video_probe(icd, client);
+ ret = mt9m111_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
kfree(mt9m111);
}
@@ -1084,9 +967,9 @@ static int mt9m111_probe(struct i2c_client *client,
static int mt9m111_remove(struct i2c_client *client)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&mt9m111->subdev);
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
kfree(mt9m111);
return 0;
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
new file mode 100644
index 000000000000..73c068993f05
--- /dev/null
+++ b/drivers/media/video/mt9p031.c
@@ -0,0 +1,964 @@
+/*
+ * Driver for MT9P031 CMOS Image Sensor from Aptina
+ *
+ * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
+ * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on the MT9V032 driver and Bastian Hecht's code.
+ *
+ * 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/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <media/v4l2-subdev.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9p031.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9P031_PIXEL_ARRAY_WIDTH 2752
+#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
+
+#define MT9P031_CHIP_VERSION 0x00
+#define MT9P031_CHIP_VERSION_VALUE 0x1801
+#define MT9P031_ROW_START 0x01
+#define MT9P031_ROW_START_MIN 0
+#define MT9P031_ROW_START_MAX 2004
+#define MT9P031_ROW_START_DEF 54
+#define MT9P031_COLUMN_START 0x02
+#define MT9P031_COLUMN_START_MIN 0
+#define MT9P031_COLUMN_START_MAX 2750
+#define MT9P031_COLUMN_START_DEF 16
+#define MT9P031_WINDOW_HEIGHT 0x03
+#define MT9P031_WINDOW_HEIGHT_MIN 2
+#define MT9P031_WINDOW_HEIGHT_MAX 2006
+#define MT9P031_WINDOW_HEIGHT_DEF 1944
+#define MT9P031_WINDOW_WIDTH 0x04
+#define MT9P031_WINDOW_WIDTH_MIN 2
+#define MT9P031_WINDOW_WIDTH_MAX 2752
+#define MT9P031_WINDOW_WIDTH_DEF 2592
+#define MT9P031_HORIZONTAL_BLANK 0x05
+#define MT9P031_HORIZONTAL_BLANK_MIN 0
+#define MT9P031_HORIZONTAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK 0x06
+#define MT9P031_VERTICAL_BLANK_MIN 0
+#define MT9P031_VERTICAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK_DEF 25
+#define MT9P031_OUTPUT_CONTROL 0x07
+#define MT9P031_OUTPUT_CONTROL_CEN 2
+#define MT9P031_OUTPUT_CONTROL_SYN 1
+#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
+#define MT9P031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9P031_SHUTTER_WIDTH_LOWER 0x09
+#define MT9P031_SHUTTER_WIDTH_MIN 1
+#define MT9P031_SHUTTER_WIDTH_MAX 1048575
+#define MT9P031_SHUTTER_WIDTH_DEF 1943
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG_1 0x11
+#define MT9P031_PLL_CONFIG_2 0x12
+#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_FRAME_RESTART 0x0b
+#define MT9P031_SHUTTER_DELAY 0x0c
+#define MT9P031_RST 0x0d
+#define MT9P031_RST_ENABLE 1
+#define MT9P031_RST_DISABLE 0
+#define MT9P031_READ_MODE_1 0x1e
+#define MT9P031_READ_MODE_2 0x20
+#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15)
+#define MT9P031_READ_MODE_2_COL_MIR (1 << 14)
+#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6)
+#define MT9P031_ROW_ADDRESS_MODE 0x22
+#define MT9P031_COLUMN_ADDRESS_MODE 0x23
+#define MT9P031_GLOBAL_GAIN 0x35
+#define MT9P031_GLOBAL_GAIN_MIN 8
+#define MT9P031_GLOBAL_GAIN_MAX 1024
+#define MT9P031_GLOBAL_GAIN_DEF 8
+#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
+#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
+#define MT9P031_TEST_PATTERN 0xa0
+#define MT9P031_TEST_PATTERN_SHIFT 3
+#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
+#define MT9P031_TEST_PATTERN_DISABLE (0 << 0)
+#define MT9P031_TEST_PATTERN_GREEN 0xa1
+#define MT9P031_TEST_PATTERN_RED 0xa2
+#define MT9P031_TEST_PATTERN_BLUE 0xa3
+
+struct mt9p031_pll_divs {
+ u32 ext_freq;
+ u32 target_freq;
+ u8 m;
+ u8 n;
+ u8 p1;
+};
+
+struct mt9p031 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_rect crop; /* Sensor window */
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_ctrl_handler ctrls;
+ struct mt9p031_platform_data *pdata;
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+ u16 xskip;
+ u16 yskip;
+
+ const struct mt9p031_pll_divs *pll;
+
+ /* Registers cache */
+ u16 output_control;
+ u16 mode2;
+};
+
+static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9p031, subdev);
+}
+
+static int mt9p031_read(struct i2c_client *client, u8 reg)
+{
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : be16_to_cpu(data);
+}
+
+static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->output_control & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->output_control = value;
+ return 0;
+}
+
+static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->mode2 & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->mode2 = value;
+ return 0;
+}
+
+static int mt9p031_reset(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
+ 0);
+}
+
+/*
+ * This static table uses ext_freq and vdd_io values to select suitable
+ * PLL dividers m, n and p1 which have been calculated as specifiec in p36
+ * of Aptina's mt9p031 datasheet. New values should be added here.
+ */
+static const struct mt9p031_pll_divs mt9p031_divs[] = {
+ /* ext_freq target_freq m n p1 */
+ {21000000, 48000000, 26, 2, 6}
+};
+
+static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) {
+ if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq &&
+ mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) {
+ mt9p031->pll = &mt9p031_divs[i];
+ return 0;
+ }
+ }
+
+ dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, "
+ "target_freq = %d\n", mt9p031->pdata->ext_freq,
+ mt9p031->pdata->target_freq);
+ return -EINVAL;
+}
+
+static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
+ (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ return ret;
+}
+
+static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+
+ return mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWROFF);
+}
+
+static int mt9p031_power_on(struct mt9p031 *mt9p031)
+{
+ /* Ensure RESET_BAR is low */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ /* Emable clock */
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev,
+ mt9p031->pdata->ext_freq);
+
+ /* Now RESET_BAR must be high */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 0);
+ usleep_range(1000, 2000);
+ }
+
+ return 0;
+}
+
+static void mt9p031_power_off(struct mt9p031 *mt9p031)
+{
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
+}
+
+static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ if (!on) {
+ mt9p031_power_off(mt9p031);
+ return 0;
+ }
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_reset(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int mt9p031_set_params(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct v4l2_mbus_framefmt *format = &mt9p031->format;
+ const struct v4l2_rect *crop = &mt9p031->crop;
+ unsigned int hblank;
+ unsigned int vblank;
+ unsigned int xskip;
+ unsigned int yskip;
+ unsigned int xbin;
+ unsigned int ybin;
+ int ret;
+
+ /* Windows position and size.
+ *
+ * TODO: Make sure the start coordinates and window size match the
+ * skipping, binning and mirroring (see description of registers 2 and 4
+ * in table 13, and Binning section on page 41).
+ */
+ ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Row and column binning and skipping. Use the maximum binning value
+ * compatible with the skipping settings.
+ */
+ xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
+ yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
+ xbin = 1 << (ffs(xskip) - 1);
+ ybin = 1 << (ffs(yskip) - 1);
+
+ ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ /* Blanking - use minimum value for horizontal blanking and default
+ * value for vertical blanking.
+ */
+ hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3));
+ vblank = MT9P031_VERTICAL_BLANK_DEF;
+
+ ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret;
+
+ if (!enable) {
+ /* Stop sensor readout */
+ ret = mt9p031_set_output_control(mt9p031,
+ MT9P031_OUTPUT_CONTROL_CEN, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_disable(mt9p031);
+ }
+
+ ret = mt9p031_set_params(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ ret = mt9p031_set_output_control(mt9p031, 0,
+ MT9P031_OUTPUT_CONTROL_CEN);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_enable(mt9p031);
+}
+
+static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = mt9p031->format.code;
+ return 0;
+}
+
+static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (fse->index >= 8 || fse->code != mt9p031->format.code)
+ return -EINVAL;
+
+ fse->min_width = MT9P031_WINDOW_WIDTH_DEF
+ / min_t(unsigned int, 7, fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9p031_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
+ fmt->which);
+ return 0;
+}
+
+static int mt9p031_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9p031_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9p031_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
+ MT9P031_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
+ MT9P031_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9P031_WINDOW_WIDTH_MIN,
+ MT9P031_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9P031_WINDOW_HEIGHT_MIN,
+ MT9P031_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+
+static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9p031 *mt9p031 =
+ container_of(ctrl->handler, struct mt9p031, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 data;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
+ ctrl->val & 0xffff);
+
+ case V4L2_CID_GAIN:
+ /* Gain is controlled by 2 analog stages and a digital stage.
+ * Valid values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the
+ * second analog stage, first analog stage, digital stage order.
+ * Gain from a previous stage should be pushed to its maximum
+ * value before the next stage is used.
+ */
+ if (ctrl->val <= 32) {
+ data = ctrl->val;
+ } else if (ctrl->val <= 64) {
+ ctrl->val &= ~1;
+ data = (1 << 6) | (ctrl->val >> 1);
+ } else {
+ ctrl->val &= ~7;
+ data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
+ }
+
+ return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
+
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_COL_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_COL_MIR, 0);
+
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_ROW_MIR, 0);
+
+ case V4L2_CID_TEST_PATTERN:
+ if (!ctrl->val) {
+ ret = mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_BLC);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ MT9P031_TEST_PATTERN_DISABLE);
+ }
+
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
+ 0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
+ | MT9P031_TEST_PATTERN_ENABLE);
+ }
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
+ .s_ctrl = mt9p031_s_ctrl,
+};
+
+static const char * const mt9p031_test_pattern_menu[] = {
+ "Disabled",
+ "Color Field",
+ "Horizontal Gradient",
+ "Vertical Gradient",
+ "Diagonal Gradient",
+ "Classic Test Pattern",
+ "Walking 1s",
+ "Monochrome Horizontal Bars",
+ "Monochrome Vertical Bars",
+ "Vertical Color Bars",
+};
+
+static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
+ {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Test Pattern",
+ .min = 0,
+ .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1,
+ .step = 0,
+ .def = 0,
+ .flags = 0,
+ .menu_skip_mask = 0,
+ .qmenu = mt9p031_test_pattern_menu,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9p031->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9p031->power_count == !on) {
+ ret = __mt9p031_set_power(mt9p031, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9p031->power_count += on ? 1 : -1;
+ WARN_ON(mt9p031->power_count < 0);
+
+out:
+ mutex_unlock(&mt9p031->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9p031_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9P031 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9p031_read(client, MT9P031_CHIP_VERSION);
+ if (data != MT9P031_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9P031 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9p031_power_off(mt9p031);
+
+ dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9P031_COLUMN_START_DEF;
+ crop->top = MT9P031_ROW_START_DEF;
+ crop->width = MT9P031_WINDOW_WIDTH_DEF;
+ crop->height = MT9P031_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ format->code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ format->width = MT9P031_WINDOW_WIDTH_DEF;
+ format->height = MT9P031_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ mt9p031->xskip = 1;
+ mt9p031->yskip = 1;
+ return mt9p031_set_power(subdev, 1);
+}
+
+static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9p031_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
+ .s_power = mt9p031_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
+ .s_stream = mt9p031_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
+ .enum_mbus_code = mt9p031_enum_mbus_code,
+ .enum_frame_size = mt9p031_enum_frame_size,
+ .get_fmt = mt9p031_get_format,
+ .set_fmt = mt9p031_set_format,
+ .get_crop = mt9p031_get_crop,
+ .set_crop = mt9p031_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9p031_subdev_ops = {
+ .core = &mt9p031_subdev_core_ops,
+ .video = &mt9p031_subdev_video_ops,
+ .pad = &mt9p031_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
+ .registered = mt9p031_registered,
+ .open = mt9p031_open,
+ .close = mt9p031_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9p031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9p031_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mt9p031 *mt9p031;
+ unsigned int i;
+ 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");
+ return -EIO;
+ }
+
+ mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL);
+ if (mt9p031 == NULL)
+ return -ENOMEM;
+
+ mt9p031->pdata = pdata;
+ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
+ mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
+
+ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
+
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
+ MT9P031_SHUTTER_WIDTH_MAX, 1,
+ MT9P031_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
+ MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
+
+ mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
+
+ if (mt9p031->ctrls.error)
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9p031->ctrls.error);
+
+ mutex_init(&mt9p031->power_lock);
+ v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
+ mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
+
+ mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
+ if (ret < 0)
+ goto done;
+
+ mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
+ mt9p031->crop.top = MT9P031_ROW_START_DEF;
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->format.field = V4L2_FIELD_NONE;
+ mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ ret = mt9p031_pll_get_divs(mt9p031);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ media_entity_cleanup(&mt9p031->subdev.entity);
+ kfree(mt9p031);
+ }
+
+ return ret;
+}
+
+static int mt9p031_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9p031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9p031_id[] = {
+ { "mt9p031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9p031_id);
+
+static struct i2c_driver mt9p031_i2c_driver = {
+ .driver = {
+ .name = "mt9p031",
+ },
+ .probe = mt9p031_probe,
+ .remove = mt9p031_remove,
+ .id_table = mt9p031_id,
+};
+
+static int __init mt9p031_mod_init(void)
+{
+ return i2c_add_driver(&mt9p031_i2c_driver);
+}
+
+static void __exit mt9p031_mod_exit(void)
+{
+ i2c_del_driver(&mt9p031_i2c_driver);
+}
+
+module_init(mt9p031_mod_init);
+module_exit(mt9p031_mod_exit);
+
+MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c
new file mode 100644
index 000000000000..08074b8a2736
--- /dev/null
+++ b/drivers/media/video/mt9t001.c
@@ -0,0 +1,836 @@
+/*
+ * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron)
+ *
+ * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * Based on the MT9M001 driver,
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/mt9t001.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9T001_PIXEL_ARRAY_HEIGHT 1568
+#define MT9T001_PIXEL_ARRAY_WIDTH 2112
+
+#define MT9T001_CHIP_VERSION 0x00
+#define MT9T001_CHIP_ID 0x1621
+#define MT9T001_ROW_START 0x01
+#define MT9T001_ROW_START_MIN 0
+#define MT9T001_ROW_START_DEF 20
+#define MT9T001_ROW_START_MAX 1534
+#define MT9T001_COLUMN_START 0x02
+#define MT9T001_COLUMN_START_MIN 0
+#define MT9T001_COLUMN_START_DEF 32
+#define MT9T001_COLUMN_START_MAX 2046
+#define MT9T001_WINDOW_HEIGHT 0x03
+#define MT9T001_WINDOW_HEIGHT_MIN 1
+#define MT9T001_WINDOW_HEIGHT_DEF 1535
+#define MT9T001_WINDOW_HEIGHT_MAX 1567
+#define MT9T001_WINDOW_WIDTH 0x04
+#define MT9T001_WINDOW_WIDTH_MIN 1
+#define MT9T001_WINDOW_WIDTH_DEF 2047
+#define MT9T001_WINDOW_WIDTH_MAX 2111
+#define MT9T001_HORIZONTAL_BLANKING 0x05
+#define MT9T001_HORIZONTAL_BLANKING_MIN 21
+#define MT9T001_HORIZONTAL_BLANKING_MAX 1023
+#define MT9T001_VERTICAL_BLANKING 0x06
+#define MT9T001_VERTICAL_BLANKING_MIN 3
+#define MT9T001_VERTICAL_BLANKING_MAX 1023
+#define MT9T001_OUTPUT_CONTROL 0x07
+#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0)
+#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1)
+#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6)
+#define MT9T001_SHUTTER_WIDTH_HIGH 0x08
+#define MT9T001_SHUTTER_WIDTH_LOW 0x09
+#define MT9T001_SHUTTER_WIDTH_MIN 1
+#define MT9T001_SHUTTER_WIDTH_DEF 1561
+#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024)
+#define MT9T001_PIXEL_CLOCK 0x0a
+#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15)
+#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8)
+#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8
+#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0)
+#define MT9T001_FRAME_RESTART 0x0b
+#define MT9T001_SHUTTER_DELAY 0x0c
+#define MT9T001_SHUTTER_DELAY_MAX 2047
+#define MT9T001_RESET 0x0d
+#define MT9T001_READ_MODE1 0x1e
+#define MT9T001_READ_MODE_SNAPSHOT (1 << 8)
+#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9)
+#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10)
+#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11)
+#define MT9T001_READ_MODE2 0x20
+#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0)
+#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9)
+#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10)
+#define MT9T001_READ_MODE3 0x21
+#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0)
+#define MT9T001_READ_MODE_GHST_CTL (1 << 1)
+#define MT9T001_ROW_ADDRESS_MODE 0x22
+#define MT9T001_ROW_SKIP_MASK (7 << 0)
+#define MT9T001_ROW_BIN_MASK (3 << 3)
+#define MT9T001_ROW_BIN_SHIFT 3
+#define MT9T001_COLUMN_ADDRESS_MODE 0x23
+#define MT9T001_COLUMN_SKIP_MASK (7 << 0)
+#define MT9T001_COLUMN_BIN_MASK (3 << 3)
+#define MT9T001_COLUMN_BIN_SHIFT 3
+#define MT9T001_GREEN1_GAIN 0x2b
+#define MT9T001_BLUE_GAIN 0x2c
+#define MT9T001_RED_GAIN 0x2d
+#define MT9T001_GREEN2_GAIN 0x2e
+#define MT9T001_TEST_DATA 0x32
+#define MT9T001_GLOBAL_GAIN 0x35
+#define MT9T001_GLOBAL_GAIN_MIN 8
+#define MT9T001_GLOBAL_GAIN_MAX 1024
+#define MT9T001_BLACK_LEVEL 0x49
+#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b
+#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d
+#define MT9T001_CAL_THRESHOLDS 0x5f
+#define MT9T001_GREEN1_OFFSET 0x60
+#define MT9T001_GREEN2_OFFSET 0x61
+#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62
+#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0)
+#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1)
+#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12)
+#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13)
+#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14)
+#define MT9T001_RED_OFFSET 0x63
+#define MT9T001_BLUE_OFFSET 0x64
+
+struct mt9t001 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *gains[4];
+
+ u16 output_control;
+ u16 black_level;
+};
+
+static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9t001, subdev);
+}
+
+static int mt9t001_read(struct i2c_client *client, u8 reg)
+{
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : be16_to_cpu(data);
+}
+
+static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ u16 value = (mt9t001->output_control & ~clear) | set;
+ int ret;
+
+ if (value == mt9t001->output_control)
+ return 0;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->output_control = value;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *format = &mt9t001->format;
+ struct v4l2_rect *crop = &mt9t001->crop;
+ unsigned int hratio;
+ unsigned int vratio;
+ int ret;
+
+ if (!enable)
+ return mt9t001_set_output_control(mt9t001, mode, 0);
+
+ /* Configure the window size and row/column bin */
+ hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
+
+ ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ return mt9t001_set_output_control(mt9t001, 0, mode);
+}
+
+static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ return 0;
+}
+
+static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index;
+ fse->max_width = fse->min_width;
+ fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int mt9t001_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ return 0;
+}
+
+static int mt9t001_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9t001_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9t001_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2),
+ MT9T001_COLUMN_START_MIN,
+ MT9T001_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2),
+ MT9T001_ROW_START_MIN,
+ MT9T001_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9T001_WINDOW_WIDTH_MIN + 1,
+ MT9T001_WINDOW_WIDTH_MAX + 1);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9T001_WINDOW_HEIGHT_MIN + 1,
+ MT9T001_WINDOW_HEIGHT_MAX + 1);
+
+ rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004)
+
+#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004)
+
+static u16 mt9t001_gain_value(s32 *gain)
+{
+ /* Gain is controlled by 2 analog stages and a digital stage. Valid
+ * values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the second
+ * analog stage, first analog stage, digital stage order. Gain from a
+ * previous stage should be pushed to its maximum value before the next
+ * stage is used.
+ */
+ if (*gain <= 32)
+ return *gain;
+
+ if (*gain <= 64) {
+ *gain &= ~1;
+ return (1 << 6) | (*gain >> 1);
+ }
+
+ *gain &= ~7;
+ return ((*gain - 64) << 5) | (1 << 6) | 32;
+}
+
+static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze)
+{
+ return mt9t001_set_output_control(mt9t001,
+ freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC,
+ freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0);
+}
+
+static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const u8 gains[4] = {
+ MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN,
+ MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN
+ };
+
+ struct mt9t001 *mt9t001 =
+ container_of(ctrl->handler, struct mt9t001, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ unsigned int count;
+ unsigned int i;
+ u16 value;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN_RED:
+ case V4L2_CID_GAIN_GREEN_RED:
+ case V4L2_CID_GAIN_GREEN_BLUE:
+ case V4L2_CID_GAIN_BLUE:
+
+ /* Disable control updates if more than one control has changed
+ * in the cluster.
+ */
+ for (i = 0, count = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val != gain->cur.val)
+ count++;
+ }
+
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Update the gain controls. */
+ for (i = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val == gain->cur.val)
+ continue;
+
+ value = mt9t001_gain_value(&gain->val);
+ ret = mt9t001_write(client, gains[i], value);
+ if (ret < 0) {
+ mt9t001_ctrl_freeze(mt9t001, false);
+ return ret;
+ }
+ }
+
+ /* Enable control updates. */
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH,
+ ctrl->val >> 16);
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = mt9t001_set_output_control(mt9t001,
+ ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA,
+ ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2);
+
+ case V4L2_CID_BLACK_LEVEL_AUTO:
+ value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE;
+ ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->black_level = value;
+ break;
+
+ case V4L2_CID_BLACK_LEVEL_OFFSET:
+ ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val);
+
+ case V4L2_CID_BLACK_LEVEL_CALIBRATE:
+ return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ MT9T001_BLACK_LEVEL_RECALCULATE |
+ mt9t001->black_level);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
+ .s_ctrl = mt9t001_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9t001_ctrls[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Test pattern",
+ .min = 0,
+ .max = 1023,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Black Level, Auto",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Black Level, Offset",
+ .min = -256,
+ .max = 255,
+ .step = 1,
+ .def = 32,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_CALIBRATE,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Black Level, Calibrate",
+ .min = 0,
+ .max = 0,
+ .step = 0,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ },
+};
+
+static const struct v4l2_ctrl_config mt9t001_gains[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (R)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (B)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ },
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9T001_COLUMN_START_DEF;
+ crop->top = MT9T001_ROW_START_DEF;
+ crop->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
+ .s_stream = mt9t001_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
+ .enum_mbus_code = mt9t001_enum_mbus_code,
+ .enum_frame_size = mt9t001_enum_frame_size,
+ .get_fmt = mt9t001_get_format,
+ .set_fmt = mt9t001_set_format,
+ .get_crop = mt9t001_get_crop,
+ .set_crop = mt9t001_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+ .video = &mt9t001_subdev_video_ops,
+ .pad = &mt9t001_subdev_pad_ops,
+};
+
+static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+ .open = mt9t001_open,
+};
+
+static int mt9t001_video_probe(struct i2c_client *client)
+{
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
+ s32 data;
+ int ret;
+
+ dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
+ client->addr);
+
+ /* Reset the chip and stop data read out */
+ ret = mt9t001_write(client, MT9T001_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the pixel clock polarity */
+ if (pdata && pdata->clk_pol) {
+ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
+ MT9T001_PIXEL_CLOCK_INVERT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Read and check the sensor version */
+ data = mt9t001_read(client, MT9T001_CHIP_VERSION);
+ if (data != MT9T001_CHIP_ID) {
+ dev_err(&client->dev, "MT9T001 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9t001_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t001 *mt9t001;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ ret = mt9t001_video_probe(client);
+ if (ret < 0)
+ return ret;
+
+ mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL);
+ if (!mt9t001)
+ return -ENOMEM;
+
+ v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
+ ARRAY_SIZE(mt9t001_gains) + 2);
+
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
+ MT9T001_SHUTTER_WIDTH_MAX, 1,
+ MT9T001_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i)
+ mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls,
+ &mt9t001_gains[i], NULL);
+
+ v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains);
+
+ mt9t001->subdev.ctrl_handler = &mt9t001->ctrls;
+
+ if (mt9t001->ctrls.error) {
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9t001->ctrls.error);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mt9t001->crop.left = MT9T001_COLUMN_START_DEF;
+ mt9t001->crop.top = MT9T001_ROW_START_DEF;
+ mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ mt9t001->format.field = V4L2_FIELD_NONE;
+ mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops);
+ mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops;
+ mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ media_entity_cleanup(&mt9t001->subdev.entity);
+ kfree(mt9t001);
+ }
+
+ return ret;
+}
+
+static int mt9t001_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9t001);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t001_id[] = {
+ { "mt9t001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t001_id);
+
+static struct i2c_driver mt9t001_driver = {
+ .driver = {
+ .name = "mt9t001",
+ },
+ .probe = mt9t001_probe,
+ .remove = mt9t001_remove,
+ .id_table = mt9t001_id,
+};
+
+static int __init mt9t001_init(void)
+{
+ return i2c_add_driver(&mt9t001_driver);
+}
+
+static void __exit mt9t001_exit(void)
+{
+ i2c_del_driver(&mt9t001_driver);
+}
+
+module_init(mt9t001_init);
+module_exit(mt9t001_exit);
+
+MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 30547cc3f89b..7ee84cc578b9 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -13,11 +13,20 @@
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * ATTENTION: this driver still cannot be used outside of the soc-camera
+ * framework because of its PM implementation, using the video_device node.
+ * If hardware becomes available for testing, alternative PM approaches shall
+ * be considered and tested.
+ */
/*
* mt9t031 i2c address 0x5d
@@ -57,21 +66,20 @@
#define MT9T031_COLUMN_SKIP 32
#define MT9T031_ROW_SKIP 20
-#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
- SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
- SOCAM_MASTER | SOCAM_DATAWIDTH_10)
-
struct mt9t031 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
struct v4l2_rect rect; /* Sensor window */
int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
u16 xskip;
u16 yskip;
- unsigned int gain;
+ unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
- unsigned int exposure;
- unsigned char autoexposure;
};
static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
@@ -179,95 +187,6 @@ static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9t031_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-
- /* The caller should have queried our parameters, check anyway */
- if (flags & ~MT9T031_BUS_PARAM)
- return -EINVAL;
-
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
- reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
- else
- reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
-
- return 0;
-}
-
-static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
-}
-
-enum {
- MT9T031_CTRL_VFLIP,
- MT9T031_CTRL_HFLIP,
- MT9T031_CTRL_GAIN,
- MT9T031_CTRL_EXPOSURE,
- MT9T031_CTRL_EXPOSURE_AUTO,
-};
-
-static const struct v4l2_queryctrl mt9t031_controls[] = {
- [MT9T031_CTRL_VFLIP] = {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- [MT9T031_CTRL_HFLIP] = {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- [MT9T031_CTRL_GAIN] = {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },
- [MT9T031_CTRL_EXPOSURE] = {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },
- [MT9T031_CTRL_EXPOSURE_AUTO] = {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9t031_ops = {
- .set_bus_param = mt9t031_set_bus_param,
- .query_bus_param = mt9t031_query_bus_param,
- .controls = mt9t031_controls,
- .num_controls = ARRAY_SIZE(mt9t031_controls),
-};
-
/* target must be _even_ */
static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
{
@@ -353,7 +272,7 @@ static int mt9t031_set_params(struct i2c_client *client,
/*
* The caller provides a supported format, as guaranteed by
- * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap()
+ * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
*/
if (ret >= 0)
ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
@@ -364,17 +283,10 @@ static int mt9t031_set_params(struct i2c_client *client,
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
rect->height + mt9t031->y_skip_top - 1);
- if (ret >= 0 && mt9t031->autoexposure) {
- unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank;
- ret = set_shutter(client, total_h);
- if (ret >= 0) {
- const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
- const struct v4l2_queryctrl *qctrl =
- &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- shutter_max + qctrl->minimum;
- }
+ if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
+ mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
+
+ ret = set_shutter(client, mt9t031->total_h);
}
/* Re-enable register update, commit all changes */
@@ -543,71 +455,57 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
}
#endif
-static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- int data;
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
+ s32 min, max;
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9T031_READ_MODE_2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x8000);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, MT9T031_READ_MODE_2);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x4000);
- break;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = mt9t031->autoexposure;
- break;
- case V4L2_CID_GAIN:
- ctrl->value = mt9t031->gain;
- break;
- case V4L2_CID_EXPOSURE:
- ctrl->value = mt9t031->exposure;
+ min = mt9t031->exposure->minimum;
+ max = mt9t031->exposure->maximum;
+ mt9t031->exposure->val =
+ (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
+ / shutter_max + min;
break;
}
return 0;
}
-static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ struct v4l2_subdev *sd = &mt9t031->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *exp = mt9t031->exposure;
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_GAIN:
- qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN];
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
/* See Datasheet Table 7, Gain settings. */
- if (ctrl->value <= qctrl->default_value) {
+ if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = qctrl->default_value - qctrl->minimum;
- data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
@@ -616,9 +514,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
} else {
/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = qctrl->maximum - qctrl->default_value - 1;
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
/* calculated gain: map 65..127 to 9..1024 step 0.125 */
- unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
1015 + range / 2) / range + 9;
if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
@@ -635,19 +533,13 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
if (data < 0)
return -EIO;
}
+ return 0;
- /* Success */
- mt9t031->gain = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- /* mt9t031 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- const unsigned long range = qctrl->maximum - qctrl->minimum;
- const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
- range / 2) / range + 1;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned int range = exp->maximum - exp->minimum;
+ unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
u32 old;
get_shutter(client, &old);
@@ -655,27 +547,15 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
old, shutter);
if (set_shutter(client, shutter) < 0)
return -EIO;
- mt9t031->exposure = ctrl->value;
- mt9t031->autoexposure = 0;
- }
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value) {
+ } else {
const u16 vblank = MT9T031_VERTICAL_BLANK;
- const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
- unsigned int total_h = mt9t031->rect.height +
+ mt9t031->total_h = mt9t031->rect.height +
mt9t031->y_skip_top + vblank;
- if (set_shutter(client, total_h) < 0)
+ if (set_shutter(client, mt9t031->total_h) < 0)
return -EIO;
- qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
- mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
- (qctrl->maximum - qctrl->minimum)) /
- shutter_max + qctrl->minimum;
- mt9t031->autoexposure = 1;
- } else
- mt9t031->autoexposure = 0;
- break;
+ }
+ return 0;
default:
return -EINVAL;
}
@@ -700,8 +580,7 @@ static int mt9t031_runtime_suspend(struct device *dev)
static int mt9t031_runtime_resume(struct device *dev)
{
struct video_device *vdev = to_video_device(dev);
- struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
@@ -734,6 +613,19 @@ static struct device_type mt9t031_dev_type = {
.pm = &mt9t031_dev_pm_ops,
};
+static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+
+ if (on)
+ vdev->dev.type = &mt9t031_dev_type;
+ else
+ vdev->dev.type = NULL;
+
+ return 0;
+}
+
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
@@ -741,7 +633,6 @@ static struct device_type mt9t031_dev_type = {
static int mt9t031_video_probe(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct video_device *vdev = soc_camera_i2c_to_vdev(client);
s32 data;
int ret;
@@ -768,11 +659,7 @@ static int mt9t031_video_probe(struct i2c_client *client)
if (ret < 0)
dev_err(&client->dev, "Failed to initialise the camera\n");
else
- vdev->dev.type = &mt9t031_dev_type;
-
- /* mt9t031_idle() has reset the chip to default. */
- mt9t031->exposure = 255;
- mt9t031->gain = 64;
+ v4l2_ctrl_handler_setup(&mt9t031->hdl);
return ret;
}
@@ -787,10 +674,14 @@ static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
+ .g_volatile_ctrl = mt9t031_g_volatile_ctrl,
+ .s_ctrl = mt9t031_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
- .g_ctrl = mt9t031_g_ctrl,
- .s_ctrl = mt9t031_s_ctrl,
.g_chip_ident = mt9t031_g_chip_ident,
+ .s_power = mt9t031_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9t031_g_register,
.s_register = mt9t031_s_register,
@@ -807,6 +698,34 @@ static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+}
+
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.s_stream = mt9t031_s_stream,
.s_mbus_fmt = mt9t031_s_fmt,
@@ -816,6 +735,8 @@ static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.g_crop = mt9t031_g_crop,
.cropcap = mt9t031_cropcap,
.enum_mbus_fmt = mt9t031_enum_fmt,
+ .g_mbus_config = mt9t031_g_mbus_config,
+ .s_mbus_config = mt9t031_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
@@ -832,18 +753,13 @@ static int mt9t031_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9t031 *mt9t031;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
int ret;
- if (icd) {
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- if (!icl) {
- dev_err(&client->dev, "MT9T031 driver needs platform data\n");
- return -EINVAL;
- }
-
- icd->ops = &mt9t031_ops;
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -857,6 +773,33 @@ static int mt9t031_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
+ &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
+ if (mt9t031->hdl.error) {
+ int err = mt9t031->hdl.error;
+
+ kfree(mt9t031);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
mt9t031->y_skip_top = 0;
mt9t031->rect.left = MT9T031_COLUMN_SKIP;
@@ -864,12 +807,6 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031->rect.width = MT9T031_MAX_WIDTH;
mt9t031->rect.height = MT9T031_MAX_HEIGHT;
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9t031->autoexposure = 1;
-
mt9t031->xskip = 1;
mt9t031->yskip = 1;
@@ -880,8 +817,7 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031_disable(client);
if (ret) {
- if (icd)
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
kfree(mt9t031);
}
@@ -891,10 +827,9 @@ static int mt9t031_probe(struct i2c_client *client,
static int mt9t031_remove(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- if (icd)
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&mt9t031->subdev);
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
kfree(mt9t031);
return 0;
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index d2e0a50063a2..32114a3c0ca7 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -22,11 +22,11 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/mt9t112.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
@@ -34,11 +34,7 @@
/* #define EXT_CLOCK 24000000 */
/************************************************************************
-
-
macro
-
-
************************************************************************/
/*
* frame size
@@ -80,17 +76,8 @@
#define VAR8(id, offset) _VAR(id, offset, 0x8000)
/************************************************************************
-
-
struct
-
-
************************************************************************/
-struct mt9t112_frame_size {
- u16 width;
- u16 height;
-};
-
struct mt9t112_format {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
@@ -102,21 +89,17 @@ struct mt9t112_priv {
struct v4l2_subdev subdev;
struct mt9t112_camera_info *info;
struct i2c_client *client;
- struct soc_camera_device icd;
- struct mt9t112_frame_size frame;
+ struct v4l2_rect frame;
const struct mt9t112_format *format;
int model;
u32 flags;
/* for flags */
-#define INIT_DONE (1<<0)
+#define INIT_DONE (1 << 0)
+#define PCLK_RISING (1 << 1)
};
/************************************************************************
-
-
supported format
-
-
************************************************************************/
static const struct mt9t112_format mt9t112_cfmts[] = {
@@ -154,11 +137,7 @@ static const struct mt9t112_format mt9t112_cfmts[] = {
};
/************************************************************************
-
-
general function
-
-
************************************************************************/
static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
{
@@ -326,50 +305,47 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
n = (n >> 8) & 0x003f;
enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
- dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
vco = 2 * m * ext / (n+1);
enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
- dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable);
+ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
clk = vco / (p1+1) / (p2+1);
enable = (96000 < clk) ? "X" : "";
- dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
clk = vco / (p3+1);
enable = (768000 < clk) ? "X" : "";
- dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
clk = vco / (p6+1);
enable = (96000 < clk) ? "X" : "";
- dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
clk = vco / (p5+1);
enable = (54000 < clk) ? "X" : "";
- dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
clk = vco / (p4+1);
enable = (70000 < clk) ? "X" : "";
- dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
clk = vco / (p7+1);
- dev_info(&client->dev, "External sensor : %10u K\n", clk);
+ dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
clk = ext / (n+1);
enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
- dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable);
+ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
return 0;
}
#endif
-static void mt9t112_frame_check(u32 *width, u32 *height)
+static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
{
- if (*width > MAX_WIDTH)
- *width = MAX_WIDTH;
-
- if (*height > MAX_HEIGHT)
- *height = MAX_HEIGHT;
+ soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
+ soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
}
static int mt9t112_set_a_frame_size(const struct i2c_client *client,
@@ -758,48 +734,7 @@ static int mt9t112_init_camera(const struct i2c_client *client)
}
/************************************************************************
-
-
- soc_camera_ops
-
-
-************************************************************************/
-static int mt9t112_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9t112_priv *priv = to_mt9t112(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH;
-
- flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ?
- SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING;
-
- if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8)
- flags |= SOCAM_DATAWIDTH_8;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static struct soc_camera_ops mt9t112_ops = {
- .set_bus_param = mt9t112_set_bus_param,
- .query_bus_param = mt9t112_query_bus_param,
-};
-
-/************************************************************************
-
-
v4l2_subdev_core_ops
-
-
************************************************************************/
static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
@@ -850,11 +785,7 @@ static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
/************************************************************************
-
-
v4l2_subdev_video_ops
-
-
************************************************************************/
static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
{
@@ -877,8 +808,7 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
}
if (!(priv->flags & INIT_DONE)) {
- u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE &
- priv->info->flags) ? 0x0001 : 0x0000;
+ u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
ECHECKER(ret, mt9t112_init_camera(client));
@@ -910,19 +840,12 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
-static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+ const struct v4l2_rect *rect,
enum v4l2_mbus_pixelcode code)
{
- struct mt9t112_priv *priv = to_mt9t112(client);
int i;
- priv->format = NULL;
-
- /*
- * frame size check
- */
- mt9t112_frame_check(&width, &height);
-
/*
* get color format
*/
@@ -933,8 +856,13 @@ static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
if (i == ARRAY_SIZE(mt9t112_cfmts))
return -EINVAL;
- priv->frame.width = (u16)width;
- priv->frame.height = (u16)height;
+ priv->frame = *rect;
+
+ /*
+ * frame size check
+ */
+ mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
+ &priv->frame.left, &priv->frame.top);
priv->format = mt9t112_cfmts + i;
@@ -945,9 +873,12 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = VGA_WIDTH;
- a->bounds.height = VGA_HEIGHT;
- a->defrect = a->bounds;
+ a->bounds.width = MAX_WIDTH;
+ a->bounds.height = MAX_HEIGHT;
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VGA_WIDTH;
+ a->defrect.height = VGA_HEIGHT;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
@@ -957,11 +888,11 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = VGA_WIDTH;
- a->c.height = VGA_HEIGHT;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ a->c = priv->frame;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
@@ -969,10 +900,10 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
struct v4l2_rect *rect = &a->c;
- return mt9t112_set_params(client, rect->width, rect->height,
- V4L2_MBUS_FMT_UYVY8_2X8);
+ return mt9t112_set_params(priv, rect, priv->format->code);
}
static int mt9t112_g_fmt(struct v4l2_subdev *sd,
@@ -981,16 +912,9 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd,
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t112_priv *priv = to_mt9t112(client);
- if (!priv->format) {
- int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT,
- V4L2_MBUS_FMT_UYVY8_2X8);
- if (ret < 0)
- return ret;
- }
-
mf->width = priv->frame.width;
mf->height = priv->frame.height;
- /* TODO: set colorspace */
+ mf->colorspace = priv->format->colorspace;
mf->code = priv->format->code;
mf->field = V4L2_FIELD_NONE;
@@ -1001,17 +925,42 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect rect = {
+ .width = mf->width,
+ .height = mf->height,
+ .left = priv->frame.left,
+ .top = priv->frame.top,
+ };
+ int ret;
+
+ ret = mt9t112_set_params(priv, &rect, mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->format->colorspace;
- /* TODO: set colorspace */
- return mt9t112_set_params(client, mf->width, mf->height, mf->code);
+ return ret;
}
static int mt9t112_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- mt9t112_frame_check(&mf->width, &mf->height);
+ unsigned int top, left;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == mf->code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts)) {
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->colorspace = mt9t112_cfmts[i].colorspace;
+ }
+
+ mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
- /* TODO: set colorspace */
mf->field = V4L2_FIELD_NONE;
return 0;
@@ -1024,6 +973,35 @@ static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return -EINVAL;
*code = mt9t112_cfmts[index].code;
+
+ return 0;
+}
+
+static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ priv->flags |= PCLK_RISING;
+
return 0;
}
@@ -1036,31 +1014,24 @@ static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
.g_crop = mt9t112_g_crop,
.s_crop = mt9t112_s_crop,
.enum_mbus_fmt = mt9t112_enum_fmt,
+ .g_mbus_config = mt9t112_g_mbus_config,
+ .s_mbus_config = mt9t112_s_mbus_config,
};
/************************************************************************
-
-
i2c driver
-
-
************************************************************************/
static struct v4l2_subdev_ops mt9t112_subdev_ops = {
.core = &mt9t112_subdev_core_ops,
.video = &mt9t112_subdev_video_ops,
};
-static int mt9t112_camera_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9t112_camera_probe(struct i2c_client *client)
{
struct mt9t112_priv *priv = to_mt9t112(client);
const char *devname;
int chipid;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* check and show chip ID
*/
@@ -1088,20 +1059,21 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd,
static int mt9t112_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct mt9t112_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
- int ret;
+ struct mt9t112_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct v4l2_rect rect = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .left = (MAX_WIDTH - VGA_WIDTH) / 2,
+ .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+ };
+ int ret;
- if (!icd) {
- dev_err(&client->dev, "mt9t112: missing soc-camera data!\n");
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "mt9t112: missing platform data!\n");
return -EINVAL;
}
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
- return -EINVAL;
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1110,13 +1082,12 @@ static int mt9t112_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
- icd->ops = &mt9t112_ops;
-
- ret = mt9t112_camera_probe(icd, client);
- if (ret) {
- icd->ops = NULL;
+ ret = mt9t112_camera_probe(client);
+ if (ret)
kfree(priv);
- }
+
+ /* Cannot fail: using the default supported pixel code */
+ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
return ret;
}
@@ -1124,9 +1095,7 @@ static int mt9t112_probe(struct i2c_client *client,
static int mt9t112_remove(struct i2c_client *client)
{
struct mt9t112_priv *priv = to_mt9t112(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
kfree(priv);
return 0;
}
@@ -1147,11 +1116,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
};
/************************************************************************
-
-
module function
-
-
************************************************************************/
static int __init mt9t112_module_init(void)
{
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 51b0fccbfe70..b6a29f7de82c 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -14,9 +14,11 @@
#include <linux/delay.h>
#include <linux/log2.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
/*
* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
@@ -100,6 +102,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
struct mt9v022 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/auto-gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
struct v4l2_rect rect; /* Sensor window */
const struct mt9v022_datafmt *fmt;
const struct mt9v022_datafmt *fmts;
@@ -178,6 +191,8 @@ static int mt9v022_init(struct i2c_client *client)
ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
if (!ret)
ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
+ if (!ret)
+ return v4l2_ctrl_handler_setup(&mt9v022->hdl);
return ret;
}
@@ -199,78 +214,6 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int mt9v022_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
- int ret;
- u16 pixclk = 0;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param) {
- ret = icl->set_bus_param(icl, width_flag);
- if (ret)
- return ret;
- } else {
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- if (width_flag != SOCAM_DATAWIDTH_10)
- return -EINVAL;
- }
-
- flags = soc_camera_apply_sensor_flags(icl, flags);
-
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
- pixclk |= 0x10;
-
- if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH))
- pixclk |= 0x1;
-
- if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH))
- pixclk |= 0x2;
-
- ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
- if (ret < 0)
- return ret;
-
- if (!(flags & SOCAM_MASTER))
- mt9v022->chip_control &= ~0x8;
-
- ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
- pixclk, mt9v022->chip_control);
-
- return 0;
-}
-
-static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE |
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -389,7 +332,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd,
/*
* The caller provides a supported format, as verified per call to
- * icd->try_fmt(), datawidth is from our supported format list
+ * .try_mbus_fmt(), datawidth is from our supported format list
*/
switch (mf->code) {
case V4L2_MBUS_FMT_Y8_1X8:
@@ -502,236 +445,131 @@ static int mt9v022_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9v022_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Analog Gain",
- .minimum = 64,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9v022_ops = {
- .set_bus_param = mt9v022_set_bus_param,
- .query_bus_param = mt9v022_query_bus_param,
- .controls = mt9v022_controls,
- .num_controls = ARRAY_SIZE(mt9v022_controls),
-};
-
-static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct v4l2_queryctrl *qctrl;
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ struct v4l2_ctrl *exp = mt9v022->exposure;
unsigned long range;
int data;
- qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
-
switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- data = reg_read(client, MT9V022_READ_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x10);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, MT9V022_READ_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x20);
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x1);
- break;
case V4L2_CID_AUTOGAIN:
- data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
- if (data < 0)
- return -EIO;
- ctrl->value = !!(data & 0x2);
- break;
- case V4L2_CID_GAIN:
data = reg_read(client, MT9V022_ANALOG_GAIN);
if (data < 0)
return -EIO;
- range = qctrl->maximum - qctrl->minimum;
- ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum;
-
- break;
- case V4L2_CID_EXPOSURE:
+ range = gain->maximum - gain->minimum;
+ gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
if (data < 0)
return -EIO;
- range = qctrl->maximum - qctrl->minimum;
- ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum;
-
- break;
+ range = exp->maximum - exp->minimum;
+ exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
-static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
{
- int data;
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct v4l2_queryctrl *qctrl;
-
- qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
+ int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x10);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x10);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x20);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x20);
if (data < 0)
return -EIO;
- break;
- case V4L2_CID_GAIN:
- /* mt9v022 has minimum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val) {
+ if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+ } else {
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ /* mt9v022 has minimum == default */
+ unsigned long range = gain->maximum - gain->minimum;
/* Valid values 16 to 64, 32 to 64 must be even. */
- unsigned long gain = ((ctrl->value - qctrl->minimum) *
+ unsigned long gain_val = ((gain->val - gain->minimum) *
48 + range / 2) / range + 16;
- if (gain >= 32)
- gain &= ~1;
+
+ if (gain_val >= 32)
+ gain_val &= ~1;
+
/*
* The user wants to set gain manually, hope, she
* knows, what she's doing... Switch AGC off.
*/
-
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
- reg_read(client, MT9V022_ANALOG_GAIN), gain);
- if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0)
+ reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
+ if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
return -EIO;
}
- break;
- case V4L2_CID_EXPOSURE:
- /* mt9v022 has maximum == default */
- if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
- return -EINVAL;
- else {
- unsigned long range = qctrl->maximum - qctrl->minimum;
- unsigned long shutter = ((ctrl->value - qctrl->minimum) *
- 479 + range / 2) / range + 1;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_AUTO) {
+ data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ } else {
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) *
+ 479 + range / 2) / range + 1;
+
/*
* The user wants to set shutter width manually, hope,
* she knows, what she's doing... Switch AEC off.
*/
-
- if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
+ data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ if (data < 0)
return -EIO;
-
dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
- reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
- shutter);
+ reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
+ shutter);
if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- shutter) < 0)
+ shutter) < 0)
return -EIO;
}
- break;
- case V4L2_CID_AUTOGAIN:
- if (ctrl->value)
- data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2);
- else
- data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2);
- if (data < 0)
- return -EIO;
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->value)
- data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- else
- data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- if (data < 0)
- return -EIO;
- break;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9v022_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int mt9v022_video_probe(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
s32 data;
int ret;
unsigned long flags;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/* Read out the chip version register */
data = reg_read(client, MT9V022_CHIP_VERSION);
@@ -805,16 +643,6 @@ ei2c:
return ret;
}
-static void mt9v022_video_remove(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- dev_dbg(icd->pdev, "Video removed: %p, %p\n",
- icd->parent, icd->vdev);
- if (icl->free_bus)
- icl->free_bus(icl);
-}
-
static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -825,9 +653,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
return 0;
}
+static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
+ .g_volatile_ctrl = mt9v022_g_volatile_ctrl,
+ .s_ctrl = mt9v022_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
- .g_ctrl = mt9v022_g_ctrl,
- .s_ctrl = mt9v022_s_ctrl,
.g_chip_ident = mt9v022_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v022_g_register,
@@ -848,6 +679,72 @@ static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
+ int ret;
+ u16 pixclk = 0;
+
+ if (icl->set_bus_param) {
+ ret = icl->set_bus_param(icl, 1 << (bps - 1));
+ if (ret)
+ return ret;
+ } else if (bps != 10) {
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return -EINVAL;
+ }
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ pixclk |= 0x10;
+
+ if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
+ pixclk |= 0x1;
+
+ if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
+ pixclk |= 0x2;
+
+ ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
+ if (ret < 0)
+ return ret;
+
+ if (!(flags & V4L2_MBUS_MASTER))
+ mt9v022->chip_control &= ~0x8;
+
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ pixclk, mt9v022->chip_control);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.s_stream = mt9v022_s_stream,
.s_mbus_fmt = mt9v022_s_fmt,
@@ -857,6 +754,8 @@ static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.g_crop = mt9v022_g_crop,
.cropcap = mt9v022_cropcap,
.enum_mbus_fmt = mt9v022_enum_fmt,
+ .g_mbus_config = mt9v022_g_mbus_config,
+ .s_mbus_config = mt9v022_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
@@ -873,17 +772,10 @@ static int mt9v022_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9v022 *mt9v022;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
int ret;
- if (!icd) {
- dev_err(&client->dev, "MT9V022: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9V022 driver needs platform data\n");
return -EINVAL;
@@ -900,10 +792,39 @@ static int mt9v022_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
+ &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
+ if (mt9v022->hdl.error) {
+ int err = mt9v022->hdl.error;
+
+ kfree(mt9v022);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
- icd->ops = &mt9v022_ops;
/*
* MT9V022 _really_ corrupts the first read out line.
* TODO: verify on i.MX31
@@ -914,9 +835,9 @@ static int mt9v022_probe(struct i2c_client *client,
mt9v022->rect.width = MT9V022_MAX_WIDTH;
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
- ret = mt9v022_video_probe(icd, client);
+ ret = mt9v022_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
kfree(mt9v022);
}
@@ -926,10 +847,12 @@ static int mt9v022_probe(struct i2c_client *client,
static int mt9v022_remove(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
- mt9v022_video_remove(icd);
+ v4l2_device_unregister_subdev(&mt9v022->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
kfree(mt9v022);
return 0;
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 087db12a3a67..18e94c7d2be8 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -78,11 +78,10 @@
#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
-#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \
- SOCAM_DATAWIDTH_8)
+#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */
@@ -490,59 +489,73 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd,
static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
unsigned int csicr1;
int ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
/* MX1 supports only 8bit buswidth */
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- CSI_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, CSI_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = CSI_BUS_FLAGS;
+ }
/* Make choises, based on platform choice */
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
- (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
- common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
csicr1 = __raw_readl(pcdev->base + CSICR1);
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
- if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
- if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
csicr1 |= CSICR1_DATA_POL;
__raw_writel(csicr1, pcdev->base + CSICR1);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index ec2410c0c806..a803d9ea8fd6 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -686,16 +686,15 @@ static void mx2_camera_init_videobuf(struct videobuf_queue *q,
icd, &icd->video_lock);
}
-#define MX2_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \
- SOCAM_MASTER | \
- SOCAM_VSYNC_ACTIVE_HIGH | \
- SOCAM_VSYNC_ACTIVE_LOW | \
- SOCAM_HSYNC_ACTIVE_HIGH | \
- SOCAM_HSYNC_ACTIVE_LOW | \
- SOCAM_PCLK_SAMPLE_RISING | \
- SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | \
- SOCAM_DATA_ACTIVE_LOW)
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
{
@@ -770,46 +769,59 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
- int ret = 0;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
int bytesperline;
u32 csicr1 = pcdev->csicr1;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- MX2_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
- if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
- if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH)
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_HSYNC_POL;
if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
csicr1 |= CSICR1_SWAP16_EN;
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c045b47803ad..f96f92f00f92 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -109,10 +109,12 @@ struct mx3_camera_dev {
unsigned long platform_flags;
unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
struct list_head capture;
spinlock_t lock; /* Protects video buffer lists */
struct mx3_camera_buffer *active;
+ size_t buf_total;
struct vb2_alloc_ctx *alloc_ctx;
enum v4l2_field field;
int sequence;
@@ -190,79 +192,53 @@ static void mx3_cam_dma_done(void *arg)
* Calculate the __buffer__ (not data) size and number of buffers.
*/
static int mx3_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
- unsigned long sizes[], void *alloc_ctxs[])
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
+ int bytes_per_line;
+ unsigned int height;
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- *num_planes = 1;
-
- mx3_cam->sequence = 0;
- sizes[0] = bytes_per_line * icd->user_height;
- alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
- if (!*count)
- *count = 32;
-
- if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0];
-
- return 0;
-}
-
-static int mx3_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct scatterlist *sg;
- struct mx3_camera_buffer *buf;
- size_t new_size;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
-
+ height = icd->user_height;
+ }
if (bytes_per_line < 0)
return bytes_per_line;
- buf = to_mx3_vb(vb);
- sg = &buf->sg;
+ sizes[0] = bytes_per_line * height;
- new_size = bytes_per_line * icd->user_height;
-
- if (vb2_plane_size(vb, 0) < new_size) {
- dev_err(icd->parent, "Buffer too small (%lu < %zu)\n",
- vb2_plane_size(vb, 0), new_size);
- return -ENOBUFS;
- }
-
- if (buf->state == CSI_BUF_NEEDS_INIT) {
- sg_dma_address(sg) = vb2_dma_contig_plane_paddr(vb, 0);
- sg_dma_len(sg) = new_size;
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
- buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
- &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
- DMA_PREP_INTERRUPT);
- if (!buf->txd)
- return -EIO;
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
- buf->txd->callback_param = buf->txd;
- buf->txd->callback = mx3_cam_dma_done;
+ if (!*count)
+ *count = 2;
- buf->state = CSI_BUF_PREPARED;
- }
+ /* If *num_planes != 0, we have already verified *count. */
+ if (!*num_planes &&
+ sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
- vb2_set_plane_payload(vb, 0, new_size);
+ *num_planes = 1;
return 0;
}
@@ -286,28 +262,58 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
- struct dma_async_tx_descriptor *txd = buf->txd;
- struct idmac_channel *ichan = to_idmac_chan(txd->chan);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
struct idmac_video_param *video = &ichan->params.video;
- dma_cookie_t cookie;
- u32 fourcc = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
unsigned long flags;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ BUG_ON(bytes_per_line <= 0);
+
+ new_size = bytes_per_line * icd->user_height;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (buf->state == CSI_BUF_NEEDS_INIT) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = ichan->dma_chan.device->device_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->state = CSI_BUF_PREPARED;
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
/* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc);
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
/*
- * If the IPU DMA channel is configured to transport
- * generic 8-bit data, we have to set up correctly the
- * geometry parameters upon the current pixel format.
- * So, since the DMA horizontal parameters are expressed
- * in bytes not pixels, convert these in the right unit.
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
*/
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- BUG_ON(bytes_per_line <= 0);
-
video->out_width = bytes_per_line;
video->out_height = icd->user_height;
video->out_stride = bytes_per_line;
@@ -351,6 +357,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
mx3_cam->active = NULL;
spin_unlock_irqrestore(&mx3_cam->lock, flags);
+error:
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
@@ -384,17 +391,24 @@ static void mx3_videobuf_release(struct vb2_buffer *vb)
}
spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
}
static int mx3_videobuf_init(struct vb2_buffer *vb)
{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
/* This is for locking debugging only */
INIT_LIST_HEAD(&buf->queue);
sg_init_table(&buf->sg, 1);
buf->state = CSI_BUF_NEEDS_INIT;
- buf->txd = NULL;
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
return 0;
}
@@ -405,13 +419,12 @@ static int mx3_stop_streaming(struct vb2_queue *q)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct dma_chan *chan;
struct mx3_camera_buffer *buf, *tmp;
unsigned long flags;
if (ichan) {
- chan = &ichan->dma_chan;
- chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ struct dma_chan *chan = &ichan->dma_chan;
+ chan->device->device_control(chan, DMA_PAUSE, 0);
}
spin_lock_irqsave(&mx3_cam->lock, flags);
@@ -419,8 +432,8 @@ static int mx3_stop_streaming(struct vb2_queue *q)
mx3_cam->active = NULL;
list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
- buf->state = CSI_BUF_NEEDS_INIT;
list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&mx3_cam->lock, flags);
@@ -430,7 +443,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
static struct vb2_ops mx3_videobuf_ops = {
.queue_setup = mx3_videobuf_setup,
- .buf_prepare = mx3_videobuf_prepare,
.buf_queue = mx3_videobuf_queue,
.buf_cleanup = mx3_videobuf_release,
.buf_init = mx3_videobuf_init,
@@ -514,6 +526,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
mx3_camera_activate(mx3_cam, icd);
+ mx3_cam->buf_total = 0;
mx3_cam->icd = icd;
dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
@@ -548,58 +561,27 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
unsigned char buswidth, unsigned long *flags)
{
/*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
* Platform specified synchronization and pixel clock polarities are
* only a recommendation and are only used during probing. MX3x
* camera interface only works in master mode, i.e., uses HSYNC and
* VSYNC signals from the sensor
*/
- *flags = SOCAM_MASTER |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_DATA_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_LOW;
-
- /*
- * If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to schift bits
- */
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
- SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
- SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
- *flags |= SOCAM_DATAWIDTH_4;
-
- switch (buswidth) {
- case 15:
- if (!(*flags & SOCAM_DATAWIDTH_15))
- return -EINVAL;
- break;
- case 10:
- if (!(*flags & SOCAM_DATAWIDTH_10))
- return -EINVAL;
- break;
- case 8:
- if (!(*flags & SOCAM_DATAWIDTH_8))
- return -EINVAL;
- break;
- case 4:
- if (!(*flags & SOCAM_DATAWIDTH_4))
- return -EINVAL;
- break;
- default:
- dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
- "Unsupported bus width %d\n", buswidth);
- return -EINVAL;
- }
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
return 0;
}
@@ -607,9 +589,11 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
const unsigned int depth)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- unsigned long bus_flags, camera_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret = test_platform_param(mx3_cam, depth, &bus_flags);
dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
@@ -617,15 +601,21 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- if (ret < 0)
- dev_warn(icd->parent,
- "Flags incompatible: camera %lx, host %lx\n",
- camera_flags, bus_flags);
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
- return ret;
+ return 0;
}
static bool chan_filter(struct dma_chan *chan, void *arg)
@@ -994,9 +984,11 @@ static int mx3_camera_querycap(struct soc_camera_host *ici,
static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- unsigned long bus_flags, camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
u32 dw, sens_conf;
const struct soc_mbus_pixelfmt *fmt;
int buswidth;
@@ -1008,83 +1000,76 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
if (!fmt)
return -EINVAL;
- buswidth = fmt->bits_per_sample;
- ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
-
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
dev_warn(dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
- camera_flags, bus_flags, common_flags);
- if (!common_flags) {
- dev_dbg(dev, "no common flags");
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
}
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
/* Make choices, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
- (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
- /*
- * Make the camera work in widest common mode, we'll take care of
- * the rest
- */
- if (common_flags & SOCAM_DATAWIDTH_15)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_15;
- else if (common_flags & SOCAM_DATAWIDTH_10)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_10;
- else if (common_flags & SOCAM_DATAWIDTH_8)
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_8;
- else
- common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
- SOCAM_DATAWIDTH_4;
-
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0) {
- dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n",
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
common_flags, ret);
return ret;
}
@@ -1108,13 +1093,13 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
/* This has been set in mx3_camera_activate(), but we clear it above */
sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
- if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
- if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
/* Just do what we're asked to do */
@@ -1199,6 +1184,14 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
"data widths, using default 8 bit\n");
mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
}
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
if (!mx3_cam->mclk) {
@@ -1281,8 +1274,6 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev)
dmaengine_put();
- dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n");
-
return 0;
}
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 0b3850023505..f0c3968ac7eb 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -21,6 +21,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DEBUG_VARIABLE debug
#include <media/saa7146_vv.h>
@@ -171,7 +173,7 @@ static int mxb_probe(struct saa7146_dev *dev)
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if (mxb == NULL) {
- DEB_D(("not enough kernel memory.\n"));
+ DEB_D("not enough kernel memory\n");
return -ENOMEM;
}
@@ -179,7 +181,7 @@ static int mxb_probe(struct saa7146_dev *dev)
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
+ DEB_S("cannot register i2c-device. skipping.\n");
kfree(mxb);
return -EFAULT;
}
@@ -200,7 +202,7 @@ static int mxb_probe(struct saa7146_dev *dev)
/* check if all devices are present */
if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
!mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
- printk("mxb: did not find all i2c devices. aborting\n");
+ pr_err("did not find all i2c devices. aborting\n");
i2c_del_adapter(&mxb->i2c_adapter);
kfree(mxb);
return -ENODEV;
@@ -346,11 +348,11 @@ static int mxb_init_done(struct saa7146_dev* dev)
msg.buf = &mxb_saa7740_init[i].data[0];
err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
if (err != 1) {
- DEB_D(("failed to initialize 'sound arena module'.\n"));
+ DEB_D("failed to initialize 'sound arena module'\n");
goto err;
}
}
- INFO(("'sound arena module' detected.\n"));
+ pr_info("'sound arena module' detected\n");
}
err:
/* the rest for saa7146: you should definitely set some basic values
@@ -390,7 +392,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == qc->id) {
*qc = mxb_controls[i];
- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
return 0;
}
}
@@ -413,11 +415,11 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
if (vc->id == V4L2_CID_AUDIO_MUTE) {
vc->value = mxb->cur_mute;
- DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
return 0;
}
- DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
return 0;
}
@@ -440,14 +442,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
/* switch the audio-source */
tea6420_route_line(mxb, vc->value ? 6 :
video_audio_connect[mxb->cur_input]);
- DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
+ DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value);
}
return 0;
}
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
if (i->index >= MXB_INPUTS)
return -EINVAL;
memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
@@ -460,7 +462,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
*i = mxb->cur_input;
- DEB_EE(("VIDIOC_G_INPUT %d.\n", *i));
+ DEB_EE("VIDIOC_G_INPUT %d\n", *i);
return 0;
}
@@ -471,7 +473,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
int err = 0;
int i = 0;
- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
if (input >= MXB_INPUTS)
return -EINVAL;
@@ -514,7 +516,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
/* switch video in saa7111a */
if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
- printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a.\n");
+ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
/* switch the audio-source only if necessary */
if (0 == mxb->cur_mute)
@@ -529,11 +531,12 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (t->index) {
- DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
+ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
return -EINVAL;
}
- DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
+ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
memset(t, 0, sizeof(*t));
strlcpy(t->name, "TV Tuner", sizeof(t->name));
@@ -550,7 +553,8 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (t->index) {
- DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index));
+ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
return -EINVAL;
}
@@ -564,14 +568,14 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (mxb->cur_input) {
- DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
- mxb->cur_input));
+ DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
return -EINVAL;
}
*f = mxb->cur_freq;
- DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
+ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
return 0;
}
@@ -588,12 +592,13 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
return -EINVAL;
if (mxb->cur_input) {
- DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
+ DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
return -EINVAL;
}
mxb->cur_freq = *f;
- DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
+ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
/* tune in desired frequency */
tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
@@ -612,18 +617,18 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
struct mxb *mxb = (struct mxb *)dev->ext_priv;
if (a->index > MXB_INPUTS) {
- DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
+ DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index);
return -EINVAL;
}
- DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
+ DEB_EE("VIDIOC_G_AUDIO %d\n", a->index);
memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
return 0;
}
static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
- DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
+ DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
return 0;
}
@@ -655,11 +660,11 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio,
int i = *(int *)arg;
if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i));
+ DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i);
return -EINVAL;
}
- DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i));
+ DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i);
tea6420_route_cd(mxb, i);
return 0;
@@ -669,17 +674,18 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio,
int i = *(int *)arg;
if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i));
+ DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n",
+ i);
return -EINVAL;
}
- DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i));
+ DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i);
tea6420_route_line(mxb, i);
return 0;
}
default:
/*
- DEB2(printk("does not handle this ioctl.\n"));
+ DEB2(pr_err("does not handle this ioctl\n"));
*/
return -ENOIOCTLCMD;
}
@@ -693,7 +699,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
{
struct mxb *mxb;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_vv_init(dev, &vv_data);
if (mxb_probe(dev)) {
@@ -720,7 +726,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
#endif
vv_data.ops.vidioc_default = vidioc_default;
if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
- ERR(("cannot register capture v4l2 device. skipping.\n"));
+ ERR("cannot register capture v4l2 device. skipping.\n");
saa7146_vv_release(dev);
return -1;
}
@@ -728,11 +734,11 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
if (MXB_BOARD_CAN_DO_VBI(dev)) {
if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
- ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ ERR("cannot register vbi v4l2 device. skipping.\n");
}
}
- printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
+ pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
mxb_num++;
mxb_init_done(dev);
@@ -743,7 +749,7 @@ static int mxb_detach(struct saa7146_dev *dev)
{
struct mxb *mxb = (struct mxb *)dev->ext_priv;
- DEB_EE(("dev:%p\n", dev));
+ DEB_EE("dev:%p\n", dev);
saa7146_unregister_device(&mxb->video_dev,dev);
if (MXB_BOARD_CAN_DO_VBI(dev))
@@ -765,7 +771,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
if (V4L2_STD_PAL_I == standard->id) {
v4l2_std_id std = V4L2_STD_PAL_I;
- DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n"));
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* unset the 7111 gpio register -- I don't know what this does exactly */
@@ -774,7 +780,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
} else {
v4l2_std_id std = V4L2_STD_PAL_BG;
- DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n"));
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* set the 7111 gpio register -- I don't know what this does exactly */
@@ -852,7 +858,7 @@ static struct saa7146_extension extension = {
static int __init mxb_init_module(void)
{
if (saa7146_register_extension(&extension)) {
- DEB_S(("failed to register extension.\n"));
+ DEB_S("failed to register extension\n");
return -ENODEV;
}
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c
index 35f722a88f76..6cd21cf91b44 100644
--- a/drivers/media/video/noon010pc30.c
+++ b/drivers/media/video/noon010pc30.c
@@ -1,7 +1,7 @@
/*
* Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP
*
- * Copyright (C) 2010 Samsung Electronics
+ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
* Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* Initial register configuration based on a driver authored by
@@ -10,7 +10,7 @@
* 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 vergsion.
+ * (at your option) any later version.
*/
#include <linux/delay.h>
@@ -131,17 +131,23 @@ static const char * const noon010_supply_name[] = {
struct noon010_info {
struct v4l2_subdev sd;
+ struct media_pad pad;
struct v4l2_ctrl_handler hdl;
- const struct noon010pc30_platform_data *pdata;
+ struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
+ u32 gpio_nreset;
+ u32 gpio_nstby;
+
+ /* Protects the struct members below */
+ struct mutex lock;
+
const struct noon010_format *curr_fmt;
const struct noon010_frmsize *curr_win;
+ unsigned int apply_new_cfg:1;
+ unsigned int streaming:1;
unsigned int hflip:1;
unsigned int vflip:1;
unsigned int power:1;
u8 i2c_reg_page;
- struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
- u32 gpio_nreset;
- u32 gpio_nstby;
};
struct i2c_regval {
@@ -292,8 +298,10 @@ static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep)
u8 reg = sleep ? 0xF1 : 0xF0;
int ret = 0;
- if (reset)
+ if (reset) {
ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+ udelay(20);
+ }
if (!ret) {
ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
if (reset && !ret)
@@ -313,6 +321,7 @@ static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
return ret;
}
+/* Called with struct noon010_info.lock mutex held */
static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
{
struct noon010_info *info = to_noon010(sd);
@@ -340,21 +349,18 @@ static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
static int noon010_set_params(struct v4l2_subdev *sd)
{
struct noon010_info *info = to_noon010(sd);
- int ret;
- if (!info->curr_win)
- return -EINVAL;
-
- ret = cam_i2c_write(sd, VDO_CTL_REG(0), info->curr_win->vid_ctl1);
-
- if (!ret && info->curr_fmt)
- ret = cam_i2c_write(sd, ISP_CTL_REG(0),
- info->curr_fmt->ispctl1_reg);
- return ret;
+ int ret = cam_i2c_write(sd, VDO_CTL_REG(0),
+ info->curr_win->vid_ctl1);
+ if (ret)
+ return ret;
+ return cam_i2c_write(sd, ISP_CTL_REG(0),
+ info->curr_fmt->ispctl1_reg);
}
/* Find nearest matching image pixel size. */
-static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf)
+static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf,
+ const struct noon010_frmsize **size)
{
unsigned int min_err = ~0;
int i = ARRAY_SIZE(noon010_sizes);
@@ -374,11 +380,14 @@ static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf)
if (match) {
mf->width = match->width;
mf->height = match->height;
+ if (size)
+ *size = match;
return 0;
}
return -EINVAL;
}
+/* Called with info.lock mutex held */
static int power_enable(struct noon010_info *info)
{
int ret;
@@ -419,6 +428,7 @@ static int power_enable(struct noon010_info *info)
return 0;
}
+/* Called with info.lock mutex held */
static int power_disable(struct noon010_info *info)
{
int ret;
@@ -448,147 +458,175 @@ static int power_disable(struct noon010_info *info)
static int noon010_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
__func__, ctrl->id, ctrl->val);
+ mutex_lock(&info->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (!info->power)
+ goto unlock;
+
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- return noon010_enable_autowhitebalance(sd, ctrl->val);
+ ret = noon010_enable_autowhitebalance(sd, ctrl->val);
+ break;
case V4L2_CID_BLUE_BALANCE:
- return cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ break;
case V4L2_CID_RED_BALANCE:
- return cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+unlock:
+ mutex_unlock(&info->lock);
+ return ret;
}
-static int noon010_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+static int noon010_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
{
- if (!code || index >= ARRAY_SIZE(noon010_formats))
+ if (code->index >= ARRAY_SIZE(noon010_formats))
return -EINVAL;
- *code = noon010_formats[index].code;
+ code->code = noon010_formats[code->index].code;
return 0;
}
-static int noon010_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
{
struct noon010_info *info = to_noon010(sd);
- int ret;
-
- if (!mf)
- return -EINVAL;
+ struct v4l2_mbus_framefmt *mf;
- if (!info->curr_win || !info->curr_fmt) {
- ret = noon010_set_params(sd);
- if (ret)
- return ret;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ }
+ return 0;
}
+ mf = &fmt->format;
- mf->width = info->curr_win->width;
- mf->height = info->curr_win->height;
- mf->code = info->curr_fmt->code;
- mf->colorspace = info->curr_fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
+ mutex_lock(&info->lock);
+ mf->width = info->curr_win->width;
+ mf->height = info->curr_win->height;
+ mf->code = info->curr_fmt->code;
+ mf->colorspace = info->curr_fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mutex_unlock(&info->lock);
return 0;
}
/* Return nearest media bus frame format. */
-static const struct noon010_format *try_fmt(struct v4l2_subdev *sd,
+static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
int i = ARRAY_SIZE(noon010_formats);
- noon010_try_frame_size(mf);
-
- while (i--)
+ while (--i)
if (mf->code == noon010_formats[i].code)
break;
-
mf->code = noon010_formats[i].code;
return &noon010_formats[i];
}
-static int noon010_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- if (!sd || !mf)
- return -EINVAL;
-
- try_fmt(sd, mf);
- return 0;
-}
-
-static int noon010_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
+static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
{
struct noon010_info *info = to_noon010(sd);
+ const struct noon010_frmsize *size = NULL;
+ const struct noon010_format *nf;
+ struct v4l2_mbus_framefmt *mf;
+ int ret = 0;
- if (!sd || !mf)
- return -EINVAL;
-
- info->curr_fmt = try_fmt(sd, mf);
+ nf = noon010_try_fmt(sd, &fmt->format);
+ noon010_try_frame_size(&fmt->format, &size);
+ fmt->format.colorspace = V4L2_COLORSPACE_JPEG;
- return noon010_set_params(sd);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ *mf = fmt->format;
+ }
+ return 0;
+ }
+ mutex_lock(&info->lock);
+ if (!info->streaming) {
+ info->apply_new_cfg = 1;
+ info->curr_fmt = nf;
+ info->curr_win = size;
+ } else {
+ ret = -EBUSY;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
}
+/* Called with struct noon010_info.lock mutex held */
static int noon010_base_config(struct v4l2_subdev *sd)
{
- struct noon010_info *info = to_noon010(sd);
- int ret;
-
- ret = noon010_bulk_write_reg(sd, noon010_base_regs);
- if (!ret) {
- info->curr_fmt = &noon010_formats[0];
- info->curr_win = &noon010_sizes[0];
+ int ret = noon010_bulk_write_reg(sd, noon010_base_regs);
+ if (!ret)
ret = noon010_set_params(sd);
- }
if (!ret)
ret = noon010_set_flip(sd, 1, 0);
- if (!ret)
- ret = noon010_power_ctrl(sd, false, false);
- /* sync the handler and the registers state */
- v4l2_ctrl_handler_setup(&to_noon010(sd)->hdl);
return ret;
}
static int noon010_s_power(struct v4l2_subdev *sd, int on)
{
struct noon010_info *info = to_noon010(sd);
- const struct noon010pc30_platform_data *pdata = info->pdata;
- int ret = 0;
-
- if (WARN(pdata == NULL, "No platform data!\n"))
- return -ENOMEM;
+ int ret;
+ mutex_lock(&info->lock);
if (on) {
ret = power_enable(info);
- if (ret)
- return ret;
- ret = noon010_base_config(sd);
+ if (!ret)
+ ret = noon010_base_config(sd);
} else {
noon010_power_ctrl(sd, false, true);
ret = power_disable(info);
- info->curr_win = NULL;
- info->curr_fmt = NULL;
}
+ mutex_unlock(&info->lock);
+
+ /* Restore the controls state */
+ if (!ret && on)
+ ret = v4l2_ctrl_handler_setup(&info->hdl);
return ret;
}
-static int noon010_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
+static int noon010_s_stream(struct v4l2_subdev *sd, int on)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
- return v4l2_chip_ident_i2c_client(client, chip,
- V4L2_IDENT_NOON010PC30, 0);
+ mutex_lock(&info->lock);
+ if (!info->streaming != !on) {
+ ret = noon010_power_ctrl(sd, false, !on);
+ if (!ret)
+ info->streaming = on;
+ }
+ if (!ret && on && info->apply_new_cfg) {
+ ret = noon010_set_params(sd);
+ if (!ret)
+ info->apply_new_cfg = 0;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
}
static int noon010_log_status(struct v4l2_subdev *sd)
@@ -599,12 +637,27 @@ static int noon010_log_status(struct v4l2_subdev *sd)
return 0;
}
+static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+
+ mf->width = noon010_sizes[0].width;
+ mf->height = noon010_sizes[0].height;
+ mf->code = noon010_formats[0].code;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = {
+ .open = noon010_open,
+};
+
static const struct v4l2_ctrl_ops noon010_ctrl_ops = {
.s_ctrl = noon010_s_ctrl,
};
static const struct v4l2_subdev_core_ops noon010_core_ops = {
- .g_chip_ident = noon010_g_chip_ident,
.s_power = noon010_s_power,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
@@ -616,15 +669,19 @@ static const struct v4l2_subdev_core_ops noon010_core_ops = {
.log_status = noon010_log_status,
};
-static const struct v4l2_subdev_video_ops noon010_video_ops = {
- .g_mbus_fmt = noon010_g_fmt,
- .s_mbus_fmt = noon010_s_fmt,
- .try_mbus_fmt = noon010_try_fmt,
- .enum_mbus_fmt = noon010_enum_fmt,
+static struct v4l2_subdev_pad_ops noon010_pad_ops = {
+ .enum_mbus_code = noon010_enum_mbus_code,
+ .get_fmt = noon010_get_fmt,
+ .set_fmt = noon010_set_fmt,
+};
+
+static struct v4l2_subdev_video_ops noon010_video_ops = {
+ .s_stream = noon010_s_stream,
};
static const struct v4l2_subdev_ops noon010_ops = {
.core = &noon010_core_ops,
+ .pad = &noon010_pad_ops,
.video = &noon010_video_ops,
};
@@ -665,10 +722,14 @@ static int noon010_probe(struct i2c_client *client,
if (!info)
return -ENOMEM;
+ mutex_init(&info->lock);
sd = &info->sd;
strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
v4l2_i2c_subdev_init(sd, client, &noon010_ops);
+ sd->internal_ops = &noon010_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
v4l2_ctrl_handler_init(&info->hdl, 3);
v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
@@ -684,10 +745,11 @@ static int noon010_probe(struct i2c_client *client,
if (ret)
goto np_err;
- info->pdata = client->dev.platform_data;
info->i2c_reg_page = -1;
info->gpio_nreset = -EINVAL;
info->gpio_nstby = -EINVAL;
+ info->curr_fmt = &noon010_formats[0];
+ info->curr_win = &noon010_sizes[0];
if (gpio_is_valid(pdata->gpio_nreset)) {
ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST");
@@ -719,11 +781,17 @@ static int noon010_probe(struct i2c_client *client,
if (ret)
goto np_reg_err;
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto np_me_err;
+
ret = noon010_detect(client, info);
if (!ret)
return 0;
- /* the sensor detection failed */
+np_me_err:
regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
np_reg_err:
if (gpio_is_valid(info->gpio_nstby))
@@ -754,6 +822,7 @@ static int noon010_remove(struct i2c_client *client)
if (gpio_is_valid(info->gpio_nstby))
gpio_free(info->gpio_nstby);
+ media_entity_cleanup(&sd->entity);
kfree(info);
return 0;
}
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index b3a5ecdb33ac..9c5c19f142de 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -400,7 +400,6 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
ovl->get_overlay_info(ovl, &info);
info.paddr = addr;
- info.vaddr = NULL;
info.width = cropwidth;
info.height = cropheight;
info.color_mode = vout->dss_mode;
@@ -834,6 +833,15 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
/*
* File operations
*/
+static unsigned int omap_vout_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
static void omap_vout_vm_open(struct vm_area_struct *vma)
{
struct omap_vout_device *vout = vma->vm_private_data;
@@ -1165,12 +1173,17 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
{
int ret = 0;
struct omap_vout_device *vout = fh;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
struct v4l2_window *win = &f->fmt.win;
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
ret = omap_vout_try_window(&vout->fbuf, win);
if (!ret) {
- if (vout->vid == OMAP_VIDEO1)
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
win->global_alpha = 255;
else
win->global_alpha = f->fmt.win.global_alpha;
@@ -1194,8 +1207,8 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
if (!ret) {
- /* Video1 plane does not support global alpha */
- if (ovl->id == OMAP_DSS_VIDEO1)
+ /* Video1 plane does not support global alpha on OMAP3 */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
vout->win.global_alpha = 255;
else
vout->win.global_alpha = f->fmt.win.global_alpha;
@@ -1788,7 +1801,9 @@ static int vidioc_s_fbuf(struct file *file, void *fh,
if (ovl->manager && ovl->manager->get_manager_info &&
ovl->manager->set_manager_info) {
ovl->manager->get_manager_info(ovl->manager, &info);
- info.alpha_enabled = enable;
+ /* enable this only if there is no zorder cap */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+ info.partial_alpha_enabled = enable;
if (ovl->manager->set_manager_info(ovl->manager, &info))
return -EINVAL;
}
@@ -1820,7 +1835,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh,
}
if (ovl->manager && ovl->manager->get_manager_info) {
ovl->manager->get_manager_info(ovl->manager, &info);
- if (info.alpha_enabled)
+ if (info.partial_alpha_enabled)
a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
}
@@ -1855,6 +1870,7 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
static const struct v4l2_file_operations omap_vout_fops = {
.owner = THIS_MODULE,
+ .poll = omap_vout_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = omap_vout_mmap,
.open = omap_vout_open,
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index 8a947e603aca..e87ae2f634b2 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -102,10 +102,10 @@
/* end of OMAP1 Camera Interface registers */
-#define SOCAM_BUS_FLAGS (SOCAM_MASTER | \
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
@@ -1438,41 +1438,55 @@ static int omap1_cam_querycap(struct soc_camera_host *ici,
static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
struct soc_camera_host *ici = to_soc_camera_host(dev);
struct omap1_cam_dev *pcdev = ici->priv;
const struct soc_camera_format_xlate *xlate;
const struct soc_mbus_pixelfmt *fmt;
- unsigned long camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
u32 ctrlclock, mode;
int ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- SOCAM_BUS_FLAGS);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
/* Make choices, possibly based on platform configuration */
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
if (ctrlclock & LCLK_EN)
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
ctrlclock |= POLCLK;
} else {
@@ -1565,10 +1579,10 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
pcdev->clk = clk;
pcdev->pdata = pdev->dev.platform_data;
- pcdev->pflags = pcdev->pdata->flags;
-
- if (pcdev->pdata)
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
switch (pcdev->camexclk) {
case 6000000:
@@ -1578,6 +1592,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
case 24000000:
break;
default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
dev_warn(&pdev->dev,
"Incorrect sensor clock frequency %ld kHz, "
"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
@@ -1585,8 +1600,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
pcdev->pdata->camexclk_khz);
pcdev->camexclk = 0;
case 0:
- dev_info(&pdev->dev,
- "Not providing sensor clock\n");
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
}
INIT_LIST_HEAD(&pcdev->capture);
@@ -1716,5 +1730,5 @@ MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
MODULE_LICENSE("GPL v2");
-MODULE_LICENSE(DRIVER_VERSION);
+MODULE_VERSION(DRIVER_VERSION);
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile
index b1b344774ae7..e8847e79e31a 100644
--- a/drivers/media/video/omap3isp/Makefile
+++ b/drivers/media/video/omap3isp/Makefile
@@ -1,8 +1,6 @@
# Makefile for OMAP3 ISP driver
-ifdef CONFIG_VIDEO_OMAP3_DEBUG
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
omap3-isp-objs += \
isp.o ispqueue.o ispvideo.o \
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 5cea2bbd7014..b818cacf420f 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -80,6 +80,13 @@
#include "isph3a.h"
#include "isphist.h"
+/*
+ * this is provided as an interim solution until omap3isp doesn't need
+ * any omap-specific iommu API
+ */
+#define to_iommu(dev) \
+ (struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
+
static unsigned int autoidle;
module_param(autoidle, int, 0444);
MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
@@ -732,7 +739,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
struct media_pad *pad;
struct v4l2_subdev *subdev;
unsigned long flags;
- int ret = 0;
+ int ret;
spin_lock_irqsave(&pipe->lock, flags);
pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
@@ -756,7 +763,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
ret = v4l2_subdev_call(subdev, video, s_stream, mode);
if (ret < 0 && ret != -ENOIOCTLCMD)
- break;
+ return ret;
if (subdev == &isp->isp_ccdc.subdev) {
v4l2_subdev_call(&isp->isp_aewb.subdev, video,
@@ -777,7 +784,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT)
atomic_inc(&pipe->frame_number);
- return ret;
+ return 0;
}
static int isp_pipeline_wait_resizer(struct isp_device *isp)
@@ -1108,7 +1115,7 @@ static void isp_save_ctx(struct isp_device *isp)
{
isp_save_context(isp, isp_reg_list);
if (isp->iommu)
- iommu_save_ctx(isp->iommu);
+ omap_iommu_save_ctx(isp->iommu);
}
/*
@@ -1122,7 +1129,7 @@ static void isp_restore_ctx(struct isp_device *isp)
{
isp_restore_context(isp, isp_reg_list);
if (isp->iommu)
- iommu_restore_ctx(isp->iommu);
+ omap_iommu_restore_ctx(isp->iommu);
omap3isp_ccdc_restore_context(isp);
omap3isp_preview_restore_context(isp);
}
@@ -1697,6 +1704,7 @@ static int isp_register_entities(struct isp_device *isp)
isp->media_dev.dev = isp->dev;
strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
sizeof(isp->media_dev.model));
+ isp->media_dev.hw_revision = isp->revision;
isp->media_dev.link_notify = isp_pipeline_link_notify;
ret = media_device_register(&isp->media_dev);
if (ret < 0) {
@@ -1975,7 +1983,8 @@ static int isp_remove(struct platform_device *pdev)
isp_cleanup_modules(isp);
omap3isp_get(isp);
- iommu_put(isp->iommu);
+ iommu_detach_device(isp->domain, isp->iommu_dev);
+ iommu_domain_free(isp->domain);
omap3isp_put(isp);
free_irq(isp->irq_num, isp);
@@ -2123,25 +2132,41 @@ static int isp_probe(struct platform_device *pdev)
}
/* IOMMU */
- isp->iommu = iommu_get("isp");
- if (IS_ERR_OR_NULL(isp->iommu)) {
- isp->iommu = NULL;
+ isp->iommu_dev = omap_find_iommu_device("isp");
+ if (!isp->iommu_dev) {
+ dev_err(isp->dev, "omap_find_iommu_device failed\n");
ret = -ENODEV;
goto error_isp;
}
+ /* to be removed once iommu migration is complete */
+ isp->iommu = to_iommu(isp->iommu_dev);
+
+ isp->domain = iommu_domain_alloc(pdev->dev.bus);
+ if (!isp->domain) {
+ dev_err(isp->dev, "can't alloc iommu domain\n");
+ ret = -ENOMEM;
+ goto error_isp;
+ }
+
+ ret = iommu_attach_device(isp->domain, isp->iommu_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
/* Interrupt */
isp->irq_num = platform_get_irq(pdev, 0);
if (isp->irq_num <= 0) {
dev_err(isp->dev, "No IRQ resource\n");
ret = -ENODEV;
- goto error_isp;
+ goto detach_dev;
}
if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
dev_err(isp->dev, "Unable to request IRQ\n");
ret = -EINVAL;
- goto error_isp;
+ goto detach_dev;
}
/* Entities */
@@ -2162,8 +2187,11 @@ error_modules:
isp_cleanup_modules(isp);
error_irq:
free_irq(isp->irq_num, isp);
+detach_dev:
+ iommu_detach_device(isp->domain, isp->iommu_dev);
+free_domain:
+ iommu_domain_free(isp->domain);
error_isp:
- iommu_put(isp->iommu);
omap3isp_put(isp);
error:
isp_put_clocks(isp);
@@ -2183,6 +2211,8 @@ error:
regulator_put(isp->isp_csiphy2.vdd);
regulator_put(isp->isp_csiphy1.vdd);
platform_set_drvdata(pdev, NULL);
+
+ mutex_destroy(&isp->isp_mutex);
kfree(isp);
return ret;
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
index 529e582ef948..705946ef4d60 100644
--- a/drivers/media/video/omap3isp/isp.h
+++ b/drivers/media/video/omap3isp/isp.h
@@ -27,11 +27,13 @@
#ifndef OMAP3_ISP_CORE_H
#define OMAP3_ISP_CORE_H
+#include <media/omap3isp.h>
#include <media/v4l2-device.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
+#include <linux/iommu.h>
#include <plat/iommu.h>
#include <plat/iovmm.h>
@@ -94,14 +96,6 @@ enum isp_subclk_resource {
OMAP3_ISP_SUBCLK_RESIZER = (1 << 4),
};
-enum isp_interface_type {
- ISP_INTERFACE_PARALLEL,
- ISP_INTERFACE_CSI2A_PHY2,
- ISP_INTERFACE_CCP2B_PHY1,
- ISP_INTERFACE_CCP2B_PHY2,
- ISP_INTERFACE_CSI2C_PHY1,
-};
-
/* ISP: OMAP 34xx ES 1.0 */
#define ISP_REVISION_1_0 0x10
/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */
@@ -130,82 +124,6 @@ struct isp_reg {
u32 val;
};
-/**
- * struct isp_parallel_platform_data - Parallel interface platform data
- * @data_lane_shift: Data lane shifter
- * 0 - CAMEXT[13:0] -> CAM[13:0]
- * 1 - CAMEXT[13:2] -> CAM[11:0]
- * 2 - CAMEXT[13:4] -> CAM[9:0]
- * 3 - CAMEXT[13:6] -> CAM[7:0]
- * @clk_pol: Pixel clock polarity
- * 0 - Non Inverted, 1 - Inverted
- * @hs_pol: Horizontal synchronization polarity
- * 0 - Active high, 1 - Active low
- * @vs_pol: Vertical synchronization polarity
- * 0 - Active high, 1 - Active low
- * @bridge: CCDC Bridge input control
- * ISPCTRL_PAR_BRIDGE_DISABLE - Disable
- * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian
- * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian
- */
-struct isp_parallel_platform_data {
- unsigned int data_lane_shift:2;
- unsigned int clk_pol:1;
- unsigned int hs_pol:1;
- unsigned int vs_pol:1;
- unsigned int bridge:4;
-};
-
-/**
- * struct isp_ccp2_platform_data - CCP2 interface platform data
- * @strobe_clk_pol: Strobe/clock polarity
- * 0 - Non Inverted, 1 - Inverted
- * @crc: Enable the cyclic redundancy check
- * @ccp2_mode: Enable CCP2 compatibility mode
- * 0 - MIPI-CSI1 mode, 1 - CCP2 mode
- * @phy_layer: Physical layer selection
- * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer
- * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer
- * @vpclk_div: Video port output clock control
- */
-struct isp_ccp2_platform_data {
- unsigned int strobe_clk_pol:1;
- unsigned int crc:1;
- unsigned int ccp2_mode:1;
- unsigned int phy_layer:1;
- unsigned int vpclk_div:2;
-};
-
-/**
- * struct isp_csi2_platform_data - CSI2 interface platform data
- * @crc: Enable the cyclic redundancy check
- * @vpclk_div: Video port output clock control
- */
-struct isp_csi2_platform_data {
- unsigned crc:1;
- unsigned vpclk_div:2;
-};
-
-struct isp_subdev_i2c_board_info {
- struct i2c_board_info *board_info;
- int i2c_adapter_id;
-};
-
-struct isp_v4l2_subdevs_group {
- struct isp_subdev_i2c_board_info *subdevs;
- enum isp_interface_type interface;
- union {
- struct isp_parallel_platform_data parallel;
- struct isp_ccp2_platform_data ccp2;
- struct isp_csi2_platform_data csi2;
- } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
-};
-
-struct isp_platform_data {
- struct isp_v4l2_subdevs_group *subdevs;
- void (*set_constraints)(struct isp_device *isp, bool enable);
-};
-
struct isp_platform_callback {
u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
int (*csiphy_config)(struct isp_csiphy *phy,
@@ -294,7 +212,9 @@ struct isp_device {
unsigned int sbl_resources;
unsigned int subclk_resources;
- struct iommu *iommu;
+ struct omap_iommu *iommu;
+ struct iommu_domain *domain;
+ struct device *iommu_dev;
struct isp_platform_callback platform_cb;
};
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 80796eb0c53e..b0b0fa5a3572 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -366,7 +366,7 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
req->iovm->sgt->nents, DMA_TO_DEVICE);
if (req->table)
- iommu_vfree(isp->iommu, req->table);
+ omap_iommu_vfree(isp->domain, isp->iommu, req->table);
kfree(req);
}
@@ -438,15 +438,15 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
req->enable = 1;
- req->table = iommu_vmalloc(isp->iommu, 0, req->config.size,
- IOMMU_FLAG);
+ req->table = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
+ req->config.size, IOMMU_FLAG);
if (IS_ERR_VALUE(req->table)) {
req->table = 0;
ret = -ENOMEM;
goto done;
}
- req->iovm = find_iovm_area(isp->iommu, req->table);
+ req->iovm = omap_find_iovm_area(isp->iommu, req->table);
if (req->iovm == NULL) {
ret = -ENOMEM;
goto done;
@@ -462,7 +462,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
req->iovm->sgt->nents, DMA_TO_DEVICE);
- table = da_to_va(isp->iommu, req->table);
+ table = omap_da_to_va(isp->iommu, req->table);
if (copy_from_user(table, config->lsc, req->config.size)) {
ret = -EFAULT;
goto done;
@@ -731,18 +731,19 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
/*
* table_new must be 64-bytes aligned, but it's
- * already done by iommu_vmalloc().
+ * already done by omap_iommu_vmalloc().
*/
size = ccdc->fpc.fpnum * 4;
- table_new = iommu_vmalloc(isp->iommu, 0, size,
- IOMMU_FLAG);
+ table_new = omap_iommu_vmalloc(isp->domain, isp->iommu,
+ 0, size, IOMMU_FLAG);
if (IS_ERR_VALUE(table_new))
return -ENOMEM;
- if (copy_from_user(da_to_va(isp->iommu, table_new),
+ if (copy_from_user(omap_da_to_va(isp->iommu, table_new),
(__force void __user *)
ccdc->fpc.fpcaddr, size)) {
- iommu_vfree(isp->iommu, table_new);
+ omap_iommu_vfree(isp->domain, isp->iommu,
+ table_new);
return -EFAULT;
}
@@ -752,7 +753,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
ccdc_configure_fpc(ccdc);
if (table_old != 0)
- iommu_vfree(isp->iommu, table_old);
+ omap_iommu_vfree(isp->domain, isp->iommu, table_old);
}
return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -1405,11 +1406,14 @@ static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
{
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccdc->video_out.video.entity);
struct video_device *vdev = &ccdc->subdev.devnode;
struct v4l2_event event;
memset(&event, 0, sizeof(event));
- event.type = V4L2_EVENT_OMAP3ISP_HS_VS;
+ event.type = V4L2_EVENT_FRAME_SYNC;
+ event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
v4l2_event_queue(vdev, &event);
}
@@ -1691,7 +1695,11 @@ static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
- if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS)
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* line number is zero at frame start */
+ if (sub->id != 0)
return -EINVAL;
return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
@@ -1828,7 +1836,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
* callers to request an output size bigger than the input size
* up to the nearest multiple of 16.
*/
- fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
+ fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
fmt->width &= ~15;
fmt->height = clamp_t(u32, height, 32, fmt->height);
break;
@@ -2144,6 +2152,37 @@ static const struct media_entity_operations ccdc_media_ops = {
.link_setup = ccdc_link_setup,
};
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
+{
+ v4l2_device_unregister_subdev(&ccdc->subdev);
+ omap3isp_video_unregister(&ccdc->video_out);
+}
+
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccdc->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccdc_unregister_entities(ccdc);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CCDC initialisation and cleanup
+ */
+
/*
* ccdc_init_entities - Initialize V4L2 subdev and media entity
* @ccdc: ISP CCDC module
@@ -2185,50 +2224,23 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the CCDC subdev to the video node. */
ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
&ccdc->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
-
- return 0;
-}
-
-void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
-{
- media_entity_cleanup(&ccdc->subdev.entity);
-
- v4l2_device_unregister_subdev(&ccdc->subdev);
- omap3isp_video_unregister(&ccdc->video_out);
-}
-
-int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video node. */
- ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&ccdc->video_out, vdev);
- if (ret < 0)
- goto error;
+ goto error_link;
return 0;
-error:
- omap3isp_ccdc_unregister_entities(ccdc);
+error_link:
+ omap3isp_video_cleanup(&ccdc->video_out);
+error_video:
+ media_entity_cleanup(me);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP CCDC initialisation and cleanup
- */
-
/*
* omap3isp_ccdc_init - CCDC module initialization.
* @dev: Device pointer specific to the OMAP3 ISP.
@@ -2240,6 +2252,7 @@ error:
int omap3isp_ccdc_init(struct isp_device *isp)
{
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ int ret;
spin_lock_init(&ccdc->lock);
init_waitqueue_head(&ccdc->wait);
@@ -2268,7 +2281,13 @@ int omap3isp_ccdc_init(struct isp_device *isp)
ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
ccdc_apply_controls(ccdc);
- return ccdc_init_entities(ccdc);
+ ret = ccdc_init_entities(ccdc);
+ if (ret < 0) {
+ mutex_destroy(&ccdc->ioctl_lock);
+ return ret;
+ }
+
+ return 0;
}
/*
@@ -2279,6 +2298,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
{
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ omap3isp_video_cleanup(&ccdc->video_out);
+ media_entity_cleanup(&ccdc->subdev.entity);
+
/* Free LSC requests. As the CCDC is stopped there's no active request,
* so only the pending request and the free queue need to be handled.
*/
@@ -2287,5 +2309,7 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
if (ccdc->fpc.fpcaddr != 0)
- iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr);
+ omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr);
+
+ mutex_destroy(&ccdc->ioctl_lock);
}
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
index ec9e395f3339..904ca8c8b17f 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -243,9 +243,9 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
if (!(val & ISPCCP2_CTRL_MODE)) {
- if (pdata->ccp2_mode)
+ if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2)
dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n");
- if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE)
+ if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
/* Strobe mode requires CCP2 */
return -EIO;
}
@@ -1032,6 +1032,48 @@ static const struct media_entity_operations ccp2_media_ops = {
};
/*
+ * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
+ * @ccp2: Pointer to ISP CCP2 device
+ */
+void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
+{
+ v4l2_device_unregister_subdev(&ccp2->subdev);
+ omap3isp_video_unregister(&ccp2->video_in);
+}
+
+/*
+ * omap3isp_ccp2_register_entities - Register the subdev media entity
+ * @ccp2: Pointer to ISP CCP2 device
+ * @vdev: Pointer to v4l device
+ * return negative error code or zero on success
+ */
+
+int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccp2->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccp2_unregister_entities(ccp2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP ccp2 initialisation and cleanup
+ */
+
+/*
* ccp2_init_entities - Initialize ccp2 subdev and media entity.
* @ccp2: Pointer to ISP CCP2 device
* return negative error code or zero on success
@@ -1083,72 +1125,23 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the video node to the ccp2 subdev. */
ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
&ccp2->subdev.entity, CCP2_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-/*
- * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
- * @ccp2: Pointer to ISP CCP2 device
- */
-void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
-{
+error_link:
+ omap3isp_video_cleanup(&ccp2->video_in);
+error_video:
media_entity_cleanup(&ccp2->subdev.entity);
-
- v4l2_device_unregister_subdev(&ccp2->subdev);
- omap3isp_video_unregister(&ccp2->video_in);
-}
-
-/*
- * omap3isp_ccp2_register_entities - Register the subdev media entity
- * @ccp2: Pointer to ISP CCP2 device
- * @vdev: Pointer to v4l device
- * return negative error code or zero on success
- */
-
-int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&ccp2->video_in, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_ccp2_unregister_entities(ccp2);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP ccp2 initialisation and cleanup
- */
-
-/*
- * omap3isp_ccp2_cleanup - CCP2 un-initialization
- * @isp : Pointer to ISP device
- */
-void omap3isp_ccp2_cleanup(struct isp_device *isp)
-{
- struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
-
- regulator_put(ccp2->vdds_csib);
-}
-
/*
* omap3isp_ccp2_init - CCP2 initialization.
* @isp : Pointer to ISP device
@@ -1184,13 +1177,25 @@ int omap3isp_ccp2_init(struct isp_device *isp)
}
ret = ccp2_init_entities(ccp2);
- if (ret < 0)
- goto out;
+ if (ret < 0) {
+ regulator_put(ccp2->vdds_csib);
+ return ret;
+ }
ccp2_reset(ccp2);
-out:
- if (ret)
- omap3isp_ccp2_cleanup(isp);
+ return 0;
+}
- return ret;
+/*
+ * omap3isp_ccp2_cleanup - CCP2 un-initialization
+ * @isp : Pointer to ISP device
+ */
+void omap3isp_ccp2_cleanup(struct isp_device *isp)
+{
+ struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+
+ omap3isp_video_cleanup(&ccp2->video_in);
+ media_entity_cleanup(&ccp2->subdev.entity);
+
+ regulator_put(ccp2->vdds_csib);
}
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
index 69161a682b3d..0c5f1cb9d99d 100644
--- a/drivers/media/video/omap3isp/ispcsi2.c
+++ b/drivers/media/video/omap3isp/ispcsi2.c
@@ -1187,6 +1187,37 @@ static const struct media_entity_operations csi2_media_ops = {
.link_setup = csi2_link_setup,
};
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->subdev);
+ omap3isp_video_unregister(&csi2->video_out);
+}
+
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&csi2->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_csi2_unregister_entities(csi2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CSI2 initialisation and cleanup
+ */
+
/*
* csi2_init_entities - Initialize subdev and media entity.
* @csi2: Pointer to csi2 structure.
@@ -1228,57 +1259,23 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
if (ret < 0)
- return ret;
+ goto error_video;
/* Connect the CSI2 subdev to the video node. */
ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
&csi2->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
-{
+error_link:
+ omap3isp_video_cleanup(&csi2->video_out);
+error_video:
media_entity_cleanup(&csi2->subdev.entity);
-
- v4l2_device_unregister_subdev(&csi2->subdev);
- omap3isp_video_unregister(&csi2->video_out);
-}
-
-int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&csi2->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_csi2_unregister_entities(csi2);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP CSI2 initialisation and cleanup
- */
-
-/*
- * omap3isp_csi2_cleanup - Routine for module driver cleanup
- */
-void omap3isp_csi2_cleanup(struct isp_device *isp)
-{
-}
-
/*
* omap3isp_csi2_init - Routine for module driver init
*/
@@ -1298,7 +1295,7 @@ int omap3isp_csi2_init(struct isp_device *isp)
ret = csi2_init_entities(csi2a);
if (ret < 0)
- goto fail;
+ return ret;
if (isp->revision == ISP_REVISION_15_0) {
csi2c->isp = isp;
@@ -1311,7 +1308,15 @@ int omap3isp_csi2_init(struct isp_device *isp)
}
return 0;
-fail:
- omap3isp_csi2_cleanup(isp);
- return ret;
+}
+
+/*
+ * omap3isp_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap3isp_csi2_cleanup(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+
+ omap3isp_video_cleanup(&csi2a->video_out);
+ media_entity_cleanup(&csi2a->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c
index 8068cefd8d89..a3c76bf18175 100644
--- a/drivers/media/video/omap3isp/isph3a_aewb.c
+++ b/drivers/media/video/omap3isp/isph3a_aewb.c
@@ -370,5 +370,5 @@ void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
{
kfree(isp->isp_aewb.priv);
kfree(isp->isp_aewb.recover_priv);
- omap3isp_stat_free(&isp->isp_aewb);
+ omap3isp_stat_cleanup(&isp->isp_aewb);
}
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c
index ba54d0acdecf..58e0bc414899 100644
--- a/drivers/media/video/omap3isp/isph3a_af.c
+++ b/drivers/media/video/omap3isp/isph3a_af.c
@@ -425,5 +425,5 @@ void omap3isp_h3a_af_cleanup(struct isp_device *isp)
{
kfree(isp->isp_af.priv);
kfree(isp->isp_af.recover_priv);
- omap3isp_stat_free(&isp->isp_af);
+ omap3isp_stat_cleanup(&isp->isp_af);
}
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c
index 1743856b30d1..1163907bcddc 100644
--- a/drivers/media/video/omap3isp/isphist.c
+++ b/drivers/media/video/omap3isp/isphist.c
@@ -516,5 +516,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp)
if (HIST_USING_DMA(&isp->isp_hist))
omap_free_dma(isp->isp_hist.dma_ch);
kfree(isp->isp_hist.priv);
- omap3isp_stat_free(&isp->isp_hist);
+ omap3isp_stat_cleanup(&isp->isp_hist);
}
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index aba537af87e4..ccb876fe023f 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -76,9 +76,51 @@ static struct omap3isp_prev_csc flr_prev_csc = {
#define DEF_DETECT_CORRECT_VAL 0xe
-#define PREV_MIN_WIDTH 64
-#define PREV_MIN_HEIGHT 8
-#define PREV_MAX_HEIGHT 16384
+/*
+ * Margins and image size limits.
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which filters are enabled. To avoid format changes when the filters are
+ * enabled or disabled (which would prevent them from being turned on or off
+ * during streaming), the driver assumes all the filters are enabled when
+ * computing sink crop and source format limits.
+ *
+ * If a filter is disabled, additional cropping is automatically added at the
+ * preview engine input by the driver to avoid overflow at line and frame end.
+ * This is completely transparent for applications.
+ *
+ * Median filter 4 pixels
+ * Noise filter,
+ * Faulty pixels correction 4 pixels, 4 lines
+ * CFA filter 4 pixels, 4 lines in Bayer mode
+ * 2 lines in other modes
+ * Color suppression 2 pixels
+ * or luma enhancement
+ * -------------------------------------------------------------
+ * Maximum total 14 pixels, 8 lines
+ *
+ * The color suppression and luma enhancement filters are applied after bayer to
+ * YUV conversion. They thus can crop one pixel on the left and one pixel on the
+ * right side of the image without changing the color pattern. When both those
+ * filters are disabled, the driver must crop the two pixels on the same side of
+ * the image to avoid changing the bayer pattern. The left margin is thus set to
+ * 8 pixels and the right margin to 6 pixels.
+ */
+
+#define PREV_MARGIN_LEFT 8
+#define PREV_MARGIN_RIGHT 6
+#define PREV_MARGIN_TOP 4
+#define PREV_MARGIN_BOTTOM 4
+
+#define PREV_MIN_IN_WIDTH 64
+#define PREV_MIN_IN_HEIGHT 8
+#define PREV_MAX_IN_HEIGHT 16384
+
+#define PREV_MIN_OUT_WIDTH 0
+#define PREV_MIN_OUT_HEIGHT 0
+#define PREV_MAX_OUT_WIDTH 1280
+#define PREV_MAX_OUT_WIDTH_ES2 3300
+#define PREV_MAX_OUT_WIDTH_3630 4096
/*
* Coeficient Tables for the submodules in Preview.
@@ -979,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
* enabled when reporting source pad formats to userspace. If this assumption is
* not true, rows and columns must be manually cropped at the preview engine
* input to avoid overflows at the end of lines and frames.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
*/
static void preview_config_input_size(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
struct prev_params *params = &prev->params;
- struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
- unsigned int sph = 0;
- unsigned int eph = format->width - 1;
- unsigned int slv = 0;
- unsigned int elv = format->height - 1;
-
- if (prev->input == PREVIEW_INPUT_CCDC) {
- sph += 2;
- eph -= 2;
+ unsigned int sph = prev->crop.left;
+ unsigned int eph = prev->crop.left + prev->crop.width - 1;
+ unsigned int slv = prev->crop.top;
+ unsigned int elv = prev->crop.top + prev->crop.height - 1;
+
+ if (params->features & PREV_CFA) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
}
-
- /*
- * Median filter 4 pixels
- * Noise filter 4 pixels, 4 lines
- * or faulty pixels correction
- * CFA filter 4 pixels, 4 lines in Bayer mode
- * 2 lines in other modes
- * Color suppression 2 pixels
- * or luma enhancement
- * -------------------------------------------------------------
- * Maximum total 14 pixels, 8 lines
- */
-
- if (!(params->features & PREV_CFA)) {
- sph += 2;
- eph -= 2;
- slv += 2;
- elv -= 2;
+ if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
}
- if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) {
- sph += 2;
- eph -= 2;
- slv += 2;
- elv -= 2;
+ if (params->features & PREV_HORZ_MEDIAN_FILTER) {
+ sph -= 2;
+ eph += 2;
}
- if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) {
- sph += 2;
- eph -= 2;
- }
- if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)))
- sph += 2;
+ if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
+ sph -= 2;
isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
@@ -1228,7 +1254,6 @@ static void preview_init_params(struct isp_prev_device *prev)
/* Init values */
params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
- params->average = NO_AVE;
params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
memcpy(params->cfa.table, cfa_coef_table,
sizeof(params->cfa.table));
@@ -1281,14 +1306,14 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev)
switch (isp->revision) {
case ISP_REVISION_1_0:
- return ISPPRV_MAXOUTPUT_WIDTH;
+ return PREV_MAX_OUT_WIDTH;
case ISP_REVISION_2_0:
default:
- return ISPPRV_MAXOUTPUT_WIDTH_ES2;
+ return PREV_MAX_OUT_WIDTH_ES2;
case ISP_REVISION_15_0:
- return ISPPRV_MAXOUTPUT_WIDTH_3630;
+ return PREV_MAX_OUT_WIDTH_3630;
}
}
@@ -1296,8 +1321,6 @@ static void preview_configure(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
struct v4l2_mbus_framefmt *format;
- unsigned int max_out_width;
- unsigned int format_avg;
preview_setup_hw(prev);
@@ -1335,10 +1358,7 @@ static void preview_configure(struct isp_prev_device *prev)
preview_config_outlineoffset(prev,
ALIGN(format->width, 0x10) * 2);
- max_out_width = preview_max_out_width(prev);
-
- format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1);
- preview_config_averager(prev, format_avg);
+ preview_config_averager(prev, 0);
preview_config_ycpos(prev, format->code);
}
@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
return &prev->formats[pad];
}
+static struct v4l2_rect *
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+ else
+ return &prev->crop;
+}
+
/* previewer format descriptions */
static const unsigned int preview_input_fmts[] = {
V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -1611,24 +1641,25 @@ static const unsigned int preview_output_fmts[] = {
};
/*
- * preview_try_format - Handle try format by pad subdev method
- * @prev: ISP preview device
- * @fh : V4L2 subdev file handle
- * @pad: pad num
- * @fmt: pointer to v4l2 format structure
+ * preview_try_format - Validate a format
+ * @prev: ISP preview engine
+ * @fh: V4L2 subdev file handle
+ * @pad: pad number
+ * @fmt: format to be validated
+ * @which: try/active format selector
+ *
+ * Validate and adjust the given format for the given pad based on the preview
+ * engine limits and the format and crop rectangles on other pads.
*/
static void preview_try_format(struct isp_prev_device *prev,
struct v4l2_subdev_fh *fh, unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
- struct v4l2_mbus_framefmt *format;
- unsigned int max_out_width;
enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_rect *crop;
unsigned int i;
- max_out_width = preview_max_out_width(prev);
-
switch (pad) {
case PREV_PAD_SINK:
/* When reading data from the CCDC, the input size has already
@@ -1641,10 +1672,11 @@ static void preview_try_format(struct isp_prev_device *prev,
* filter array interpolation.
*/
if (prev->input == PREVIEW_INPUT_MEMORY) {
- fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH,
- max_out_width * 8);
- fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT,
- PREV_MAX_HEIGHT);
+ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+ preview_max_out_width(prev));
+ fmt->height = clamp_t(u32, fmt->height,
+ PREV_MIN_IN_HEIGHT,
+ PREV_MAX_IN_HEIGHT);
}
fmt->colorspace = V4L2_COLORSPACE_SRGB;
@@ -1661,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,
case PREV_PAD_SOURCE:
pixelcode = fmt->code;
- format = __preview_get_format(prev, fh, PREV_PAD_SINK, which);
- memcpy(fmt, format, sizeof(*fmt));
+ *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
- /* The preview module output size is configurable through the
- * input interface (horizontal and vertical cropping) and the
- * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In
- * spite of this, hardcode the output size to the biggest
- * possible value for simplicity reasons.
- */
switch (pixelcode) {
case V4L2_MBUS_FMT_YUYV8_1X16:
case V4L2_MBUS_FMT_UYVY8_1X16:
@@ -1681,31 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
break;
}
- /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped
- * from the left and right sides when the input source is the
- * CCDC. This seems not to be needed in practice, investigation
- * is required.
- */
- if (prev->input == PREVIEW_INPUT_CCDC)
- fmt->width -= 4;
-
- /* The preview module can output a maximum of 3312 pixels
- * horizontally due to fixed memory-line sizes. Compute the
- * horizontal averaging factor accordingly. Note that the limit
- * applies to the noise filter and CFA interpolation blocks, so
- * it doesn't take cropping by further blocks into account.
- *
- * ES 1.0 hardware revision is limited to 1280 pixels
- * horizontally.
- */
- fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1);
-
- /* Assume that all blocks are enabled and crop pixels and lines
- * accordingly. See preview_config_input_size() for more
- * information.
+ /* The preview module output size is configurable through the
+ * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
+ * is not supported yet, hardcode the output size to the crop
+ * rectangle size.
*/
- fmt->width -= 14;
- fmt->height -= 8;
+ crop = __preview_get_crop(prev, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
fmt->colorspace = V4L2_COLORSPACE_JPEG;
break;
@@ -1715,6 +1723,49 @@ static void preview_try_format(struct isp_prev_device *prev,
}
/*
+ * preview_try_crop - Validate a crop rectangle
+ * @prev: ISP preview engine
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ *
+ * The preview engine crops lines and columns for its internal operation,
+ * depending on which filters are enabled. Enforce minimum crop margins to
+ * handle that transparently for userspace.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_try_crop(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ unsigned int left = PREV_MARGIN_LEFT;
+ unsigned int right = sink->width - PREV_MARGIN_RIGHT;
+ unsigned int top = PREV_MARGIN_TOP;
+ unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
+
+ /* When processing data on-the-fly from the CCDC, at least 2 pixels must
+ * be cropped from the left and right sides of the image. As we don't
+ * know which filters will be enabled, increase the left and right
+ * margins by two.
+ */
+ if (prev->input == PREVIEW_INPUT_CCDC) {
+ left += 2;
+ right -= 2;
+ }
+
+ /* Restrict left/top to even values to keep the Bayer pattern. */
+ crop->left &= ~1;
+ crop->top &= ~1;
+
+ crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
+ crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
+ crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
+ right - crop->left);
+ crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
+ bottom - crop->top);
+}
+
+/*
* preview_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
* @fh : V4L2 subdev file handle
@@ -1776,6 +1827,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
}
/*
+ * preview_get_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ crop->rect = *__preview_get_crop(prev, fh, crop->which);
+ return 0;
+}
+
+/*
+ * preview_set_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
+ preview_try_crop(prev, format, &crop->rect);
+ *__preview_get_crop(prev, fh, crop->which) = crop->rect;
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
+
+ return 0;
+}
+
+/*
* preview_get_format - Handle get format by pads subdev method
* @sd : pointer to v4l2 subdev structure
* @fh : V4L2 subdev file handle
@@ -1808,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
if (format == NULL)
@@ -1818,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
/* Propagate the format from sink to source */
if (fmt->pad == PREV_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __preview_get_crop(prev, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ preview_try_crop(prev, &fmt->format, crop);
+
+ /* Update the source format. */
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
fmt->which);
- *format = fmt->format;
preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
fmt->which);
}
@@ -1869,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
.enum_frame_size = preview_enum_frame_size,
.get_fmt = preview_get_format,
.set_fmt = preview_set_format,
+ .get_crop = preview_get_crop,
+ .set_crop = preview_set_crop,
};
/* subdev operations */
@@ -1966,8 +2083,44 @@ static const struct media_entity_operations preview_media_ops = {
.link_setup = preview_link_setup,
};
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
+{
+ v4l2_device_unregister_subdev(&prev->subdev);
+ omap3isp_video_unregister(&prev->video_in);
+ omap3isp_video_unregister(&prev->video_out);
+}
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prev,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &prev->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_preview_unregister_entities(prev);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP previewer initialisation and cleanup
+ */
+
/*
- * review_init_entities - Initialize subdev and media entity.
+ * preview_init_entities - Initialize subdev and media entity.
* @prev : Pointer to preview structure
* return -ENOMEM or zero on success
*/
@@ -2024,69 +2177,34 @@ static int preview_init_entities(struct isp_prev_device *prev)
ret = omap3isp_video_init(&prev->video_in, "preview");
if (ret < 0)
- return ret;
+ goto error_video_in;
ret = omap3isp_video_init(&prev->video_out, "preview");
if (ret < 0)
- return ret;
+ goto error_video_out;
/* Connect the video nodes to the previewer subdev. */
ret = media_entity_create_link(&prev->video_in.video.entity, 0,
&prev->subdev.entity, PREV_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
&prev->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
-{
+error_link:
+ omap3isp_video_cleanup(&prev->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&prev->video_in);
+error_video_in:
media_entity_cleanup(&prev->subdev.entity);
-
- v4l2_device_unregister_subdev(&prev->subdev);
- v4l2_ctrl_handler_free(&prev->ctrls);
- omap3isp_video_unregister(&prev->video_in);
- omap3isp_video_unregister(&prev->video_out);
-}
-
-int omap3isp_preview_register_entities(struct isp_prev_device *prev,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &prev->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_preview_unregister_entities(prev);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP previewer initialisation and cleanup
- */
-
-void omap3isp_preview_cleanup(struct isp_device *isp)
-{
-}
-
/*
* isp_preview_init - Previewer initialization.
* @dev : Pointer to ISP device
@@ -2095,19 +2213,20 @@ void omap3isp_preview_cleanup(struct isp_device *isp)
int omap3isp_preview_init(struct isp_device *isp)
{
struct isp_prev_device *prev = &isp->isp_prev;
- int ret;
spin_lock_init(&prev->lock);
init_waitqueue_head(&prev->wait);
preview_init_params(prev);
- ret = preview_init_entities(prev);
- if (ret < 0)
- goto out;
+ return preview_init_entities(prev);
+}
-out:
- if (ret)
- omap3isp_preview_cleanup(isp);
+void omap3isp_preview_cleanup(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
- return ret;
+ v4l2_ctrl_handler_free(&prev->ctrls);
+ omap3isp_video_cleanup(&prev->video_in);
+ omap3isp_video_cleanup(&prev->video_out);
+ media_entity_cleanup(&prev->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
index fa943bd05c7f..f54e775c2df4 100644
--- a/drivers/media/video/omap3isp/isppreview.h
+++ b/drivers/media/video/omap3isp/isppreview.h
@@ -45,11 +45,6 @@
#define ISPPRV_CONTRAST_HIGH 0xFF
#define ISPPRV_CONTRAST_UNITS 0x1
-#define NO_AVE 0x0
-#define AVE_2_PIX 0x1
-#define AVE_4_PIX 0x2
-#define AVE_8_PIX 0x3
-
/* Features list */
#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH
#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW
@@ -106,7 +101,6 @@ enum preview_ycpos_mode {
* @rgb2ycbcr: RGB to ycbcr parameters.
* @hmed: Horizontal median filter.
* @yclimit: YC limits parameters.
- * @average: Downsampling rate for averager.
* @contrast: Contrast.
* @brightness: Brightness.
*/
@@ -124,7 +118,6 @@ struct prev_params {
struct omap3isp_prev_csc rgb2ycbcr;
struct omap3isp_prev_hmed hmed;
struct omap3isp_prev_yclimit yclimit;
- u8 average;
u8 contrast;
u8 brightness;
};
@@ -159,6 +152,7 @@ struct isptables_update {
* @subdev: V4L2 subdevice
* @pads: Media entity pads
* @formats: Active formats at the subdev pad
+ * @crop: Active crop rectangle
* @input: Module currently connected to the input pad
* @output: Bitmask of the active output
* @video_in: Input video entity
@@ -177,6 +171,7 @@ struct isp_prev_device {
struct v4l2_subdev subdev;
struct media_pad pads[PREV_PADS_NUM];
struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
+ struct v4l2_rect crop;
struct v4l2_ctrl_handler ctrls;
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c
index 9c317148205f..9bebb1e57aab 100644
--- a/drivers/media/video/omap3isp/ispqueue.c
+++ b/drivers/media/video/omap3isp/ispqueue.c
@@ -868,6 +868,10 @@ int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
goto done;
if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->length < buf->vbuf.length)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
vbuf->m.userptr != buf->vbuf.m.userptr) {
isp_video_buffer_cleanup(buf);
buf->vbuf.m.userptr = vbuf->m.userptr;
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h
index 69f6af6f6b9c..084ea77d65a7 100644
--- a/drivers/media/video/omap3isp/ispreg.h
+++ b/drivers/media/video/omap3isp/ispreg.h
@@ -402,9 +402,6 @@
#define ISPPRV_YENH_TABLE_ADDR 0x1000
#define ISPPRV_CFA_TABLE_ADDR 0x1400
-#define ISPPRV_MAXOUTPUT_WIDTH 1280
-#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300
-#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096
#define ISPRSZ_MIN_OUTPUT 64
#define ISPRSZ_MAX_OUTPUT 3312
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 0bb0f8cd36f5..50e593bfcfaf 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -1608,6 +1608,42 @@ static const struct media_entity_operations resizer_media_ops = {
.link_setup = resizer_link_setup,
};
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
+{
+ v4l2_device_unregister_subdev(&res->subdev);
+ omap3isp_video_unregister(&res->video_in);
+ omap3isp_video_unregister(&res->video_out);
+}
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &res->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_resizer_unregister_entities(res);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP resizer initialization and cleanup
+ */
+
/*
* resizer_init_entities - Initialize resizer subdev and media entity.
* @res : Pointer to resizer device structure
@@ -1652,68 +1688,34 @@ static int resizer_init_entities(struct isp_res_device *res)
ret = omap3isp_video_init(&res->video_in, "resizer");
if (ret < 0)
- return ret;
+ goto error_video_in;
ret = omap3isp_video_init(&res->video_out, "resizer");
if (ret < 0)
- return ret;
+ goto error_video_out;
/* Connect the video nodes to the resizer subdev. */
ret = media_entity_create_link(&res->video_in.video.entity, 0,
&res->subdev.entity, RESZ_PAD_SINK, 0);
if (ret < 0)
- return ret;
+ goto error_link;
ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
&res->video_out.video.entity, 0, 0);
if (ret < 0)
- return ret;
+ goto error_link;
return 0;
-}
-void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
-{
+error_link:
+ omap3isp_video_cleanup(&res->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&res->video_in);
+error_video_in:
media_entity_cleanup(&res->subdev.entity);
-
- v4l2_device_unregister_subdev(&res->subdev);
- omap3isp_video_unregister(&res->video_in);
- omap3isp_video_unregister(&res->video_out);
-}
-
-int omap3isp_resizer_register_entities(struct isp_res_device *res,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &res->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_resizer_unregister_entities(res);
return ret;
}
-/* -----------------------------------------------------------------------------
- * ISP resizer initialization and cleanup
- */
-
-void omap3isp_resizer_cleanup(struct isp_device *isp)
-{
-}
-
/*
* isp_resizer_init - Resizer initialization.
* @isp : Pointer to ISP device
@@ -1722,17 +1724,17 @@ void omap3isp_resizer_cleanup(struct isp_device *isp)
int omap3isp_resizer_init(struct isp_device *isp)
{
struct isp_res_device *res = &isp->isp_res;
- int ret;
init_waitqueue_head(&res->wait);
atomic_set(&res->stopping, 0);
- ret = resizer_init_entities(res);
- if (ret < 0)
- goto out;
+ return resizer_init_entities(res);
+}
-out:
- if (ret)
- omap3isp_resizer_cleanup(isp);
+void omap3isp_resizer_cleanup(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
- return ret;
+ omap3isp_video_cleanup(&res->video_in);
+ omap3isp_video_cleanup(&res->video_out);
+ media_entity_cleanup(&res->subdev.entity);
}
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
index 808065948ac1..68d539456c55 100644
--- a/drivers/media/video/omap3isp/ispstat.c
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -366,7 +366,8 @@ static void isp_stat_bufs_free(struct ispstat *stat)
dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
buf->iovm->sgt->nents,
DMA_FROM_DEVICE);
- iommu_vfree(isp->iommu, buf->iommu_addr);
+ omap_iommu_vfree(isp->domain, isp->iommu,
+ buf->iommu_addr);
} else {
if (!buf->virt_addr)
continue;
@@ -399,8 +400,8 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
struct iovm_struct *iovm;
WARN_ON(buf->dma_addr);
- buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size,
- IOMMU_FLAG);
+ buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
+ size, IOMMU_FLAG);
if (IS_ERR((void *)buf->iommu_addr)) {
dev_err(stat->isp->dev,
"%s: Can't acquire memory for "
@@ -409,7 +410,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
return -ENOMEM;
}
- iovm = find_iovm_area(isp->iommu, buf->iommu_addr);
+ iovm = omap_find_iovm_area(isp->iommu, buf->iommu_addr);
if (!iovm ||
!dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
DMA_FROM_DEVICE)) {
@@ -418,7 +419,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
}
buf->iovm = iovm;
- buf->virt_addr = da_to_va(stat->isp->iommu,
+ buf->virt_addr = omap_da_to_va(stat->isp->iommu,
(u32)buf->iommu_addr);
buf->empty = 1;
dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
@@ -1022,24 +1023,6 @@ void omap3isp_stat_dma_isr(struct ispstat *stat)
__stat_isr(stat, 1);
}
-static int isp_stat_init_entities(struct ispstat *stat, const char *name,
- const struct v4l2_subdev_ops *sd_ops)
-{
- struct v4l2_subdev *subdev = &stat->subdev;
- struct media_entity *me = &subdev->entity;
-
- v4l2_subdev_init(subdev, sd_ops);
- snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
- subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
- subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- v4l2_set_subdevdata(subdev, stat);
-
- stat->pad.flags = MEDIA_PAD_FL_SINK;
- me->ops = NULL;
-
- return media_entity_init(me, 1, &stat->pad, 0);
-}
-
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
@@ -1061,7 +1044,6 @@ int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
void omap3isp_stat_unregister_entities(struct ispstat *stat)
{
- media_entity_cleanup(&stat->subdev.entity);
v4l2_device_unregister_subdev(&stat->subdev);
}
@@ -1071,21 +1053,50 @@ int omap3isp_stat_register_entities(struct ispstat *stat,
return v4l2_device_register_subdev(vdev, &stat->subdev);
}
+static int isp_stat_init_entities(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ struct v4l2_subdev *subdev = &stat->subdev;
+ struct media_entity *me = &subdev->entity;
+
+ v4l2_subdev_init(subdev, sd_ops);
+ snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
+ subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(subdev, stat);
+
+ stat->pad.flags = MEDIA_PAD_FL_SINK;
+ me->ops = NULL;
+
+ return media_entity_init(me, 1, &stat->pad, 0);
+}
+
int omap3isp_stat_init(struct ispstat *stat, const char *name,
const struct v4l2_subdev_ops *sd_ops)
{
+ int ret;
+
stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
if (!stat->buf)
return -ENOMEM;
+
isp_stat_buf_clear(stat);
mutex_init(&stat->ioctl_lock);
atomic_set(&stat->buf_err, 0);
- return isp_stat_init_entities(stat, name, sd_ops);
+ ret = isp_stat_init_entities(stat, name, sd_ops);
+ if (ret < 0) {
+ mutex_destroy(&stat->ioctl_lock);
+ kfree(stat->buf);
+ }
+
+ return ret;
}
-void omap3isp_stat_free(struct ispstat *stat)
+void omap3isp_stat_cleanup(struct ispstat *stat)
{
+ media_entity_cleanup(&stat->subdev.entity);
+ mutex_destroy(&stat->ioctl_lock);
isp_stat_bufs_free(stat);
kfree(stat->buf);
}
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
index d86da94fa50d..9b7c8654dc8a 100644
--- a/drivers/media/video/omap3isp/ispstat.h
+++ b/drivers/media/video/omap3isp/ispstat.h
@@ -144,7 +144,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
struct omap3isp_stat_data *data);
int omap3isp_stat_init(struct ispstat *stat, const char *name,
const struct v4l2_subdev_ops *sd_ops);
-void omap3isp_stat_free(struct ispstat *stat);
+void omap3isp_stat_cleanup(struct ispstat *stat);
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub);
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index fd965adfd597..d1000723c5ae 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -278,7 +278,8 @@ isp_video_far_end(struct isp_video *video)
* limits reported by every block in the pipeline.
*
* Return 0 if all formats match, or -EPIPE if at least one link is found with
- * different formats on its two ends.
+ * different formats on its two ends or if the pipeline doesn't start with a
+ * video source (either a subdev with no input pad, or a non-subdev entity).
*/
static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
{
@@ -329,10 +330,15 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
* in the middle of it. */
shifter_link = subdev == &isp->isp_ccdc.subdev;
- /* Retrieve the source format */
+ /* Retrieve the source format. Return an error if no source
+ * entity can be found, and stop checking the pipeline if the
+ * source entity isn't a subdev.
+ */
pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (pad == NULL)
+ return -EPIPE;
+
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
subdev = media_entity_to_v4l2_subdev(pad->entity);
@@ -446,7 +452,7 @@ ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
sgt->nents = sglen;
sgt->orig_nents = sglen;
- da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG);
+ da = omap_iommu_vmap(isp->domain, isp->iommu, 0, sgt, IOMMU_FLAG);
if (IS_ERR_VALUE(da))
kfree(sgt);
@@ -462,7 +468,7 @@ static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
{
struct sg_table *sgt;
- sgt = iommu_vunmap(isp->iommu, (u32)da);
+ sgt = omap_iommu_vunmap(isp->domain, isp->iommu, (u32)da);
kfree(sgt);
}
@@ -1050,6 +1056,14 @@ error:
if (video->isp->pdata->set_constraints)
video->isp->pdata->set_constraints(video->isp, false);
media_entity_pipeline_stop(&video->video.entity);
+ /* The DMA queue must be emptied here, otherwise CCDC interrupts
+ * that will get triggered the next time the CCDC is powered up
+ * will try to access buffers that might have been freed but
+ * still present in the DMA queue. This can easily get triggered
+ * if the above omap3isp_pipeline_set_stream() call fails on a
+ * system with a free-running sensor.
+ */
+ INIT_LIST_HEAD(&video->dmaqueue);
video->queue = NULL;
}
@@ -1311,6 +1325,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
return 0;
}
+void omap3isp_video_cleanup(struct isp_video *video)
+{
+ media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->stream_lock);
+ mutex_destroy(&video->mutex);
+}
+
int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
{
int ret;
@@ -1327,8 +1348,6 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
void omap3isp_video_unregister(struct isp_video *video)
{
- if (video_is_registered(&video->video)) {
- media_entity_cleanup(&video->video.entity);
+ if (video_is_registered(&video->video))
video_unregister_device(&video->video);
- }
}
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index 53160aa24e6e..08cbfa144e6e 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -190,6 +190,7 @@ struct isp_video_fh {
container_of(q, struct isp_video_fh, queue)
int omap3isp_video_init(struct isp_video *video, const char *name);
+void omap3isp_video_cleanup(struct isp_video *video);
int omap3isp_video_register(struct isp_video *video,
struct v4l2_device *vdev);
void omap3isp_video_unregister(struct isp_video *video);
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index 9ce2fa037b94..b5247cb64fde 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -18,11 +18,13 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/v4l2-ctrls.h>
#define VAL_SET(x, mask, rshift, lshift) \
((((x) >> rshift) & mask) << lshift)
@@ -299,12 +301,10 @@ struct ov2640_win_size {
struct ov2640_priv {
struct v4l2_subdev subdev;
- struct ov2640_camera_info *info;
+ struct v4l2_ctrl_handler hdl;
enum v4l2_mbus_pixelcode cfmt_code;
const struct ov2640_win_size *win;
int model;
- u16 flag_vflip:1;
- u16 flag_hflip:1;
};
/*
@@ -610,29 +610,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = {
};
/*
- * Supported controls
- */
-static const struct v4l2_queryctrl ov2640_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
-/*
* General functions
*/
static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
@@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int ov2640_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
- /* Only one width bit may be set */
- if (!is_power_of_2(width_flag))
- return -EINVAL;
-
- if (icl->set_bus_param)
- return icl->set_bus_param(icl, width_flag);
-
- /*
- * Without board specific bus width settings we support only the
- * sensors native bus width witch are tested working
- */
- if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8))
- return 0;
-
- return 0;
-}
-
-static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (icl->query_bus_param)
- flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- }
- return 0;
-}
-
-static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
- int ret = 0;
u8 val;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- val = ctrl->value ? REG04_VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->value ? 1 : 0;
- ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
- break;
+ val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
case V4L2_CID_HFLIP:
- val = ctrl->value ? REG04_HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->value ? 1 : 0;
- ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
- break;
+ val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
}
- return ret;
+ return -EINVAL;
}
static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
@@ -1023,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int ov2640_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov2640_video_probe(struct i2c_client *client)
{
struct ov2640_priv *priv = to_ov2640(client);
u8 pid, ver, midh, midl;
const char *devname;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* check and show product ID and manufacturer ID
*/
@@ -1060,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd,
"%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
- return 0;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
err:
return ret;
}
-static struct soc_camera_ops ov2640_ops = {
- .set_bus_param = ov2640_set_bus_param,
- .query_bus_param = ov2640_query_bus_param,
- .controls = ov2640_controls,
- .num_controls = ARRAY_SIZE(ov2640_controls),
+static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
+ .s_ctrl = ov2640_s_ctrl,
};
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
- .g_ctrl = ov2640_g_ctrl,
- .s_ctrl = ov2640_s_ctrl,
.g_chip_ident = ov2640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov2640_g_register,
@@ -1083,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
#endif
};
+static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
.s_stream = ov2640_s_stream,
.g_mbus_fmt = ov2640_g_fmt,
@@ -1091,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
.cropcap = ov2640_cropcap,
.g_crop = ov2640_g_crop,
.enum_mbus_fmt = ov2640_enum_fmt,
+ .g_mbus_config = ov2640_g_mbus_config,
};
static struct v4l2_subdev_ops ov2640_subdev_ops = {
@@ -1104,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = {
static int ov2640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct ov2640_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
-
- if (!icd) {
- dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n");
- return -EINVAL;
- }
+ struct ov2640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&adapter->dev,
"OV2640: Missing platform_data for driver\n");
@@ -1135,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client,
return -ENOMEM;
}
- priv->info = icl->priv;
-
v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov2640_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov2640_video_probe(icd, client);
+ ret = ov2640_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
} else {
dev_info(&adapter->dev, "OV2640 Probed\n");
@@ -1155,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client,
static int ov2640_remove(struct i2c_client *client)
{
struct ov2640_priv *priv = to_ov2640(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
index 349a4ad3ccc1..bb37ec80f274 100644
--- a/drivers/media/video/ov5642.c
+++ b/drivers/media/video/ov5642.c
@@ -14,14 +14,16 @@
* published by the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>
+#include <linux/v4l2-mediabus.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
@@ -35,7 +37,7 @@
#define REG_WINDOW_START_Y_LOW 0x3803
#define REG_WINDOW_WIDTH_HIGH 0x3804
#define REG_WINDOW_WIDTH_LOW 0x3805
-#define REG_WINDOW_HEIGHT_HIGH 0x3806
+#define REG_WINDOW_HEIGHT_HIGH 0x3806
#define REG_WINDOW_HEIGHT_LOW 0x3807
#define REG_OUT_WIDTH_HIGH 0x3808
#define REG_OUT_WIDTH_LOW 0x3809
@@ -45,19 +47,44 @@
#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
+#define REG_OUTPUT_FORMAT 0x4300
+#define REG_ISP_CTRL_01 0x5001
+#define REG_AVG_WINDOW_END_X_HIGH 0x5682
+#define REG_AVG_WINDOW_END_X_LOW 0x5683
+#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
+#define REG_AVG_WINDOW_END_Y_LOW 0x5687
+
+/* active pixel array size */
+#define OV5642_SENSOR_SIZE_X 2592
+#define OV5642_SENSOR_SIZE_Y 1944
/*
- * define standard resolution.
- * Works currently only for up to 720 lines
- * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720
+ * About OV5642 resolution, cropping and binning:
+ * This sensor supports it all, at least in the feature description.
+ * Unfortunately, no combination of appropriate registers settings could make
+ * the chip work the intended way. As it works with predefined register lists,
+ * some undocumented registers are presumably changed there to achieve their
+ * goals.
+ * This driver currently only works for resolutions up to 720 lines with a
+ * 1:1 scale. Hopefully these restrictions will be removed in the future.
*/
+#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
+#define OV5642_MAX_HEIGHT 720
-#define OV5642_WIDTH 1280
-#define OV5642_HEIGHT 720
-#define OV5642_TOTAL_WIDTH 3200
-#define OV5642_TOTAL_HEIGHT 2000
-#define OV5642_SENSOR_SIZE_X 2592
-#define OV5642_SENSOR_SIZE_Y 1944
+/* default sizes */
+#define OV5642_DEFAULT_WIDTH 1280
+#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
+
+/* minimum extra blanking */
+#define BLANKING_EXTRA_WIDTH 500
+#define BLANKING_EXTRA_HEIGHT 20
+
+/*
+ * the sensor's autoexposure is buggy when setting total_height low.
+ * It tries to expose longer than 1 frame period without taking care of it
+ * and this leads to weird output. So we set 1000 lines as minimum.
+ */
+#define BLANKING_MIN_HEIGHT 1000
struct regval_list {
u16 reg_num;
@@ -582,6 +609,11 @@ struct ov5642_datafmt {
struct ov5642 {
struct v4l2_subdev subdev;
const struct ov5642_datafmt *fmt;
+ struct v4l2_rect crop_rect;
+
+ /* blanking information */
+ int total_width;
+ int total_height;
};
static const struct ov5642_datafmt ov5642_colour_fmts[] = {
@@ -642,6 +674,21 @@ static int reg_write(struct i2c_client *client, u16 reg, u8 val)
return 0;
}
+
+/*
+ * convenience function to write 16 bit register values that are split up
+ * into two consecutive high and low parts
+ */
+static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
+{
+ int ret;
+
+ ret = reg_write(client, reg, val16 >> 8);
+ if (ret)
+ return ret;
+ return reg_write(client, reg + 1, val16 & 0x00ff);
+}
+
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
@@ -685,58 +732,55 @@ static int ov5642_write_array(struct i2c_client *client,
return 0;
}
-static int ov5642_set_resolution(struct i2c_client *client)
+static int ov5642_set_resolution(struct v4l2_subdev *sd)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ int width = priv->crop_rect.width;
+ int height = priv->crop_rect.height;
+ int total_width = priv->total_width;
+ int total_height = priv->total_height;
+ int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
+ int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
int ret;
- u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8;
- u8 start_x_low = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff;
- u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8;
- u8 start_y_low = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff;
-
- u8 width_high = OV5642_WIDTH >> 8;
- u8 width_low = OV5642_WIDTH & 0xff;
- u8 height_high = OV5642_HEIGHT >> 8;
- u8 height_low = OV5642_HEIGHT & 0xff;
-
- u8 total_width_high = OV5642_TOTAL_WIDTH >> 8;
- u8 total_width_low = OV5642_TOTAL_WIDTH & 0xff;
- u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8;
- u8 total_height_low = OV5642_TOTAL_HEIGHT & 0xff;
-
- ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low);
+ /*
+ * This should set the starting point for cropping.
+ * Doesn't work so far.
+ */
+ ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
if (!ret)
- ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high);
- if (!ret)
- ret = reg_write(client, REG_WINDOW_HEIGHT_LOW, height_low);
+ ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
+ if (!ret) {
+ priv->crop_rect.left = start_x;
+ priv->crop_rect.top = start_y;
+ }
if (!ret)
- ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high);
+ ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
if (!ret)
- ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low);
- if (!ret)
- ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high);
+ ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
+ if (ret)
+ return ret;
+ priv->crop_rect.width = width;
+ priv->crop_rect.height = height;
+
+ /* Set the output window size. Only 1:1 scale is supported so far. */
+ ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
if (!ret)
- ret = reg_write(client, REG_OUT_HEIGHT_LOW, height_low);
+ ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
+ /* Total width = output size + blanking */
if (!ret)
- ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high);
+ ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
if (!ret)
- ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low);
+ ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
+
+ /* Sets the window for AWB calculations */
if (!ret)
- ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high);
+ ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
if (!ret)
- ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW, total_height_low);
+ ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
return ret;
}
@@ -744,18 +788,18 @@ static int ov5642_set_resolution(struct i2c_client *client)
static int ov5642_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
- dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n",
- __func__, mf->code, mf->width, mf->height);
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
if (!fmt) {
mf->code = ov5642_colour_fmts[0].code;
mf->colorspace = ov5642_colour_fmts[0].colorspace;
}
- mf->width = OV5642_WIDTH;
- mf->height = OV5642_HEIGHT;
mf->field = V4L2_FIELD_NONE;
return 0;
@@ -767,20 +811,13 @@ static int ov5642_s_fmt(struct v4l2_subdev *sd,
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5642 *priv = to_ov5642(client);
- dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
-
/* MIPI CSI could have changed the format, double-check */
if (!ov5642_find_datafmt(mf->code))
return -EINVAL;
ov5642_try_fmt(sd, mf);
-
priv->fmt = ov5642_find_datafmt(mf->code);
- ov5642_write_array(client, ov5642_default_regs_init);
- ov5642_set_resolution(client);
- ov5642_write_array(client, ov5642_default_regs_finalise);
-
return 0;
}
@@ -794,8 +831,8 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd,
mf->code = fmt->code;
mf->colorspace = fmt->colorspace;
- mf->width = OV5642_WIDTH;
- mf->height = OV5642_HEIGHT;
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
mf->field = V4L2_FIELD_NONE;
return 0;
@@ -828,15 +865,44 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
return 0;
}
+static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+ int ret;
+
+ v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
+ &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
+
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+ priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
+ priv->total_height = max_t(int, rect->height +
+ BLANKING_EXTRA_HEIGHT,
+ BLANKING_MIN_HEIGHT);
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
struct v4l2_rect *rect = &a->c;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rect->top = 0;
- rect->left = 0;
- rect->width = OV5642_WIDTH;
- rect->height = OV5642_HEIGHT;
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ *rect = priv->crop_rect;
return 0;
}
@@ -845,8 +911,8 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = OV5642_WIDTH;
- a->bounds.height = OV5642_HEIGHT;
+ a->bounds.width = OV5642_MAX_WIDTH;
+ a->bounds.height = OV5642_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
@@ -855,16 +921,47 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client;
+ int ret;
+
+ if (!on)
+ return 0;
+
+ client = v4l2_get_subdevdata(sd);
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
.s_mbus_fmt = ov5642_s_fmt,
.g_mbus_fmt = ov5642_g_fmt,
.try_mbus_fmt = ov5642_try_fmt,
.enum_mbus_fmt = ov5642_enum_fmt,
+ .s_crop = ov5642_s_crop,
.g_crop = ov5642_g_crop,
.cropcap = ov5642_cropcap,
+ .g_mbus_config = ov5642_g_mbus_config,
};
static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+ .s_power = ov5642_s_power,
.g_chip_ident = ov5642_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov5642_get_register,
@@ -877,28 +974,7 @@ static struct v4l2_subdev_ops ov5642_subdev_ops = {
.video = &ov5642_subdev_video_ops,
};
-/*
- * We have to provide soc-camera operations, but we don't have anything to say
- * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
- */
-static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd)
-{
- return 0;
-}
-
-static int soc_ov5642_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return -EINVAL;
-}
-
-static struct soc_camera_ops soc_ov5642_ops = {
- .query_bus_param = soc_ov5642_query_bus_param,
- .set_bus_param = soc_ov5642_set_bus_param,
-};
-
-static int ov5642_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov5642_video_probe(struct i2c_client *client)
{
int ret;
u8 id_high, id_low;
@@ -929,16 +1005,9 @@ static int ov5642_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov5642 *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "OV5642: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "OV5642: missing platform data!\n");
return -EINVAL;
@@ -950,17 +1019,24 @@ static int ov5642_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
- icd->ops = &soc_ov5642_ops;
- priv->fmt = &ov5642_colour_fmts[0];
+ priv->fmt = &ov5642_colour_fmts[0];
+
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
+ priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
+ priv->total_height = BLANKING_MIN_HEIGHT;
- ret = ov5642_video_probe(icd, client);
+ ret = ov5642_video_probe(client);
if (ret < 0)
goto error;
return 0;
error:
- icd->ops = NULL;
kfree(priv);
return ret;
}
@@ -968,10 +1044,8 @@ error:
static int ov5642_remove(struct i2c_client *client)
{
struct ov5642 *priv = to_ov5642(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
if (icl->free_bus)
icl->free_bus(icl);
kfree(priv);
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
index 456d9ad9ae5a..d5b057207a7b 100644
--- a/drivers/media/video/ov6650.c
+++ b/drivers/media/video/ov6650.c
@@ -28,10 +28,11 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
-
+#include <media/v4l2-ctrls.h>
/* Register definitions */
#define REG_GAIN 0x00 /* range 00 - 3F */
@@ -177,20 +178,23 @@ struct ov6650_reg {
struct ov6650 {
struct v4l2_subdev subdev;
-
- int gain;
- int blue;
- int red;
- int saturation;
- int hue;
- int brightness;
- int exposure;
- int gamma;
- int aec;
- bool vflip;
- bool hflip;
- bool awb;
- bool agc;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/autoexposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/autogain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct {
+ /* blue/red/autowhitebalance cluster */
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
bool half_scale; /* scale down output by 2 */
struct v4l2_rect rect; /* sensor cropping window */
unsigned long pclk_limit; /* from host */
@@ -210,126 +214,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
V4L2_MBUS_FMT_Y8_1X8,
};
-static const struct v4l2_queryctrl ov6650_controls[] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "AGC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- .default_value = DEF_GAIN,
- },
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "AWB",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_BLUE,
- },
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_RED,
- },
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xf,
- .step = 1,
- .default_value = 0x8,
- },
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = HUE_MASK,
- .step = 1,
- .default_value = DEF_HUE,
- },
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x80,
- },
- {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "AEC",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = DEF_AECH,
- },
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x12,
- },
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
{
@@ -419,213 +303,90 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-/* Alter bus settings on camera side */
-static int ov6650_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- int ret;
-
- flags = soc_camera_apply_sensor_flags(icl, flags);
-
- if (flags & SOCAM_PCLK_SAMPLE_RISING)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
- if (ret)
- return ret;
-
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
- ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
- if (ret)
- return ret;
-
- if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
-
- return ret;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- unsigned long flags = SOCAM_MASTER |
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
/* Get status of additional camera capabilities */
-static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- uint8_t reg;
- int ret = 0;
+ uint8_t reg, reg2;
+ int ret;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
- ctrl->value = priv->agc;
- break;
- case V4L2_CID_GAIN:
- if (priv->agc) {
- ret = ov6650_reg_read(client, REG_GAIN, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->gain;
- }
- break;
+ ret = ov6650_reg_read(client, REG_GAIN, &reg);
+ if (!ret)
+ priv->gain->val = reg;
+ return ret;
case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = priv->awb;
- break;
- case V4L2_CID_BLUE_BALANCE:
- if (priv->awb) {
- ret = ov6650_reg_read(client, REG_BLUE, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->blue;
- }
- break;
- case V4L2_CID_RED_BALANCE:
- if (priv->awb) {
- ret = ov6650_reg_read(client, REG_RED, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->red;
+ ret = ov6650_reg_read(client, REG_BLUE, &reg);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_RED, &reg2);
+ if (!ret) {
+ priv->blue->val = reg;
+ priv->red->val = reg2;
}
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = priv->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = priv->hue;
- break;
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = priv->brightness;
- break;
+ return ret;
case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = priv->aec;
- break;
- case V4L2_CID_EXPOSURE:
- if (priv->aec) {
- ret = ov6650_reg_read(client, REG_AECH, &reg);
- ctrl->value = reg;
- } else {
- ctrl->value = priv->exposure;
- }
- break;
- case V4L2_CID_GAMMA:
- ctrl->value = priv->gamma;
- break;
- case V4L2_CID_VFLIP:
- ctrl->value = priv->vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->hflip;
- break;
+ ret = ov6650_reg_read(client, REG_AECH, &reg);
+ if (!ret)
+ priv->exposure->val = reg;
+ return ret;
}
- return ret;
+ return -EINVAL;
}
/* Set status of additional camera capabilities */
-static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- int ret = 0;
+ int ret;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_AGC : 0, COMB_AGC);
- if (!ret)
- priv->agc = ctrl->value;
- break;
- case V4L2_CID_GAIN:
- ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
- if (!ret)
- priv->gain = ctrl->value;
- break;
+ ctrl->val ? COMB_AGC : 0, COMB_AGC);
+ if (!ret && !ctrl->val)
+ ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
+ return ret;
case V4L2_CID_AUTO_WHITE_BALANCE:
ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_AWB : 0, COMB_AWB);
- if (!ret)
- priv->awb = ctrl->value;
- break;
- case V4L2_CID_BLUE_BALANCE:
- ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
- if (!ret)
- priv->blue = ctrl->value;
- break;
- case V4L2_CID_RED_BALANCE:
- ret = ov6650_reg_write(client, REG_RED, ctrl->value);
- if (!ret)
- priv->red = ctrl->value;
- break;
+ ctrl->val ? COMB_AWB : 0, COMB_AWB);
+ if (!ret && !ctrl->val) {
+ ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_RED,
+ priv->red->val);
+ }
+ return ret;
case V4L2_CID_SATURATION:
- ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
+ return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
SAT_MASK);
- if (!ret)
- priv->saturation = ctrl->value;
- break;
case V4L2_CID_HUE:
- ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
+ return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
HUE_MASK);
- if (!ret)
- priv->hue = ctrl->value;
- break;
case V4L2_CID_BRIGHTNESS:
- ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
- if (!ret)
- priv->brightness = ctrl->value;
- break;
+ return ov6650_reg_write(client, REG_BRT, ctrl->val);
case V4L2_CID_EXPOSURE_AUTO:
- switch (ctrl->value) {
- case V4L2_EXPOSURE_AUTO:
- ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
- break;
- default:
- ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
- break;
- }
- if (!ret)
- priv->aec = ctrl->value;
- break;
- case V4L2_CID_EXPOSURE:
- ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
- if (!ret)
- priv->exposure = ctrl->value;
- break;
+ ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
+ V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+ ret = ov6650_reg_write(client, REG_AECH,
+ priv->exposure->val);
+ return ret;
case V4L2_CID_GAMMA:
- ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
- if (!ret)
- priv->gamma = ctrl->value;
- break;
+ return ov6650_reg_write(client, REG_GAM1, ctrl->val);
case V4L2_CID_VFLIP:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
- if (!ret)
- priv->vflip = ctrl->value;
- break;
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
case V4L2_CID_HFLIP:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
- if (!ret)
- priv->hflip = ctrl->value;
- break;
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
}
- return ret;
+ return -EINVAL;
}
/* Get chip identification */
@@ -778,7 +539,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe,
static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
struct soc_camera_sense *sense = icd->sense;
struct ov6650 *priv = to_ov6650(client);
bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
@@ -1057,8 +818,7 @@ static int ov6650_prog_dflt(struct i2c_client *client)
return ret;
}
-static int ov6650_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov6650_video_probe(struct i2c_client *client)
{
u8 pidh, pidl, midh, midl;
int ret = 0;
@@ -1094,16 +854,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
return ret;
}
-static struct soc_camera_ops ov6650_ops = {
- .set_bus_param = ov6650_set_bus_param,
- .query_bus_param = ov6650_query_bus_param,
- .controls = ov6650_controls,
- .num_controls = ARRAY_SIZE(ov6650_controls),
+static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
+ .g_volatile_ctrl = ov6550_g_volatile_ctrl,
+ .s_ctrl = ov6550_s_ctrl,
};
static struct v4l2_subdev_core_ops ov6650_core_ops = {
- .g_ctrl = ov6650_g_ctrl,
- .s_ctrl = ov6650_s_ctrl,
.g_chip_ident = ov6650_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov6650_get_register,
@@ -1111,6 +867,55 @@ static struct v4l2_subdev_core_ops ov6650_core_ops = {
#endif
};
+/* Request bus settings on camera side */
+static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ int ret;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+ return ret;
+}
+
static struct v4l2_subdev_video_ops ov6650_video_ops = {
.s_stream = ov6650_s_stream,
.g_mbus_fmt = ov6650_g_fmt,
@@ -1122,6 +927,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = {
.s_crop = ov6650_s_crop,
.g_parm = ov6650_g_parm,
.s_parm = ov6650_s_parm,
+ .g_mbus_config = ov6650_g_mbus_config,
+ .s_mbus_config = ov6650_s_mbus_config,
};
static struct v4l2_subdev_ops ov6650_subdev_ops = {
@@ -1136,16 +943,9 @@ static int ov6650_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov6650 *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -1159,8 +959,46 @@ static int ov6650_probe(struct i2c_client *client,
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
+ priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
+ priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
+ priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
+ &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
+
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov6650_ops;
+ kfree(priv);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
+ v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
priv->rect.left = DEF_HSTRT << 1;
priv->rect.top = DEF_VSTRT << 1;
@@ -1170,10 +1008,12 @@ static int ov6650_probe(struct i2c_client *client,
priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
- ret = ov6650_video_probe(icd, client);
+ ret = ov6650_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -1184,6 +1024,8 @@ static int ov6650_remove(struct i2c_client *client)
{
struct ov6650 *priv = to_ov6650(client);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 397870f076c1..9f6ce3d8a29e 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -20,12 +20,14 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/ov772x.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/ov772x.h>
/*
* register offset
@@ -400,6 +402,7 @@ struct ov772x_win_size {
struct ov772x_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
struct ov772x_camera_info *info;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
@@ -517,36 +520,6 @@ static const struct ov772x_win_size ov772x_win_qvga = {
.regs = ov772x_qvga_regs,
};
-static const struct v4l2_queryctrl ov772x_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_BAND_STOP_FILTER,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Band-stop filter",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
-};
-
/*
* general function
*/
@@ -620,75 +593,30 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int ov772x_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
- struct ov772x_priv *priv = i2c_get_clientdata(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (priv->info->flags & OV772X_FLAG_8BIT)
- flags |= SOCAM_DATAWIDTH_8;
- else
- flags |= SOCAM_DATAWIDTH_10;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- case V4L2_CID_BAND_STOP_FILTER:
- ctrl->value = priv->band_filter;
- break;
- }
- return 0;
-}
-
-static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct ov772x_priv *priv = container_of(ctrl->handler,
+ struct ov772x_priv, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
int ret = 0;
u8 val;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- val = ctrl->value ? VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->value;
+ val = ctrl->val ? VFLIP_IMG : 0x00;
+ priv->flag_vflip = ctrl->val;
if (priv->info->flags & OV772X_FLAG_VFLIP)
val ^= VFLIP_IMG;
- ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
- break;
+ return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
- val = ctrl->value ? HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->value;
+ val = ctrl->val ? HFLIP_IMG : 0x00;
+ priv->flag_hflip = ctrl->val;
if (priv->info->flags & OV772X_FLAG_HFLIP)
val ^= HFLIP_IMG;
- ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
- break;
+ return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
case V4L2_CID_BAND_STOP_FILTER:
- if ((unsigned)ctrl->value > 256)
- ctrl->value = 256;
- if (ctrl->value == priv->band_filter)
- break;
- if (!ctrl->value) {
+ if (!ctrl->val) {
/* Switch the filter off, it is on now */
ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
if (!ret)
@@ -696,7 +624,7 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
BNDF_ON_OFF, 0);
} else {
/* Switch the filter on, set AEC low limit */
- val = 256 - ctrl->value;
+ val = 256 - ctrl->val;
ret = ov772x_mask_set(client, COM8,
BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
@@ -704,11 +632,11 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
0xff, val);
}
if (!ret)
- priv->band_filter = ctrl->value;
- break;
+ priv->band_filter = ctrl->val;
+ return ret;
}
- return ret;
+ return -EINVAL;
}
static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
@@ -822,13 +750,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
+ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
priv->info->edgectrl.threshold);
if (ret < 0)
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_STRNGT, EDGE_STRENGTH_MASK,
+ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
priv->info->edgectrl.strength);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -840,13 +768,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
* set upper and lower limit
*/
ret = ov772x_mask_set(client,
- EDGE_UPPER, EDGE_UPPER_MASK,
+ EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
priv->info->edgectrl.upper);
if (ret < 0)
goto ov772x_set_fmt_error;
ret = ov772x_mask_set(client,
- EDGE_LOWER, EDGE_LOWER_MASK,
+ EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
priv->info->edgectrl.lower);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -1025,17 +953,12 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int ov772x_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov772x_video_probe(struct i2c_client *client)
{
struct ov772x_priv *priv = to_ov772x(client);
u8 pid, ver;
const char *devname;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* check and show product ID and manufacturer ID
*/
@@ -1064,20 +987,14 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
ver,
i2c_smbus_read_byte_data(client, MIDH),
i2c_smbus_read_byte_data(client, MIDL));
-
- return 0;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
}
-static struct soc_camera_ops ov772x_ops = {
- .set_bus_param = ov772x_set_bus_param,
- .query_bus_param = ov772x_query_bus_param,
- .controls = ov772x_controls,
- .num_controls = ARRAY_SIZE(ov772x_controls),
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+ .s_ctrl = ov772x_s_ctrl,
};
static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
- .g_ctrl = ov772x_g_ctrl,
- .s_ctrl = ov772x_s_ctrl,
.g_chip_ident = ov772x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov772x_g_register,
@@ -1095,6 +1012,21 @@ static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.s_stream = ov772x_s_stream,
.g_mbus_fmt = ov772x_g_fmt,
@@ -1103,6 +1035,7 @@ static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.cropcap = ov772x_cropcap,
.g_crop = ov772x_g_crop,
.enum_mbus_fmt = ov772x_enum_fmt,
+ .g_mbus_config = ov772x_g_mbus_config,
};
static struct v4l2_subdev_ops ov772x_subdev_ops = {
@@ -1117,20 +1050,15 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = {
static int ov772x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct ov772x_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
-
- if (!icd) {
- dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
- return -EINVAL;
- }
+ struct ov772x_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "OV772X: missing platform data!\n");
return -EINVAL;
+ }
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&adapter->dev,
@@ -1146,12 +1074,24 @@ static int ov772x_probe(struct i2c_client *client,
priv->info = icl->priv;
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 3);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov772x_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov772x_video_probe(icd, client);
+ ret = ov772x_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -1161,9 +1101,9 @@ static int ov772x_probe(struct i2c_client *client,
static int ov772x_remove(struct i2c_client *client)
{
struct ov772x_priv *priv = to_ov772x(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 3681a6ff0815..a4f99797eb56 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -24,10 +24,13 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
#include "ov9640.h"
@@ -162,27 +165,6 @@ static enum v4l2_mbus_pixelcode ov9640_codes[] = {
V4L2_MBUS_FMT_RGB565_2X8_LE,
};
-static const struct v4l2_queryctrl ov9640_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
{
@@ -284,75 +266,25 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-/* Alter bus settings on camera side */
-static int ov9640_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- /*
- * REVISIT: the camera probably can do 10 bit transfers, but I don't
- * have those pins connected on my hardware.
- */
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-/* Get status of additional camera capabilities */
-static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- }
- return 0;
-}
-
/* Set status of additional camera capabilities */
-static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- int ret = 0;
+ struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- priv->flag_vflip = ctrl->value;
- if (ctrl->value)
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_V, 0);
- else
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
- 0, OV9640_MVFP_V);
- break;
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
case V4L2_CID_HFLIP:
- priv->flag_hflip = ctrl->value;
- if (ctrl->value)
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_H, 0);
- else
- ret = ov9640_reg_rmw(client, OV9640_MVFP,
- 0, OV9640_MVFP_H);
- break;
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
}
-
- return ret;
+ return -EINVAL;
}
/* Get chip identification */
@@ -646,10 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-
-
-static int ov9640_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov9640_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
@@ -657,29 +586,19 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
const char *devname;
int ret = 0;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* check and show product ID and manufacturer ID
*/
ret = ov9640_reg_read(client, OV9640_PID, &pid);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_VER, &ver);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_VER, &ver);
- if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
- if (ret)
- goto err;
-
- ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
- if (ret)
- goto err;
+ return ret;
switch (VERSION(pid, ver)) {
case OV9640_V2:
@@ -693,27 +612,20 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
break;
default:
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
- ret = -ENODEV;
- goto err;
+ return -ENODEV;
}
dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
-err:
- return ret;
+ return v4l2_ctrl_handler_setup(&priv->hdl);
}
-static struct soc_camera_ops ov9640_ops = {
- .set_bus_param = ov9640_set_bus_param,
- .query_bus_param = ov9640_query_bus_param,
- .controls = ov9640_controls,
- .num_controls = ARRAY_SIZE(ov9640_controls),
+static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
+ .s_ctrl = ov9640_s_ctrl,
};
static struct v4l2_subdev_core_ops ov9640_core_ops = {
- .g_ctrl = ov9640_g_ctrl,
- .s_ctrl = ov9640_s_ctrl,
.g_chip_ident = ov9640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9640_get_register,
@@ -722,6 +634,22 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = {
};
+/* Request bus settings on camera side */
+static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov9640_video_ops = {
.s_stream = ov9640_s_stream,
.s_mbus_fmt = ov9640_s_fmt,
@@ -729,7 +657,7 @@ static struct v4l2_subdev_video_ops ov9640_video_ops = {
.enum_mbus_fmt = ov9640_enum_fmt,
.cropcap = ov9640_cropcap,
.g_crop = ov9640_g_crop,
-
+ .g_mbus_config = ov9640_g_mbus_config,
};
static struct v4l2_subdev_ops ov9640_subdev_ops = {
@@ -744,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9640_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -768,12 +689,23 @@ static int ov9640_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
- icd->ops = &ov9640_ops;
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
- ret = ov9640_video_probe(icd, client);
+ ret = ov9640_video_probe(client);
if (ret) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -785,6 +717,8 @@ static int ov9640_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
index f8a51b70792e..6b33a972c83c 100644
--- a/drivers/media/video/ov9640.h
+++ b/drivers/media/video/ov9640.h
@@ -198,12 +198,10 @@ struct ov9640_reg {
struct ov9640_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
int model;
int revision;
-
- bool flag_vflip;
- bool flag_hflip;
};
#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
index edd1ffcca30b..d9a9f7174f7a 100644
--- a/drivers/media/video/ov9740.c
+++ b/drivers/media/video/ov9740.c
@@ -14,8 +14,11 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <media/v4l2-chip-ident.h>
+#include <linux/v4l2-mediabus.h>
+
#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
@@ -192,6 +195,7 @@ struct ov9740_reg {
struct ov9740_priv {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
int ident;
u16 model;
@@ -392,27 +396,6 @@ static enum v4l2_mbus_pixelcode ov9740_codes[] = {
V4L2_MBUS_FMT_YUYV8_2X8,
};
-static const struct v4l2_queryctrl ov9740_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
/* read a register */
static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
{
@@ -560,25 +543,6 @@ static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
-/* Alter bus settings on camera side */
-static int ov9740_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
/* select nearest higher resolution for capture */
static void ov9740_res_roundup(u32 *width, u32 *height)
{
@@ -788,36 +752,18 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
return 0;
}
-/* Get status of additional camera capabilities */
-static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- ctrl->value = priv->flag_vflip;
- break;
- case V4L2_CID_HFLIP:
- ctrl->value = priv->flag_hflip;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
/* Set status of additional camera capabilities */
-static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ov9740_priv *priv = to_ov9740(sd);
+ struct ov9740_priv *priv =
+ container_of(ctrl->handler, struct ov9740_priv, hdl);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- priv->flag_vflip = ctrl->value;
+ priv->flag_vflip = ctrl->val;
break;
case V4L2_CID_HFLIP:
- priv->flag_hflip = ctrl->value;
+ priv->flag_hflip = ctrl->val;
break;
default:
return -EINVAL;
@@ -890,18 +836,13 @@ static int ov9740_set_register(struct v4l2_subdev *sd,
}
#endif
-static int ov9740_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int ov9740_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9740_priv *priv = to_ov9740(sd);
u8 modelhi, modello;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* check and show product ID and manufacturer ID
*/
@@ -942,25 +883,33 @@ err:
return ret;
}
-static struct soc_camera_ops ov9740_ops = {
- .set_bus_param = ov9740_set_bus_param,
- .query_bus_param = ov9740_query_bus_param,
- .controls = ov9740_controls,
- .num_controls = ARRAY_SIZE(ov9740_controls),
-};
+/* Request bus settings on camera side */
+static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
static struct v4l2_subdev_video_ops ov9740_video_ops = {
- .s_stream = ov9740_s_stream,
- .s_mbus_fmt = ov9740_s_fmt,
- .try_mbus_fmt = ov9740_try_fmt,
- .enum_mbus_fmt = ov9740_enum_fmt,
- .cropcap = ov9740_cropcap,
- .g_crop = ov9740_g_crop,
+ .s_stream = ov9740_s_stream,
+ .s_mbus_fmt = ov9740_s_fmt,
+ .try_mbus_fmt = ov9740_try_fmt,
+ .enum_mbus_fmt = ov9740_enum_fmt,
+ .cropcap = ov9740_cropcap,
+ .g_crop = ov9740_g_crop,
+ .g_mbus_config = ov9740_g_mbus_config,
};
static struct v4l2_subdev_core_ops ov9740_core_ops = {
- .g_ctrl = ov9740_g_ctrl,
- .s_ctrl = ov9740_s_ctrl,
.g_chip_ident = ov9740_g_chip_ident,
.s_power = ov9740_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -974,6 +923,10 @@ static struct v4l2_subdev_ops ov9740_subdev_ops = {
.video = &ov9740_video_ops,
};
+static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
+ .s_ctrl = ov9740_s_ctrl,
+};
+
/*
* i2c_driver function
*/
@@ -981,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9740_priv *priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
int ret;
- if (!icd) {
- dev_err(&client->dev, "Missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
@@ -1003,12 +949,24 @@ static int ov9740_probe(struct i2c_client *client,
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
- icd->ops = &ov9740_ops;
+ kfree(priv);
+ return err;
+ }
- ret = ov9740_video_probe(icd, client);
+ ret = ov9740_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
if (ret < 0) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
}
@@ -1019,8 +977,9 @@ static int ov9740_remove(struct i2c_client *client)
{
struct ov9740_priv *priv = i2c_get_clientdata(client);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
kfree(priv);
-
return 0;
}
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index de2fc14f043b..c17f37d964ad 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -16,7 +16,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o \
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index e98d38212791..5a6f24d1246d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -2993,6 +2993,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
}
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std)
+{
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ video, querystd, std);
+ return 0;
+}
+
/* Execute whatever commands are required to update the state of all the
sub-devices so that they match our current control values. */
static void pvr2_subdev_update(struct pvr2_hdw *hdw)
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index d7753ae9ff46..66546580b17d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -214,6 +214,9 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
unsigned int idx);
+/* Get the detected video standard */
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std);
+
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index e27f8ab76966..ce7ac4595276 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -227,6 +227,14 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break;
}
+ case VIDIOC_QUERYSTD:
+ {
+ v4l2_std_id *std = arg;
+ *std = V4L2_STD_ALL;
+ ret = pvr2_hdw_get_detected_std(hdw, std);
+ break;
+ }
+
case VIDIOC_G_STD:
{
int val = 0;
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 51ca3589b1b5..01ff643e682d 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -744,9 +744,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
/***************************************************************************/
/* Videobuf2 operations */
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
@@ -816,7 +816,7 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}
-static int start_streaming(struct vb2_queue *vq)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 8c70e64444e7..a10ff6b64acf 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -83,6 +83,7 @@ static const struct v4l2_ctrl_config pwc_contour_cfg = {
.id = PWC_CID_CUSTOM(contour),
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contour",
+ .flags = V4L2_CTRL_FLAG_SLIDER,
.min = 0,
.max = 63,
.step = 1,
@@ -206,8 +207,7 @@ int pwc_init_controls(struct pwc_device *pdev)
pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
- v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual,
- pdev->auto_white_balance->cur.val == awb_auto);
+ v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true);
/* autogain, gain */
r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
@@ -331,12 +331,12 @@ int pwc_init_controls(struct pwc_device *pdev)
pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
NULL);
if (pdev->restore_user)
- pdev->restore_user->flags = V4L2_CTRL_FLAG_UPDATE;
+ pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE;
pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
&pwc_restore_factory_cfg,
NULL);
if (pdev->restore_factory)
- pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE;
+ pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE;
if (!(pdev->features & FEATURE_MOTOR_PANTILT))
return hdl->error;
@@ -563,8 +563,10 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- if (pdev->color_bal_valid && time_before(jiffies,
- pdev->last_color_bal_update + HZ / 4)) {
+ if (pdev->color_bal_valid &&
+ (pdev->auto_white_balance->val != awb_auto ||
+ time_before(jiffies,
+ pdev->last_color_bal_update + HZ / 4))) {
pdev->red_balance->val = pdev->last_red_balance;
pdev->blue_balance->val = pdev->last_blue_balance;
break;
@@ -630,7 +632,7 @@ leave:
static int pwc_set_awb(struct pwc_device *pdev)
{
- int ret = 0;
+ int ret;
if (pdev->auto_white_balance->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
@@ -639,52 +641,34 @@ static int pwc_set_awb(struct pwc_device *pdev)
if (ret)
return ret;
- /* Update val when coming from auto or going to a preset */
- if (pdev->red_balance->is_volatile ||
- pdev->auto_white_balance->val == awb_indoor ||
- pdev->auto_white_balance->val == awb_outdoor ||
- pdev->auto_white_balance->val == awb_fl) {
- if (!pdev->red_balance->is_new)
- pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
- READ_RED_GAIN_FORMATTER,
- &pdev->red_balance->val);
- if (!pdev->blue_balance->is_new)
- pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
- READ_BLUE_GAIN_FORMATTER,
- &pdev->blue_balance->val);
- }
- if (pdev->auto_white_balance->val == awb_auto) {
- pdev->red_balance->is_volatile = true;
- pdev->blue_balance->is_volatile = true;
+ if (pdev->auto_white_balance->val != awb_manual)
pdev->color_bal_valid = false; /* Force cache update */
- } else {
- pdev->red_balance->is_volatile = false;
- pdev->blue_balance->is_volatile = false;
- }
}
+ if (pdev->auto_white_balance->val != awb_manual)
+ return 0;
- if (ret == 0 && pdev->red_balance->is_new) {
- if (pdev->auto_white_balance->val != awb_manual)
- return -EBUSY;
+ if (pdev->red_balance->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->red_balance->val);
+ if (ret)
+ return ret;
}
- if (ret == 0 && pdev->blue_balance->is_new) {
- if (pdev->auto_white_balance->val != awb_manual)
- return -EBUSY;
+ if (pdev->blue_balance->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->blue_balance->val);
+ if (ret)
+ return ret;
}
- return ret;
+ return 0;
}
/* For CODEC2 models which have separate autogain and auto exposure */
static int pwc_set_autogain(struct pwc_device *pdev)
{
- int ret = 0;
+ int ret;
if (pdev->autogain->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
@@ -692,27 +676,28 @@ static int pwc_set_autogain(struct pwc_device *pdev)
pdev->autogain->val ? 0 : 0xff);
if (ret)
return ret;
+
if (pdev->autogain->val)
pdev->gain_valid = false; /* Force cache update */
- else if (!pdev->gain->is_new)
- pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
- READ_AGC_FORMATTER,
- &pdev->gain->val);
}
- if (ret == 0 && pdev->gain->is_new) {
- if (pdev->autogain->val)
- return -EBUSY;
+
+ if (pdev->autogain->val)
+ return 0;
+
+ if (pdev->gain->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
PRESET_AGC_FORMATTER,
pdev->gain->val);
+ if (ret)
+ return ret;
}
- return ret;
+ return 0;
}
/* For CODEC2 models which have separate autogain and auto exposure */
static int pwc_set_exposure_auto(struct pwc_device *pdev)
{
- int ret = 0;
+ int ret;
int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
if (pdev->exposure_auto->is_new) {
@@ -721,27 +706,28 @@ static int pwc_set_exposure_auto(struct pwc_device *pdev)
is_auto ? 0 : 0xff);
if (ret)
return ret;
+
if (is_auto)
pdev->exposure_valid = false; /* Force cache update */
- else if (!pdev->exposure->is_new)
- pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
- READ_SHUTTER_FORMATTER,
- &pdev->exposure->val);
}
- if (ret == 0 && pdev->exposure->is_new) {
- if (is_auto)
- return -EBUSY;
+
+ if (is_auto)
+ return 0;
+
+ if (pdev->exposure->is_new) {
ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
PRESET_SHUTTER_FORMATTER,
pdev->exposure->val);
+ if (ret)
+ return ret;
}
- return ret;
+ return 0;
}
/* For CODEC3 models which have autogain controlling both gain and exposure */
static int pwc_set_autogain_expo(struct pwc_device *pdev)
{
- int ret = 0;
+ int ret;
if (pdev->autogain->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
@@ -749,35 +735,32 @@ static int pwc_set_autogain_expo(struct pwc_device *pdev)
pdev->autogain->val ? 0 : 0xff);
if (ret)
return ret;
+
if (pdev->autogain->val) {
pdev->gain_valid = false; /* Force cache update */
pdev->exposure_valid = false; /* Force cache update */
- } else {
- if (!pdev->gain->is_new)
- pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
- READ_AGC_FORMATTER,
- &pdev->gain->val);
- if (!pdev->exposure->is_new)
- pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
- READ_SHUTTER_FORMATTER,
- &pdev->exposure->val);
}
}
- if (ret == 0 && pdev->gain->is_new) {
- if (pdev->autogain->val)
- return -EBUSY;
+
+ if (pdev->autogain->val)
+ return 0;
+
+ if (pdev->gain->is_new) {
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
PRESET_AGC_FORMATTER,
pdev->gain->val);
+ if (ret)
+ return ret;
}
- if (ret == 0 && pdev->exposure->is_new) {
- if (pdev->autogain->val)
- return -EBUSY;
+
+ if (pdev->exposure->is_new) {
ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
PRESET_SHUTTER_FORMATTER,
pdev->exposure->val);
+ if (ret)
+ return ret;
}
- return ret;
+ return 0;
}
static int pwc_set_motor(struct pwc_device *pdev)
@@ -878,10 +861,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
pdev->autocontour->val ? 0 : 0xff);
}
if (ret == 0 && pdev->contour->is_new) {
- if (pdev->autocontour->val) {
- ret = -EBUSY;
- break;
- }
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
PRESET_CONTOUR_FORMATTER,
pdev->contour->val);
@@ -1099,6 +1078,14 @@ static int pwc_enum_frameintervals(struct file *file, void *fh,
return 0;
}
+static int pwc_log_status(struct file *file, void *priv)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME);
+ return 0;
+}
+
static long pwc_default(struct file *file, void *fh, bool valid_prio,
int cmd, void *arg)
{
@@ -1122,6 +1109,7 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_dqbuf = pwc_dqbuf,
.vidioc_streamon = pwc_streamon,
.vidioc_streamoff = pwc_streamoff,
+ .vidioc_log_status = pwc_log_status,
.vidioc_enum_framesizes = pwc_enum_framesizes,
.vidioc_enum_frameintervals = pwc_enum_frameintervals,
.vidioc_default = pwc_default,
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index d07df22a5ec6..79fb22c89ae9 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -214,6 +214,7 @@ struct pxa_camera_dev {
unsigned long ciclk;
unsigned long mclk;
u32 mclk_divisor;
+ u16 width_flags; /* max 10 bits */
struct list_head capture;
@@ -1020,37 +1021,20 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
* quick capture interface supports both.
*/
*flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
- SOCAM_MASTER : SOCAM_SLAVE) |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_PCLK_SAMPLE_FALLING;
+ V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING;
/* If requested data width is supported by the platform, use it */
- switch (buswidth) {
- case 10:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_10;
- break;
- case 9:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_9;
- break;
- case 8:
- if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
- return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_8;
- break;
- default:
- return -EINVAL;
- }
+ if ((1 << (buswidth - 1)) & pcdev->width_flags)
+ return 0;
- return 0;
+ return -EINVAL;
}
static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
@@ -1070,12 +1054,12 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
* Datawidth is now guaranteed to be equal to one of the three values.
* We fix bit-per-pixel equal to data-width...
*/
- switch (flags & SOCAM_DATAWIDTH_MASK) {
- case SOCAM_DATAWIDTH_10:
+ switch (icd->current_fmt->host_fmt->bits_per_sample) {
+ case 10:
dw = 4;
bpp = 0x40;
break;
- case SOCAM_DATAWIDTH_9:
+ case 9:
dw = 3;
bpp = 0x20;
break;
@@ -1084,7 +1068,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
* Actually it can only be 8 now,
* default is just to silence compiler warnings
*/
- case SOCAM_DATAWIDTH_8:
+ case 8:
dw = 2;
bpp = 0;
}
@@ -1093,11 +1077,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
cicr4 |= CICR4_PCLK_EN;
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
cicr4 |= CICR4_MCLK_EN;
- if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
cicr4 |= CICR4_PCP;
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cicr4 |= CICR4_HSP;
- if (flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
cicr4 |= CICR4_VSP;
cicr0 = __raw_readl(pcdev->base + CICR0);
@@ -1151,9 +1135,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- unsigned long bus_flags, camera_flags, common_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret;
struct pxa_cam *cam = icd->host_priv;
@@ -1162,44 +1148,58 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- if (!common_flags)
- return -EINVAL;
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
pcdev->channels = 1;
/* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & PXA_CAMERA_HSP)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & PXA_CAMERA_VSP)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (pcdev->platform_flags & PXA_CAMERA_PCP)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
- cam->flags = common_flags;
-
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
+
+ cam->flags = common_flags;
pxa_camera_setup_cicr(icd, common_flags, pixfmt);
@@ -1209,17 +1209,31 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
unsigned char buswidth)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- unsigned long bus_flags, camera_flags;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
int ret = test_platform_param(pcdev, buswidth, &bus_flags);
if (ret < 0)
return ret;
- camera_flags = icd->ops->query_bus_param(icd);
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret == -ENOIOCTLCMD) {
+ ret = 0;
+ }
- return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
+ return ret;
}
static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
@@ -1687,6 +1701,12 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
"data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
}
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
+ pcdev->width_flags = 1 << 7;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
+ pcdev->width_flags |= 1 << 8;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
+ pcdev->width_flags |= 1 << 9;
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
if (!pcdev->mclk) {
dev_warn(&pdev->dev,
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 847ccc067e87..6afc61689549 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -11,13 +11,14 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/rj54n1cb0c.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#define RJ54N1_DEV_CODE 0x0400
#define RJ54N1_DEV_CODE2 0x0401
@@ -148,6 +149,7 @@ struct rj54n1_clock_div {
struct rj54n1 {
struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
struct rj54n1_clock_div clk_div;
const struct rj54n1_datafmt *fmt;
struct v4l2_rect rect; /* Sensor window */
@@ -499,31 +501,6 @@ static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
}
-static int rj54n1_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
-
- if (flags & SOCAM_PCLK_SAMPLE_RISING)
- return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
- else
- return reg_write(client, RJ54N1_OUT_SIGPO, 0);
-}
-
-static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- const unsigned long flags =
- SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int rj54n1_set_rect(struct i2c_client *client,
u16 reg_x, u16 reg_y, u16 reg_xy,
u32 width, u32 height)
@@ -1202,134 +1179,51 @@ static int rj54n1_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl rj54n1_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 66,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto white balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
-};
-
-static struct soc_camera_ops rj54n1_ops = {
- .set_bus_param = rj54n1_set_bus_param,
- .query_bus_param = rj54n1_query_bus_param,
- .controls = rj54n1_controls,
- .num_controls = ARRAY_SIZE(rj54n1_controls),
-};
-
-static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
+ struct v4l2_subdev *sd = &rj54n1->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
- data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !(data & 1);
- break;
- case V4L2_CID_HFLIP:
- data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
- if (data < 0)
- return -EIO;
- ctrl->value = !(data & 2);
- break;
- case V4L2_CID_GAIN:
- data = reg_read(client, RJ54N1_Y_GAIN);
- if (data < 0)
- return -EIO;
-
- ctrl->value = data / 2;
- break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = rj54n1->auto_wb;
- break;
- }
-
- return 0;
-}
-
-static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- int data;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct v4l2_queryctrl *qctrl;
-
- qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
- if (!qctrl)
- return -EINVAL;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
else
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_HFLIP:
- if (ctrl->value)
+ if (ctrl->val)
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
else
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
if (data < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_GAIN:
- if (ctrl->value > qctrl->maximum ||
- ctrl->value < qctrl->minimum)
- return -EINVAL;
- else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
+ if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
return -EIO;
- break;
+ return 0;
case V4L2_CID_AUTO_WHITE_BALANCE:
/* Auto WB area - whole image */
- if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7,
+ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
0x80) < 0)
return -EIO;
- rj54n1->auto_wb = ctrl->value;
- break;
+ rj54n1->auto_wb = ctrl->val;
+ return 0;
}
- return 0;
+ return -EINVAL;
}
+static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
+ .s_ctrl = rj54n1_s_ctrl,
+};
+
static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
- .g_ctrl = rj54n1_g_ctrl,
- .s_ctrl = rj54n1_s_ctrl,
.g_chip_ident = rj54n1_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = rj54n1_g_register,
@@ -1337,6 +1231,36 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
#endif
};
+static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags =
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+ else
+ return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
.s_stream = rj54n1_s_stream,
.s_mbus_fmt = rj54n1_s_fmt,
@@ -1346,6 +1270,8 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
.g_crop = rj54n1_g_crop,
.s_crop = rj54n1_s_crop,
.cropcap = rj54n1_cropcap,
+ .g_mbus_config = rj54n1_g_mbus_config,
+ .s_mbus_config = rj54n1_s_mbus_config,
};
static struct v4l2_subdev_ops rj54n1_subdev_ops = {
@@ -1357,17 +1283,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = {
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int rj54n1_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client,
+static int rj54n1_video_probe(struct i2c_client *client,
struct rj54n1_pdata *priv)
{
int data1, data2;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/* Read out the chip version register */
data1 = reg_read(client, RJ54N1_DEV_CODE);
data2 = reg_read(client, RJ54N1_DEV_CODE2);
@@ -1395,18 +1316,11 @@ static int rj54n1_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct rj54n1 *rj54n1;
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
struct rj54n1_pdata *rj54n1_priv;
int ret;
- if (!icd) {
- dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
- return -EINVAL;
- }
-
- icl = to_soc_camera_link(icd);
if (!icl || !icl->priv) {
dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
return -EINVAL;
@@ -1425,8 +1339,22 @@ static int rj54n1_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+ v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 66);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
+ if (rj54n1->hdl.error) {
+ int err = rj54n1->hdl.error;
- icd->ops = &rj54n1_ops;
+ kfree(rj54n1);
+ return err;
+ }
rj54n1->clk_div = clk_div;
rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
@@ -1440,25 +1368,24 @@ static int rj54n1_probe(struct i2c_client *client,
rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
- ret = rj54n1_video_probe(icd, client, rj54n1_priv);
+ ret = rj54n1_video_probe(client, rj54n1_priv);
if (ret < 0) {
- icd->ops = NULL;
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
kfree(rj54n1);
return ret;
}
-
- return ret;
+ return v4l2_ctrl_handler_setup(&rj54n1->hdl);
}
static int rj54n1_remove(struct i2c_client *client)
{
struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- icd->ops = NULL;
+ v4l2_device_unregister_subdev(&rj54n1->subdev);
if (icl->free_bus)
icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
kfree(rj54n1);
return 0;
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
new file mode 100644
index 000000000000..2446736b7871
--- /dev/null
+++ b/drivers/media/video/s5k6aa.c
@@ -0,0 +1,1680 @@
+/*
+ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on a driver authored by Dongsoo Nathaniel Kim.
+ * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5k6aa.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DRIVER_NAME "S5K6AA"
+
+/* The token to indicate array termination */
+#define S5K6AA_TERM 0xffff
+#define S5K6AA_OUT_WIDTH_DEF 640
+#define S5K6AA_OUT_HEIGHT_DEF 480
+#define S5K6AA_WIN_WIDTH_MAX 1280
+#define S5K6AA_WIN_HEIGHT_MAX 1024
+#define S5K6AA_WIN_WIDTH_MIN 8
+#define S5K6AA_WIN_HEIGHT_MIN 8
+
+/*
+ * H/W register Interface (0xD0000000 - 0xD0000FFF)
+ */
+#define AHB_MSB_ADDR_PTR 0xfcfc
+#define GEN_REG_OFFSH 0xd000
+#define REG_CMDWR_ADDRH 0x0028
+#define REG_CMDWR_ADDRL 0x002a
+#define REG_CMDRD_ADDRH 0x002c
+#define REG_CMDRD_ADDRL 0x002e
+#define REG_CMDBUF0_ADDR 0x0f12
+#define REG_CMDBUF1_ADDR 0x0f10
+
+/*
+ * Host S/W Register interface (0x70000000 - 0x70002000)
+ * The value of the two most significant address bytes is 0x7000,
+ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
+ */
+#define HOST_SWIF_OFFSH 0x7000
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L 0x01b8
+#define REG_I_INCLK_FREQ_H 0x01ba
+#define MIN_MCLK_FREQ_KHZ 6000U
+#define MAX_MCLK_FREQ_KHZ 27000U
+#define REG_I_USE_NPVI_CLOCKS 0x01c6
+#define REG_I_USE_NMIPI_CLOCKS 0x01c8
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
+#define SYS_PLL_OUT_FREQ (48000000 / 4000)
+#define PCLK_FREQ_MIN (24000000 / 4000)
+#define PCLK_FREQ_MAX (48000000 / 4000)
+#define REG_I_INIT_PARAMS_UPDATED 0x01e0
+#define REG_I_ERROR_INFO 0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x01e4
+#define REG_USER_CONTRAST 0x01e6
+#define REG_USER_SATURATION 0x01e8
+#define REG_USER_SHARPBLUR 0x01ea
+
+#define REG_G_SPEC_EFFECTS 0x01ee
+#define REG_G_ENABLE_PREV 0x01f0
+#define REG_G_ENABLE_PREV_CHG 0x01f2
+#define REG_G_NEW_CFG_SYNC 0x01f8
+#define REG_G_PREVZOOM_IN_WIDTH 0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
+#define REG_G_PREVZOOM_IN_XOFFS 0x020e
+#define REG_G_PREVZOOM_IN_YOFFS 0x0210
+#define REG_G_INPUTS_CHANGE_REQ 0x021a
+#define REG_G_ACTIVE_PREV_CFG 0x021c
+#define REG_G_PREV_CFG_CHG 0x021e
+#define REG_G_PREV_OPEN_AFTER_CH 0x0220
+#define REG_G_PREV_CFG_ERROR 0x0222
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x) ((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
+#define REG_P_FMT(n) PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
+#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
+#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
+#define FR_RATE_DYNAMIC 0
+#define FR_RATE_FIXED 1
+#define FR_RATE_FIXED_ACCURATE 2
+#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
+#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
+#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
+/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
+#define US_TO_FR_TIME(__t) ((__t) / 100)
+#define S5K6AA_MIN_FR_TIME 33300 /* us */
+#define S5K6AA_MAX_FR_TIME 650000 /* us */
+#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L 0x03c6
+#define REG_SF_USR_EXPOSURE_H 0x03c8
+#define REG_SF_USR_EXPOSURE_CHG 0x03ca
+#define REG_SF_USR_TOT_GAIN 0x03cc
+#define REG_SF_USR_TOT_GAIN_CHG 0x03ce
+#define REG_SF_RGAIN 0x03d0
+#define REG_SF_RGAIN_CHG 0x03d2
+#define REG_SF_GGAIN 0x03d4
+#define REG_SF_GGAIN_CHG 0x03d6
+#define REG_SF_BGAIN 0x03d8
+#define REG_SF_BGAIN_CHG 0x03da
+#define REG_SF_FLICKER_QUANT 0x03dc
+#define REG_SF_FLICKER_QUANT_CHG 0x03de
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES 0x03fa
+#define REG_OIF_EN_PACKETS 0x03fc
+#define REG_OIF_CFG_CHG 0x03fe
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN 0x0400
+#define AALG_ALL_EN_MASK (1 << 0)
+#define AALG_AE_EN_MASK (1 << 1)
+#define AALG_DIVLEI_EN_MASK (1 << 2)
+#define AALG_WB_EN_MASK (1 << 3)
+#define AALG_FLICKER_EN_MASK (1 << 5)
+#define AALG_FIT_EN_MASK (1 << 6)
+#define AALG_WRHW_EN_MASK (1 << 7)
+
+/* Firmware revision information */
+#define REG_FW_APIVER 0x012e
+#define S5K6AAFX_FW_APIVER 0x0001
+#define REG_FW_REVISION 0x0130
+
+/* For now we use only one user configuration register set */
+#define S5K6AA_MAX_PRESETS 1
+
+static const char * const s5k6aa_supply_names[] = {
+ "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */
+ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
+ "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0) */
+ "vddio", /* I/O supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V) */
+};
+#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
+
+enum s5k6aa_gpio_id {
+ STBY,
+ RST,
+ GPIO_NUM,
+};
+
+struct s5k6aa_regval {
+ u16 addr;
+ u16 val;
+};
+
+struct s5k6aa_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_P_FMT(x) register value */
+ u16 reg_p_fmt;
+};
+
+struct s5k6aa_preset {
+ /* output pixel format and resolution */
+ struct v4l2_mbus_framefmt mbus_fmt;
+ u8 clk_id;
+ u8 index;
+};
+
+struct s5k6aa_ctrls {
+ struct v4l2_ctrl_handler handler;
+ /* Auto / manual white balance cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain_red;
+ struct v4l2_ctrl *gain_blue;
+ struct v4l2_ctrl *gain_green;
+ /* Mirror cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ /* Auto exposure / manual exposure and gain cluster */
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+};
+
+struct s5k6aa_interval {
+ u16 reg_fr_time;
+ struct v4l2_fract interval;
+ /* Maximum rectangle for the interval */
+ struct v4l2_frmsize_discrete size;
+};
+
+struct s5k6aa {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ enum v4l2_mbus_type bus_type;
+ u8 mipi_lanes;
+
+ int (*s_power)(int enable);
+ struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
+ struct s5k6aa_gpio gpio[GPIO_NUM];
+
+ /* external master clock frequency */
+ unsigned long mclk_frequency;
+ /* ISP internal master clock frequency */
+ u16 clk_fop;
+ /* output pixel clock frequency range */
+ u16 pclk_fmin;
+ u16 pclk_fmax;
+
+ unsigned int inv_hflip:1;
+ unsigned int inv_vflip:1;
+
+ /* protects the struct members below */
+ struct mutex lock;
+
+ /* sensor matrix scan window */
+ struct v4l2_rect ccd_rect;
+
+ struct s5k6aa_ctrls ctrls;
+ struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
+ struct s5k6aa_preset *preset;
+ const struct s5k6aa_interval *fiv;
+
+ unsigned int streaming:1;
+ unsigned int apply_cfg:1;
+ unsigned int apply_crop:1;
+ unsigned int power;
+};
+
+static struct s5k6aa_regval s5k6aa_analog_config[] = {
+ /* Analog settings */
+ { 0x112a, 0x0000 }, { 0x1132, 0x0000 },
+ { 0x113e, 0x0000 }, { 0x115c, 0x0000 },
+ { 0x1164, 0x0000 }, { 0x1174, 0x0000 },
+ { 0x1178, 0x0000 }, { 0x077a, 0x0000 },
+ { 0x077c, 0x0000 }, { 0x077e, 0x0000 },
+ { 0x0780, 0x0000 }, { 0x0782, 0x0000 },
+ { 0x0784, 0x0000 }, { 0x0786, 0x0000 },
+ { 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
+ { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
+ { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
+ { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
+ { 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
+ { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
+};
+
+/* TODO: Add RGB888 and Bayer format */
+static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ /* range 16-240 */
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+};
+
+static const struct s5k6aa_interval s5k6aa_intervals[] = {
+ { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
+ { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */
+ { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */
+ { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */
+ { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */
+};
+
+#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
+}
+
+static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6aa, sd);
+}
+
+/* Set initial values for all preview presets */
+static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
+{
+ struct s5k6aa_preset *preset = &s5k6aa->presets[0];
+ int i;
+
+ for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
+ preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF;
+ preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF;
+ preset->mbus_fmt.code = s5k6aa_formats[0].code;
+ preset->index = i;
+ preset->clk_id = 0;
+ preset++;
+ }
+
+ s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
+ s5k6aa->preset = &s5k6aa->presets[0];
+}
+
+static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ u8 wbuf[2] = {addr >> 8, addr & 0xFF};
+ struct i2c_msg msg[2];
+ u8 rbuf[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = wbuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rbuf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ *val = be16_to_cpu(*((u16 *)rbuf));
+
+ v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+ u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
+
+ int ret = i2c_master_send(client, buf, 4);
+ v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
+
+ return ret == 4 ? 0 : ret;
+}
+
+/* The command register write, assumes Command_Wr_addH = 0x7000. */
+static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
+{
+ int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
+}
+
+/* The command register read, assumes Command_Rd_addH = 0x7000. */
+static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
+}
+
+static int s5k6aa_write_array(struct v4l2_subdev *sd,
+ const struct s5k6aa_regval *msg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr_incr = 0;
+ int ret = 0;
+
+ while (msg->addr != S5K6AA_TERM) {
+ if (addr_incr != 2)
+ ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
+ msg->addr);
+ if (ret)
+ break;
+ ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
+ if (ret)
+ break;
+ /* Assume that msg->addr is always less than 0xfffc */
+ addr_incr = (msg + 1)->addr - msg->addr;
+ msg++;
+ }
+
+ return ret;
+}
+
+/* Configure the AHB high address bytes for GTG registers access */
+static int s5k6aa_set_ahb_address(struct i2c_client *client)
+{
+ int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+ if (ret)
+ return ret;
+ ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
+}
+
+/**
+ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
+ *
+ * Configure the internal ISP PLL for the required output frequency.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
+ u16 status;
+ int ret;
+
+ if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
+ "Invalid clock frequency: %ld\n", fmclk))
+ return -EINVAL;
+
+ s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
+ s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
+ s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
+
+ /* External input clock frequency in kHz */
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
+ /* Internal PLL frequency */
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
+ if (!ret)
+ ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
+
+ return ret ? ret : (status ? -EINVAL : 0);
+}
+
+/* Set horizontal and vertical image flipping */
+static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int index = s5k6aa->preset->index;
+
+ unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
+ unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
+
+ return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ u16 reg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
+
+ if (!ret && !awb) {
+ ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
+ }
+ if (!ret) {
+ reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
+ ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
+ }
+
+ return ret;
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
+{
+ unsigned int time = exposure / 10;
+
+ int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
+}
+
+static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
+{
+ int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned int exp_time = s5k6aa->ctrls.exposure->val;
+ u16 auto_alg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
+ exp_time, value, auto_alg);
+
+ if (value == V4L2_EXPOSURE_AUTO) {
+ auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
+ } else {
+ ret = s5k6aa_set_user_exposure(c, exp_time);
+ if (ret)
+ return ret;
+ ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
+ if (ret)
+ return ret;
+ auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
+ }
+
+ return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 auto_alg;
+ int ret;
+
+ ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+ auto_alg |= AALG_FLICKER_EN_MASK;
+ } else {
+ auto_alg &= ~AALG_FLICKER_EN_MASK;
+ /* The V4L2_CID_LINE_FREQUENCY control values match
+ * the register values */
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
+ if (ret)
+ return ret;
+ }
+
+ return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ static const struct v4l2_control colorfx[] = {
+ { V4L2_COLORFX_NONE, 0 },
+ { V4L2_COLORFX_BW, 1 },
+ { V4L2_COLORFX_NEGATIVE, 2 },
+ { V4L2_COLORFX_SEPIA, 3 },
+ { V4L2_COLORFX_SKY_BLUE, 4 },
+ { V4L2_COLORFX_SKETCH, 5 },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+ if (colorfx[i].id == val)
+ return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
+ colorfx[i].value);
+ }
+ return -EINVAL;
+}
+
+static int s5k6aa_preview_config_status(struct i2c_client *client)
+{
+ u16 error = 0;
+ int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
+
+ v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
+ return ret ? ret : (error ? -EINVAL : 0);
+}
+
+static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
+ if (mf->colorspace == s5k6aa_formats[i].colorspace &&
+ mf->code == s5k6aa_formats[i].code)
+ return i;
+ return 0;
+}
+
+static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
+ int ret;
+
+ ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
+ preset->mbus_fmt.width);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
+ preset->mbus_fmt.height);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FMT(preset->index),
+ s5k6aa_formats[fmt_index].reg_p_fmt);
+ return ret;
+}
+
+static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct v4l2_rect *r = &s5k6aa->ccd_rect;
+ int ret;
+
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
+ if (!ret)
+ s5k6aa->apply_crop = 0;
+
+ return ret;
+}
+
+/**
+ * s5k6aa_configure_video_bus - configure the video output interface
+ * @bus_type: video bus type: parallel or MIPI-CSI
+ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
+ *
+ * Note: Only parallel bus operation has been tested.
+ */
+static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
+ enum v4l2_mbus_type bus_type, int nlanes)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 cfg = 0;
+ int ret;
+
+ /*
+ * TODO: The sensor is supposed to support BT.601 and BT.656
+ * but there is nothing indicating how to switch between both
+ * in the datasheet. For now default BT.601 interface is assumed.
+ */
+ if (bus_type == V4L2_MBUS_CSI2)
+ cfg = nlanes;
+ else if (bus_type != V4L2_MBUS_PARALLEL)
+ return -EINVAL;
+
+ ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
+}
+
+/* This function should be called when switching to new user configuration set*/
+static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
+ int cid)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+ u16 reg = 1;
+ int ret;
+
+ ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
+ if (timeout == 0)
+ return ret;
+
+ while (ret >= 0 && time_is_after_jiffies(end)) {
+ ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
+ if (!reg)
+ return 0;
+ usleep_range(1000, 5000);
+ }
+ return ret ? ret : -ETIMEDOUT;
+}
+
+/**
+ * s5k6aa_set_prev_config - write user preview register set
+ *
+ * Configure output resolution and color fromat, pixel clock
+ * frequency range, device frame rate type and frame period range.
+ */
+static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int idx = preset->index;
+ u16 frame_rate_q;
+ int ret;
+
+ if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
+ frame_rate_q = FR_RATE_Q_BEST_FRRATE;
+ else
+ frame_rate_q = FR_RATE_Q_BEST_QUALITY;
+
+ ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
+ preset->clk_id);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
+ FR_RATE_DYNAMIC);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
+ frame_rate_q);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time + 33);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time - 33);
+ if (!ret)
+ ret = s5k6aa_new_config_sync(client, 250, idx);
+ if (!ret)
+ ret = s5k6aa_preview_config_status(client);
+ if (!ret)
+ s5k6aa->apply_cfg = 0;
+
+ v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
+ s5k6aa->fiv->reg_fr_time, ret);
+ return ret;
+}
+
+/**
+ * s5k6aa_initialize_isp - basic ISP MCU initialization
+ *
+ * Configure AHB addresses for registers read/write; configure PLLs for
+ * required output pixel clock. The ISP power supply needs to be already
+ * enabled, with an optional H/W reset.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ s5k6aa->apply_crop = 1;
+ s5k6aa->apply_cfg = 1;
+ msleep(100);
+
+ ret = s5k6aa_set_ahb_address(client);
+ if (ret)
+ return ret;
+ ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
+ s5k6aa->mipi_lanes);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
+ if (ret)
+ return ret;
+ msleep(20);
+
+ return s5k6aa_configure_pixel_clocks(s5k6aa);
+}
+
+static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
+{
+ if (!gpio_is_valid(priv->gpio[id].gpio))
+ return 0;
+ gpio_set_value(priv->gpio[id].gpio, !!val);
+ return 1;
+}
+
+static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
+}
+
+static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
+}
+
+static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ if (ret)
+ return ret;
+ if (s5k6aa_gpio_deassert(s5k6aa, STBY))
+ usleep_range(150, 200);
+
+ if (s5k6aa->s_power)
+ ret = s5k6aa->s_power(1);
+ usleep_range(4000, 4000);
+
+ if (s5k6aa_gpio_deassert(s5k6aa, RST))
+ msleep(20);
+
+ return ret;
+}
+
+static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ if (s5k6aa_gpio_assert(s5k6aa, RST))
+ usleep_range(100, 150);
+
+ if (s5k6aa->s_power) {
+ ret = s5k6aa->s_power(0);
+ if (ret)
+ return ret;
+ }
+ if (s5k6aa_gpio_assert(s5k6aa, STBY))
+ usleep_range(50, 100);
+ s5k6aa->streaming = 0;
+
+ return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (!on == s5k6aa->power) {
+ if (on) {
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret)
+ ret = s5k6aa_initialize_isp(sd);
+ } else {
+ ret = __s5k6aa_power_off(s5k6aa);
+ }
+
+ if (!ret)
+ s5k6aa->power += on ? 1 : -1;
+ }
+
+ mutex_unlock(&s5k6aa->lock);
+
+ if (!on || ret || s5k6aa->power != 1)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int ret = 0;
+
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
+ if (!ret)
+ s5k6aa->streaming = enable;
+
+ return ret;
+}
+
+static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (s5k6aa->streaming == !on) {
+ if (!ret && s5k6aa->apply_cfg)
+ ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
+ if (s5k6aa->apply_crop)
+ ret = s5k6aa_set_input_params(s5k6aa);
+ if (!ret)
+ ret = __s5k6aa_stream(s5k6aa, !!on);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ mutex_lock(&s5k6aa->lock);
+ fi->interval = s5k6aa->fiv->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
+ const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
+ unsigned int err, min_err = UINT_MAX;
+ unsigned int i, fr_time;
+
+ if (fi->interval.denominator == 0)
+ return -EINVAL;
+
+ fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
+ const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
+
+ if (mbus_fmt->width > iv->size.width ||
+ mbus_fmt->height > iv->size.height)
+ continue;
+
+ err = abs(iv->reg_fr_time - fr_time);
+ if (err < min_err) {
+ fiv = iv;
+ min_err = err;
+ }
+ }
+ s5k6aa->fiv = fiv;
+
+ v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
+ fiv->reg_fr_time * 100);
+ return 0;
+}
+
+static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
+ fi->interval.numerator, fi->interval.denominator);
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
+ s5k6aa->apply_cfg = 1;
+
+ mutex_unlock(&s5k6aa->lock);
+ return ret;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ const struct s5k6aa_interval *fi;
+ int ret = 0;
+
+ if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
+ return -EINVAL;
+
+ v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &fie->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ mutex_lock(&s5k6aa->lock);
+ fi = &s5k6aa_intervals[fie->index];
+ if (fie->width > fi->size.width || fie->height > fi->size.height)
+ ret = -EINVAL;
+ else
+ fie->interval = fi->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6aa_formats))
+ return -EINVAL;
+
+ code->code = s5k6aa_formats[code->index].code;
+ return 0;
+}
+
+static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int i = ARRAY_SIZE(s5k6aa_formats);
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ while (--i)
+ if (fse->code == s5k6aa_formats[i].code)
+ break;
+
+ fse->code = s5k6aa_formats[i].code;
+ fse->min_width = S5K6AA_WIN_WIDTH_MIN;
+ fse->max_width = S5K6AA_WIN_WIDTH_MAX;
+ fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
+ fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return &s5k6aa->ccd_rect;
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, 0);
+
+ return NULL;
+}
+
+static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int index;
+
+ v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &mf->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
+ mf->colorspace != V4L2_COLORSPACE_REC709)
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
+
+ mf->colorspace = s5k6aa_formats[index].colorspace;
+ mf->code = s5k6aa_formats[index].code;
+ mf->field = V4L2_FIELD_NONE;
+}
+
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ memset(fmt->reserved, 0, sizeof(fmt->reserved));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mutex_lock(&s5k6aa->lock);
+ fmt->format = s5k6aa->preset->mbus_fmt;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct s5k6aa_preset *preset = s5k6aa->preset;
+ struct v4l2_mbus_framefmt *mf;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+ s5k6aa_try_format(s5k6aa, &fmt->format);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ } else {
+ if (s5k6aa->streaming) {
+ ret = -EBUSY;
+ } else {
+ mf = &preset->mbus_fmt;
+ crop = &s5k6aa->ccd_rect;
+ s5k6aa->apply_cfg = 1;
+ }
+ }
+
+ if (ret == 0) {
+ struct v4l2_subdev_frame_interval fiv = {
+ .interval = {0, 1}
+ };
+
+ *mf = fmt->format;
+ /*
+ * Make sure the crop window is valid, i.e. its size is
+ * greater than the output window, as the ISP supports
+ * only down-scaling.
+ */
+ crop->width = clamp_t(unsigned int, crop->width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX);
+ crop->height = clamp_t(unsigned int, crop->height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX);
+ crop->left = clamp_t(unsigned int, crop->left, 0,
+ S5K6AA_WIN_WIDTH_MAX - crop->width);
+ crop->top = clamp_t(unsigned int, crop->top, 0,
+ S5K6AA_WIN_HEIGHT_MAX - crop->height);
+
+ /* Reset to minimum possible frame interval */
+ ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_rect *rect;
+
+ memset(crop->reserved, 0, sizeof(crop->reserved));
+ mutex_lock(&s5k6aa->lock);
+
+ rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+ if (rect)
+ crop->rect = *rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
+ rect->left, rect->top, rect->width, rect->height);
+
+ return 0;
+}
+
+static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int max_x, max_y;
+ struct v4l2_rect *crop_r;
+
+ mutex_lock(&s5k6aa->lock);
+ crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mf = &s5k6aa->preset->mbus_fmt;
+ s5k6aa->apply_crop = 1;
+ } else {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ }
+ v4l_bound_align_image(&crop->rect.width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &crop->rect.height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
+ max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+
+ crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
+ crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+
+ *crop_r = crop->rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
+ crop_r->left, crop_r->top, crop_r->width, crop_r->height);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
+ .enum_mbus_code = s5k6aa_enum_mbus_code,
+ .enum_frame_size = s5k6aa_enum_frame_size,
+ .enum_frame_interval = s5k6aa_enum_frame_interval,
+ .get_fmt = s5k6aa_get_fmt,
+ .set_fmt = s5k6aa_set_fmt,
+ .get_crop = s5k6aa_get_crop,
+ .set_crop = s5k6aa_set_crop,
+};
+
+static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
+ .g_frame_interval = s5k6aa_g_frame_interval,
+ .s_frame_interval = s5k6aa_s_frame_interval,
+ .s_stream = s5k6aa_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int idx, err = 0;
+
+ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+ mutex_lock(&s5k6aa->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (s5k6aa->power == 0)
+ goto unlock;
+ idx = s5k6aa->preset->index;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ err = s5k6aa_set_awb(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
+ break;
+
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+ }
+unlock:
+ mutex_unlock(&s5k6aa->lock);
+ return err;
+}
+
+static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
+ .s_ctrl = s5k6aa_s_ctrl,
+};
+
+static int s5k6aa_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+ return 0;
+}
+
+#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+
+static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
+ {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_RED_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_GREEN_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_BLUE_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ },
+};
+
+static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ int ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret)
+ return ret;
+ /* Auto white balance cluster */
+ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
+ ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
+ ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
+ v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
+
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ /* Exposure time: x 1 us */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 6000000U, 1, 100000U);
+ /* Total gain: 256 <=> 1x */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 256, 1, 256);
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 256, 1, 0);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ s5k6aa->sd.ctrl_handler = hdl;
+ return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+
+ format->colorspace = s5k6aa_formats[0].colorspace;
+ format->code = s5k6aa_formats[0].code;
+ format->width = S5K6AA_OUT_WIDTH_DEF;
+ format->height = S5K6AA_OUT_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+
+ crop->width = S5K6AA_WIN_WIDTH_MAX;
+ crop->height = S5K6AA_WIN_HEIGHT_MAX;
+ crop->left = 0;
+ crop->top = 0;
+
+ return 0;
+}
+
+int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 api_ver = 0, fw_rev = 0;
+
+ int ret = s5k6aa_set_ahb_address(client);
+
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
+ if (ret) {
+ v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
+ return ret;
+ }
+
+ v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
+ api_ver, fw_rev);
+
+ return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
+}
+
+static int s5k6aa_registered(struct v4l2_subdev *sd)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret) {
+ msleep(100);
+ ret = s5k6aa_check_fw_revision(s5k6aa);
+ __s5k6aa_power_off(s5k6aa);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
+ .registered = s5k6aa_registered,
+ .open = s5k6aa_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
+ .s_power = s5k6aa_set_power,
+ .log_status = s5k6aa_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
+ .core = &s5k6aa_core_ops,
+ .pad = &s5k6aa_pad_ops,
+ .video = &s5k6aa_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k6aa_configure_gpio(int nr, int val, const char *name)
+{
+ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ int ret;
+
+ if (!gpio_is_valid(nr))
+ return 0;
+ ret = gpio_request_one(nr, flags, name);
+ if (!ret)
+ gpio_export(nr, 0);
+ return ret;
+}
+
+static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
+ if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
+ continue;
+ gpio_free(s5k6aa->gpio[i].gpio);
+ s5k6aa->gpio[i].gpio = -EINVAL;
+ }
+}
+
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
+ const struct s5k6aa_platform_data *pdata)
+{
+ const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+ int ret;
+
+ s5k6aa->gpio[STBY].gpio = -EINVAL;
+ s5k6aa->gpio[RST].gpio = -EINVAL;
+
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[STBY] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ gpio = &pdata->gpio_reset;
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[RST] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ return 0;
+}
+
+static int s5k6aa_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_subdev *sd;
+ struct s5k6aa *s5k6aa;
+ int i, ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->mclk_frequency == 0) {
+ dev_err(&client->dev, "MCLK frequency not specified\n");
+ return -EINVAL;
+ }
+
+ s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
+ if (!s5k6aa)
+ return -ENOMEM;
+
+ mutex_init(&s5k6aa->lock);
+
+ s5k6aa->mclk_frequency = pdata->mclk_frequency;
+ s5k6aa->bus_type = pdata->bus_type;
+ s5k6aa->mipi_lanes = pdata->nlanes;
+ s5k6aa->s_power = pdata->set_power;
+ s5k6aa->inv_hflip = pdata->horiz_flip;
+ s5k6aa->inv_vflip = pdata->vert_flip;
+
+ sd = &s5k6aa->sd;
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+ v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+
+ sd->internal_ops = &s5k6aa_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+ if (ret)
+ goto out_err1;
+
+ ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+ if (ret)
+ goto out_err2;
+
+ for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
+ s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
+
+ ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
+ s5k6aa->supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators\n");
+ goto out_err3;
+ }
+
+ ret = s5k6aa_initialize_ctrls(s5k6aa);
+ if (ret)
+ goto out_err4;
+
+ s5k6aa_presets_data_init(s5k6aa);
+
+ s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
+ s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX;
+ s5k6aa->ccd_rect.left = 0;
+ s5k6aa->ccd_rect.top = 0;
+
+ return 0;
+
+out_err4:
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+out_err3:
+ s5k6aa_free_gpios(s5k6aa);
+out_err2:
+ media_entity_cleanup(&s5k6aa->sd.entity);
+out_err1:
+ kfree(s5k6aa);
+ return ret;
+}
+
+static int s5k6aa_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ s5k6aa_free_gpios(s5k6aa);
+ kfree(s5k6aa);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k6aa_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
+
+
+static struct i2c_driver s5k6aa_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME
+ },
+ .probe = s5k6aa_probe,
+ .remove = s5k6aa_remove,
+ .id_table = s5k6aa_id,
+};
+
+static int __init s5k6aa_init(void)
+{
+ return i2c_add_driver(&s5k6aa_i2c_driver);
+}
+
+static void __exit s5k6aa_exit(void)
+{
+ i2c_del_driver(&s5k6aa_i2c_driver);
+}
+
+module_init(s5k6aa_init);
+module_exit(s5k6aa_exit);
+
+MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
index df6954ab1d99..33dec7f890e7 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -1,4 +1,4 @@
-s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o
+s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o
s5p-csis-objs := mipi-csis.o
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 0d730e55605d..c8d91b0cd9bd 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -16,11 +16,9 @@
#include <linux/bug.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
@@ -29,135 +27,88 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+#include "fimc-mdevice.h"
#include "fimc-core.h"
-static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *isp_info)
+static int fimc_init_capture(struct fimc_dev *fimc)
{
- struct i2c_adapter *i2c_adap;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sd = NULL;
-
- i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num);
- if (!i2c_adap)
- return ERR_PTR(-ENOMEM);
-
- sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap,
- isp_info->board_info, NULL);
- if (!sd) {
- v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n");
- return NULL;
- }
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+ int ret = 0;
- v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n",
- isp_info->board_info->type);
+ if (fimc->pipeline.sensor == NULL || ctx == NULL)
+ return -ENXIO;
+ if (ctx->s_frame.fmt == NULL)
+ return -EINVAL;
- return sd;
-}
+ sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor);
-static void fimc_subdev_unregister(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct i2c_client *client;
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_set_yuv_order(ctx);
- if (vid_cap->input_index < 0)
- return; /* Subdevice already released or not registered. */
+ fimc_hw_set_camera_polarity(fimc, sensor->pdata);
+ fimc_hw_set_camera_type(fimc, sensor->pdata);
+ fimc_hw_set_camera_source(fimc, sensor->pdata);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
- if (vid_cap->sd) {
- v4l2_device_unregister_subdev(vid_cap->sd);
- client = v4l2_get_subdevdata(vid_cap->sd);
- i2c_unregister_device(client);
- i2c_put_adapter(client->adapter);
- vid_cap->sd = NULL;
+ ret = fimc_set_scaler_info(ctx);
+ if (!ret) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx, false);
+ fimc_hw_set_output_path(ctx);
+ fimc_hw_set_out_dma(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
-
- vid_cap->input_index = -1;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
}
-/**
- * fimc_subdev_attach - attach v4l2_subdev to camera host interface
- *
- * @fimc: FIMC device information
- * @index: index to the array of available subdevices,
- * -1 for full array search or non negative value
- * to select specific subdevice
- */
-static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct s5p_platform_fimc *pdata = fimc->pdata;
- struct s5p_fimc_isp_info *isp_info;
- struct v4l2_subdev *sd;
- int i;
-
- for (i = 0; i < pdata->num_clients; ++i) {
- isp_info = &pdata->isp_info[i];
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ unsigned long flags;
+ bool streaming;
- if (index >= 0 && i != index)
- continue;
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
- sd = fimc_subdev_register(fimc, isp_info);
- if (!IS_ERR_OR_NULL(sd)) {
- vid_cap->sd = sd;
- vid_cap->input_index = i;
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (!suspend)
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
- return 0;
- }
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
+ buf = fimc_pending_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&cap->active_buf_q)) {
+ buf = fimc_active_queue_pop(cap);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ set_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
- vid_cap->input_index = -1;
- vid_cap->sd = NULL;
- v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n",
- fimc->id);
- return -ENODEV;
-}
-
-static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index)
-{
- struct s5p_fimc_isp_info *isp_info;
- struct s5p_platform_fimc *pdata = fimc->pdata;
- int ret;
-
- if (index >= pdata->num_clients)
- return -EINVAL;
-
- isp_info = &pdata->isp_info[index];
-
- if (isp_info->clk_frequency)
- clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency);
-
- ret = clk_enable(fimc->clock[CLK_CAM]);
- if (ret)
- return ret;
-
- ret = fimc_subdev_attach(fimc, index);
- if (ret)
- return ret;
-
- ret = fimc_hw_set_camera_polarity(fimc, isp_info);
- if (ret)
- return ret;
-
- ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1);
- if (!ret)
- return ret;
-
- /* enabling power failed so unregister subdev */
- fimc_subdev_unregister(fimc);
-
- v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n",
- ret);
-
- return ret;
+ if (streaming)
+ return fimc_pipeline_s_stream(fimc, 0);
+ else
+ return 0;
}
-static int fimc_stop_capture(struct fimc_dev *fimc)
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
{
unsigned long flags;
- struct fimc_vid_cap *cap;
- struct fimc_vid_buffer *buf;
-
- cap = &fimc->vid_cap;
if (!fimc_capture_active(fimc))
return 0;
@@ -169,81 +120,73 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
wait_event_timeout(fimc->irq_queue,
!test_bit(ST_CAPT_SHUT, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
-
- v4l2_subdev_call(cap->sd, video, s_stream, 0);
+ (2*HZ/10)); /* 200 ms */
- spin_lock_irqsave(&fimc->slock, flags);
- fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
- 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM);
+ return fimc_capture_state_cleanup(fimc, suspend);
+}
- fimc->vid_cap.active_buf_cnt = 0;
+/**
+ * fimc_capture_config_update - apply the camera interface configuration
+ *
+ * To be called from within the interrupt handler with fimc.slock
+ * spinlock held. It updates the camera pixel crop, rotation and
+ * image flip in H/W.
+ */
+int fimc_capture_config_update(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
- /* Release buffers that were enqueued in the driver by videobuf2. */
- while (!list_empty(&cap->pending_buf_q)) {
- buf = pending_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
+ if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ return 0;
- while (!list_empty(&cap->active_buf_q)) {
- buf = active_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ spin_lock(&ctx->slock);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret == 0) {
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_hw_set_out_dma(ctx);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
-
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- dbg("state: 0x%lx", fimc->state);
- return 0;
+ spin_unlock(&ctx->slock);
+ return ret;
}
-static int start_streaming(struct vb2_queue *q)
+static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_ctx *ctx = q->drv_priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct s5p_fimc_isp_info *isp_info;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int min_bufs;
int ret;
fimc_hw_reset(fimc);
+ vid_cap->frame_count = 0;
- ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD)
- return ret;
-
- ret = fimc_prepare_config(ctx, ctx->state);
+ ret = fimc_init_capture(fimc);
if (ret)
- return ret;
-
- isp_info = &fimc->pdata->isp_info[fimc->vid_cap.input_index];
- fimc_hw_set_camera_type(fimc, isp_info);
- fimc_hw_set_camera_source(fimc, isp_info);
- fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+ goto error;
- if (ctx->state & FIMC_PARAMS) {
- ret = fimc_set_scaler_info(ctx);
- if (ret) {
- err("Scaler setup error");
- return ret;
- }
- fimc_hw_set_input_path(ctx);
- fimc_hw_set_prescaler(ctx);
- fimc_hw_set_mainscaler(ctx);
- fimc_hw_set_target_format(ctx);
- fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx);
- }
+ set_bit(ST_CAPT_PEND, &fimc->state);
- fimc_hw_set_output_path(ctx);
- fimc_hw_set_out_dma(ctx);
+ min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
- INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q);
- INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
- fimc->vid_cap.active_buf_cnt = 0;
- fimc->vid_cap.frame_count = 0;
- fimc->vid_cap.buf_index = 0;
+ if (vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
- set_bit(ST_CAPT_PEND, &fimc->state);
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ }
return 0;
+error:
+ fimc_capture_state_cleanup(fimc, false);
+ return ret;
}
static int stop_streaming(struct vb2_queue *q)
@@ -254,7 +197,46 @@ static int stop_streaming(struct vb2_queue *q)
if (!fimc_capture_active(fimc))
return -EINVAL;
- return fimc_stop_capture(fimc);
+ return fimc_stop_capture(fimc, false);
+}
+
+int fimc_capture_suspend(struct fimc_dev *fimc)
+{
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_shutdown(fimc);
+}
+
+static void buffer_queue(struct vb2_buffer *vb);
+
+int fimc_capture_resume(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
+
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ false);
+ fimc_init_capture(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+
}
static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
@@ -264,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
}
-static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *allocators[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
{
struct fimc_ctx *ctx = vq->drv_priv;
struct fimc_fmt *fmt = ctx->d_frame.fmt;
@@ -289,21 +271,20 @@ static int buffer_prepare(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct fimc_ctx *ctx = vq->drv_priv;
- struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
int i;
- if (!ctx->d_frame.fmt || vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (ctx->d_frame.fmt == NULL)
return -EINVAL;
for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
- unsigned long size = get_plane_size(&ctx->d_frame, i);
+ unsigned long size = ctx->d_frame.payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n",
+ v4l2_err(ctx->fimc_dev->vid_cap.vfd,
+ "User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
}
-
vb2_set_plane_payload(vb, i, size);
}
@@ -312,10 +293,10 @@ static int buffer_prepare(struct vb2_buffer *vb)
static void buffer_queue(struct vb2_buffer *vb)
{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_buffer *buf
= container_of(vb, struct fimc_vid_buffer, vb);
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
unsigned long flags;
int min_bufs;
@@ -323,15 +304,16 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
- if (!test_bit(ST_CAPT_STREAM, &fimc->state)
- && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
/* Setup the buffer directly for processing. */
int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
vid_cap->buf_index;
fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
buf->index = vid_cap->buf_index;
- active_queue_add(vid_cap, buf);
+ fimc_active_queue_add(vid_cap, buf);
if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
vid_cap->buf_index = 0;
@@ -341,10 +323,17 @@ static void buffer_queue(struct vb2_buffer *vb)
min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
- if (vid_cap->active_buf_cnt >= min_bufs &&
- !test_and_set_bit(ST_CAPT_STREAM, &fimc->state))
+
+ if (vb2_is_streaming(&vid_cap->vbq) &&
+ vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
fimc_activate_capture(ctx);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ return;
+ }
spin_unlock_irqrestore(&fimc->slock, flags);
}
@@ -370,10 +359,40 @@ static struct vb2_ops fimc_capture_qops = {
.stop_streaming = stop_streaming,
};
+/**
+ * fimc_capture_ctrls_create - initialize the control handler
+ * Initialize the capture video node control handler and fill it
+ * with the FIMC controls. Inherit any sensor's controls if the
+ * 'user_subdev_api' flag is false (default behaviour).
+ * This function need to be called with the graph mutex held.
+ */
+int fimc_capture_ctrls_create(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int ret;
+
+ if (WARN_ON(vid_cap->ctx == NULL))
+ return -ENXIO;
+ if (vid_cap->ctx->ctrls_rdy)
+ return 0;
+
+ ret = fimc_ctrls_create(vid_cap->ctx);
+ if (ret || vid_cap->user_subdev_api)
+ return ret;
+
+ return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler,
+ fimc->pipeline.sensor->ctrl_handler);
+}
+
+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 = 0;
+ int ret = v4l2_fh_open(file);
+
+ if (ret)
+ return ret;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
@@ -381,17 +400,27 @@ static int fimc_capture_open(struct file *file)
if (fimc_m2m_active(fimc))
return -EBUSY;
+ set_bit(ST_CAPT_BUSY, &fimc->state);
+ pm_runtime_get_sync(&fimc->pdev->dev);
+
if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_isp_subdev_init(fimc, 0);
- if (ret) {
+ ret = fimc_pipeline_initialize(fimc,
+ &fimc->vid_cap.vfd->entity, true);
+ if (ret < 0) {
+ dev_err(&fimc->pdev->dev,
+ "Video pipeline initialization failed\n");
+ pm_runtime_put_sync(&fimc->pdev->dev);
fimc->vid_cap.refcnt--;
- return -EIO;
+ v4l2_fh_release(file);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ return ret;
}
- }
-
- file->private_data = fimc->vid_cap.ctx;
+ ret = fimc_capture_ctrls_create(fimc);
- return 0;
+ if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_capture_set_default_format(fimc);
+ }
+ return ret;
}
static int fimc_capture_close(struct file *file)
@@ -401,37 +430,36 @@ static int fimc_capture_close(struct file *file)
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) {
- fimc_stop_capture(fimc);
- vb2_queue_release(&fimc->vid_cap.vbq);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
+ fimc_pipeline_shutdown(fimc);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ }
- v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n");
+ pm_runtime_put(&fimc->pdev->dev);
- v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
- clk_disable(fimc->clock[CLK_CAM]);
- fimc_subdev_unregister(fimc);
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
}
-
- return 0;
+ return v4l2_fh_release(file);
}
static unsigned int fimc_capture_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
return vb2_poll(&fimc->vid_cap.vbq, file, wait);
}
static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
return vb2_mmap(&fimc->vid_cap.vbq, vma);
}
-/* video device file operations */
static const struct v4l2_file_operations fimc_capture_fops = {
.owner = THIS_MODULE,
.open = fimc_capture_open,
@@ -441,263 +469,553 @@ static const struct v4l2_file_operations fimc_capture_fops = {
.mmap = fimc_capture_mmap,
};
+/*
+ * Format and crop negotiation helpers
+ */
+
+static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *dst = &ctx->d_frame;
+ u32 depth, min_w, max_w, min_h, align_h = 3;
+ u32 mask = FMT_FLAGS_CAM;
+ struct fimc_fmt *ffmt;
+
+ /* Color conversion from/to JPEG is not supported */
+ if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
+ fimc_fmt_is_jpeg(ctx->s_frame.fmt->color))
+ *code = V4L2_MBUS_FMT_JPEG_1X8;
+
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ mask |= FMT_FLAGS_M2M;
+
+ ffmt = fimc_find_format(fourcc, code, mask, 0);
+ if (WARN_ON(!ffmt))
+ return NULL;
+ if (code)
+ *code = ffmt->mbus_code;
+ if (fourcc)
+ *fourcc = ffmt->fourcc;
+
+ if (pad == FIMC_SD_PAD_SINK) {
+ max_w = fimc_fmt_is_jpeg(ffmt->color) ?
+ pl->scaler_dis_w : pl->scaler_en_w;
+ /* Apply the camera input interface pixel constraints */
+ v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
+ height, max_t(u32, *height, 32),
+ FIMC_CAMIF_MAX_HEIGHT,
+ fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1,
+ 0);
+ return ffmt;
+ }
+ /* Can't scale or crop in transparent (JPEG) transfer mode */
+ if (fimc_fmt_is_jpeg(ffmt->color)) {
+ *width = ctx->s_frame.f_width;
+ *height = ctx->s_frame.f_height;
+ return ffmt;
+ }
+ /* Apply the scaler and the output DMA constraints */
+ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
+ min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize;
+ min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize;
+ if (fimc->id == 1 && var->pix_hoff)
+ align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
+
+ depth = fimc_get_format_depth(ffmt);
+ v4l_bound_align_image(width, min_w, max_w,
+ ffs(var->min_out_pixsize) - 1,
+ height, min_h, FIMC_CAMIF_MAX_HEIGHT,
+ align_h,
+ 64/(ALIGN(depth, 8)));
+
+ dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
+ pad, code ? *code : 0, *width, *height,
+ dst->f_width, dst->f_height);
+
+ return ffmt;
+}
+
+static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
+ int pad)
+{
+ bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *sink = &ctx->s_frame;
+ u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
+ u32 align_sz = 0, align_h = 4;
+ u32 max_sc_h, max_sc_v;
+
+ /* In JPEG transparent transfer mode cropping is not supported */
+ if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) {
+ r->width = sink->f_width;
+ r->height = sink->f_height;
+ r->left = r->top = 0;
+ return;
+ }
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ if (ctx->rotation != 90 && ctx->rotation != 270)
+ align_h = 1;
+ max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
+ max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
+ min_sz = var->min_out_pixsize;
+ } else {
+ u32 depth = fimc_get_format_depth(sink->fmt);
+ align_sz = 64/ALIGN(depth, 8);
+ min_sz = var->min_inp_pixsize;
+ min_w = min_h = min_sz;
+ max_sc_h = max_sc_v = 1;
+ }
+ /*
+ * For the crop rectangle at source pad the following constraints
+ * must be met:
+ * - it must fit in the sink pad format rectangle (f_width/f_height);
+ * - maximum downscaling ratio is 64;
+ * - maximum crop size depends if the rotator is used or not;
+ * - the sink pad format width/height must be 4 multiple of the
+ * prescaler ratios determined by sink pad size and source pad crop,
+ * the prescaler ratio is returned by fimc_get_scaler_factor().
+ */
+ max_w = min_t(u32,
+ rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
+ rotate ? sink->f_height : sink->f_width);
+ max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ 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) {
+ swap(max_sc_h, max_sc_v);
+ swap(min_w, min_h);
+ }
+ }
+ v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
+ &r->height, min_h, max_h, align_h,
+ align_sz);
+ /* Adjust left/top if cropping rectangle is out of bounds */
+ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
+ r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
+ r->left = round_down(r->left, var->hor_offs_align);
+
+ dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ pad, r->left, r->top, r->width, r->height,
+ sink->f_width, sink->f_height);
+}
+
+/*
+ * The video node ioctl operations
+ */
static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
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 | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
-/* Synchronize formats of the camera interface input and attached sensor. */
-static int sync_capture_fmt(struct fimc_ctx *ctx)
+static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
+ * elements
+ * @ctx: FIMC capture context
+ * @tfmt: media bus format to try/set on subdevs
+ * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
+ * @set: true to set format on subdevs, false to try only
+ */
+static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
+ struct v4l2_mbus_framefmt *tfmt,
+ struct fimc_fmt **fmt_id,
+ bool set)
{
- struct fimc_frame *frame = &ctx->s_frame;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_mbus_framefmt *fmt = &fimc->vid_cap.fmt;
- int ret;
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ struct v4l2_subdev *csis = fimc->pipeline.csis;
+ struct v4l2_subdev_format sfmt;
+ struct v4l2_mbus_framefmt *mf = &sfmt.format;
+ struct fimc_fmt *ffmt = NULL;
+ int ret, i = 0;
+
+ if (WARN_ON(!sd || !tfmt))
+ return -EINVAL;
- fmt->width = ctx->d_frame.o_width;
- fmt->height = ctx->d_frame.o_height;
+ memset(&sfmt, 0, sizeof(sfmt));
+ sfmt.format = *tfmt;
+
+ sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
+ while (1) {
+ ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
+ FMT_FLAGS_CAM, i++);
+ if (ffmt == NULL) {
+ /*
+ * Notify user-space if common pixel code for
+ * host and sensor does not exist.
+ */
+ return -EINVAL;
+ }
+ mf->code = tfmt->code = ffmt->mbus_code;
- ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_mbus_fmt, fmt);
- if (ret == -ENOIOCTLCMD) {
- err("s_mbus_fmt failed");
- return ret;
- }
- dbg("w: %d, h: %d, code= %d", fmt->width, fmt->height, fmt->code);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ if (ret)
+ return ret;
+ if (mf->code != tfmt->code) {
+ mf->code = 0;
+ continue;
+ }
+ if (mf->width != tfmt->width || mf->width != tfmt->width) {
+ u32 fcc = ffmt->fourcc;
+ tfmt->width = mf->width;
+ tfmt->height = mf->height;
+ ffmt = fimc_capture_try_format(ctx,
+ &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SOURCE);
+ if (ffmt && ffmt->mbus_code)
+ mf->code = ffmt->mbus_code;
+ if (mf->width != tfmt->width || mf->width != tfmt->width)
+ continue;
+ tfmt->code = mf->code;
+ }
+ if (csis)
+ ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
- frame->fmt = find_mbus_format(fmt, FMT_FLAGS_CAM);
- if (!frame->fmt) {
- err("fimc source format not found\n");
- return -EINVAL;
+ if (mf->code == tfmt->code &&
+ mf->width == tfmt->width && mf->width == tfmt->width)
+ break;
}
- frame->f_width = fmt->width;
- frame->f_height = fmt->height;
- frame->width = fmt->width;
- frame->height = fmt->height;
- frame->o_width = fmt->width;
- frame->o_height = fmt->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ if (fmt_id && ffmt)
+ *fmt_id = ffmt;
+ *tfmt = *mf;
+ dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
return 0;
}
-static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pix;
- int ret;
- int i;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
- if (ret)
- return ret;
-
- if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
- return -EBUSY;
+ return fimc_fill_format(&ctx->d_frame, f);
+}
- frame = &ctx->d_frame;
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt mf;
+ struct fimc_fmt *ffmt = NULL;
- pix = &f->fmt.pix_mp;
- frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM);
- if (!frame->fmt) {
- err("fimc target format not found\n");
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
}
+ ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ffmt)
+ return -EINVAL;
- for (i = 0; i < frame->fmt->colplanes; i++) {
- frame->payload[i] =
- (pix->width * pix->height * frame->fmt->depth[i]) >> 3;
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.code = ffmt->mbus_code;
+ fimc_md_graph_lock(fimc);
+ fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
+ fimc_md_graph_unlock(fimc);
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ if (ffmt)
+ pix->pixelformat = ffmt->fourcc;
}
- /* Output DMA frame pixel size and offsets. */
- frame->f_width = pix->plane_fmt[0].bytesperline * 8
- / frame->fmt->depth[0];
- frame->f_height = pix->height;
- frame->width = pix->width;
- frame->height = pix->height;
- frame->o_width = pix->width;
- frame->o_height = pix->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ return 0;
+}
- ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT);
+static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
+{
+ ctx->scaler.enabled = !jpeg;
+ fimc_ctrls_activate(ctx, !jpeg);
- ret = sync_capture_fmt(ctx);
- return ret;
+ if (jpeg)
+ set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+ else
+ clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
}
-static int fimc_cap_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
+static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata;
- struct s5p_fimc_isp_info *isp_info;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
+ struct fimc_frame *ff = &ctx->d_frame;
+ struct fimc_fmt *s_fmt = NULL;
+ int ret, i;
- if (i->index >= pldata->num_clients)
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
+ if (vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
- isp_info = &pldata->isp_info[i->index];
+ /* Pre-configure format at camera interface input, for JPEG only */
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ /* Try the format at the scaler and the DMA output */
+ ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ff->fmt)
+ return -EINVAL;
+ /* Try to match format at the host and the sensor */
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf->code = ff->fmt->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(fimc);
+ ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
+ fimc_md_graph_unlock(fimc);
+ if (ret)
+ return ret;
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+ fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
+ for (i = 0; i < ff->fmt->colplanes; i++)
+ ff->payload[i] =
+ (pix->width * pix->height * ff->fmt->depth[i]) / 8;
+
+ set_frame_bounds(ff, pix->width, pix->height);
+ /* Reset the composition rectangle if not yet configured */
+ if (!(ctx->state & FIMC_DST_CROP))
+ set_frame_crop(ff, 0, 0, pix->width, pix->height);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
+
+ /* Reset cropping and set format at the camera interface input */
+ if (!fimc->vid_cap.user_subdev_api) {
+ ctx->s_frame.fmt = s_fmt;
+ set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
+ set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
+ }
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strncpy(i->name, isp_info->board_info->type, 32);
- return 0;
+ return ret;
}
-static int fimc_cap_s_input(struct file *file, void *priv,
- unsigned int i)
+static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct s5p_platform_fimc *pdata = fimc->pdata;
-
- if (fimc_capture_active(ctx->fimc_dev))
- return -EBUSY;
+ struct fimc_dev *fimc = video_drvdata(file);
- if (i >= pdata->num_clients)
- return -EINVAL;
+ return fimc_capture_set_format(fimc, f);
+}
+static int fimc_cap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
- if (fimc->vid_cap.sd) {
- int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
- if (ret)
- err("s_power failed: %d", ret);
+ if (i->index != 0)
+ return -EINVAL;
- clk_disable(fimc->clock[CLK_CAM]);
- }
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ if (sd)
+ strlcpy(i->name, sd->name, sizeof(i->name));
+ return 0;
+}
- /* Release the attached sensor subdevice. */
- fimc_subdev_unregister(fimc);
+static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i == 0 ? i : -EINVAL;
+}
- return fimc_isp_subdev_init(fimc, i);
+static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
}
-static int fimc_cap_g_input(struct file *file, void *priv,
- unsigned int *i)
+/**
+ * fimc_pipeline_validate - check for formats inconsistencies
+ * between source and sink pad of each link
+ *
+ * Return 0 if all formats match or -EPIPE otherwise.
+ */
+static int fimc_pipeline_validate(struct fimc_dev *fimc)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ int ret;
- *i = cap->input_index;
+ /* Start with the video capture node pad */
+ pad = media_entity_remote_source(&vid_cap->vd_pad);
+ if (pad == NULL)
+ return -EPIPE;
+ /* FIMC.{N} subdevice */
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == fimc->vid_cap.subdev) {
+ struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
return 0;
}
static int fimc_cap_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret;
- if (fimc_capture_active(fimc) || !fimc->vid_cap.sd)
+ if (fimc_capture_active(fimc))
return -EBUSY;
- if (!(ctx->state & FIMC_DST_FMT)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n");
- return -EINVAL;
- }
+ media_entity_pipeline_start(&p->sensor->entity, p->pipe);
+ if (fimc->vid_cap.user_subdev_api) {
+ ret = fimc_pipeline_validate(fimc);
+ if (ret)
+ return ret;
+ }
return vb2_streamon(&fimc->vid_cap.vbq, type);
}
static int fimc_cap_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ int ret;
- return vb2_streamoff(&fimc->vid_cap.vbq, type);
+ ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
}
static int fimc_cap_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
- int ret;
-
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
- ret = vb2_reqbufs(&cap->vbq, reqbufs);
if (!ret)
- cap->reqbufs_count = reqbufs->count;
-
+ fimc->vid_cap.reqbufs_count = reqbufs->count;
return ret;
}
static int fimc_cap_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+ struct fimc_dev *fimc = video_drvdata(file);
- return vb2_querybuf(&cap->vbq, buf);
+ return vb2_querybuf(&fimc->vid_cap.vbq, buf);
}
static int fimc_cap_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
- return vb2_qbuf(&cap->vbq, buf);
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vid_cap.vbq, buf);
}
static int fimc_cap_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
- return vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf,
- file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_cap_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- int ret = -EINVAL;
+ struct fimc_dev *fimc = video_drvdata(file);
- /* Allow any controls but 90/270 rotation while streaming */
- if (!fimc_capture_active(ctx->fimc_dev) ||
- ctrl->id != V4L2_CID_ROTATE ||
- (ctrl->value != 90 && ctrl->value != 270)) {
- ret = check_ctrl_val(ctx, ctrl);
- if (!ret) {
- ret = fimc_s_ctrl(ctx, ctrl);
- if (!ret)
- ctx->state |= FIMC_PARAMS;
- }
- }
- if (ret == -EINVAL)
- ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
- core, s_ctrl, ctrl);
- return ret;
+ return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
}
static int fimc_cap_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = fh;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- f = &ctx->s_frame;
-
cr->bounds.left = 0;
cr->bounds.top = 0;
cr->bounds.width = f->o_width;
@@ -709,10 +1027,8 @@ static int fimc_cap_cropcap(struct file *file, void *fh,
static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = file->private_data;
-
- f = &ctx->s_frame;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
cr->c.left = f->offs_h;
cr->c.top = f->offs_v;
@@ -722,53 +1038,31 @@ static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
return 0;
}
-static int fimc_cap_s_crop(struct file *file, void *fh,
- struct v4l2_crop *cr)
+static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_frame *f;
- struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = -EINVAL;
-
- if (fimc_capture_active(fimc))
- return -EBUSY;
-
- ret = fimc_try_crop(ctx, cr);
- if (ret)
- return ret;
-
- if (!(ctx->state & FIMC_DST_FMT)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev,
- "Capture color format not set\n");
- return -EINVAL; /* TODO: make sure this is the right value */
- }
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ unsigned long flags;
- f = &ctx->s_frame;
- /* Check for the pixel scaling ratio when cropping input image. */
- ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
- ctx->d_frame.width, ctx->d_frame.height,
- ctx->rotation);
- if (ret) {
- v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range\n");
- return ret;
- }
+ fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK);
+ ff = &ctx->s_frame;
- f->offs_h = cr->c.left;
- f->offs_v = cr->c.top;
- f->width = cr->c.width;
- f->height = cr->c.height;
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
return 0;
}
-
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_querycap = fimc_vidioc_querycap_capture,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
.vidioc_reqbufs = fimc_cap_reqbufs,
.vidioc_querybuf = fimc_cap_querybuf,
@@ -779,10 +1073,6 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_streamon = fimc_cap_streamon,
.vidioc_streamoff = fimc_cap_streamoff,
- .vidioc_queryctrl = fimc_vidioc_queryctrl,
- .vidioc_g_ctrl = fimc_vidioc_g_ctrl,
- .vidioc_s_ctrl = fimc_cap_s_ctrl,
-
.vidioc_g_crop = fimc_cap_g_crop,
.vidioc_s_crop = fimc_cap_s_crop,
.vidioc_cropcap = fimc_cap_cropcap,
@@ -792,17 +1082,328 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_g_input = fimc_cap_g_input,
};
+/* Capture subdev media entity operations */
+static int fimc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ dbg("%s --> %s, flags: 0x%x. input: 0x%x",
+ local->entity->name, remote->entity->name, flags,
+ fimc->vid_cap.input);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->vid_cap.input != 0)
+ return -EBUSY;
+ fimc->vid_cap.input = sd->grp_id;
+ return 0;
+ }
+
+ fimc->vid_cap.input = 0;
+ return 0;
+}
+
+static const struct media_entity_operations fimc_sd_media_ops = {
+ .link_setup = fimc_link_setup,
+};
+
+/**
+ * fimc_sensor_notify - v4l2_device notification from a sensor subdev
+ * @sd: pointer to a subdev generating the notification
+ * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
+ * @arg: pointer to an u32 type integer that stores the frame payload value
+ *
+ * The End Of Frame notification sent by sensor subdev in its still capture
+ * mode. If there is only a single VSYNC generated by the sensor at the
+ * beginning of a frame transmission, FIMC does not issue the LastIrq
+ * (end of frame) interrupt. And this notification is used to complete the
+ * frame capture and returning a buffer to user-space. Subdev drivers should
+ * call this notification from their last 'End of frame capture' interrupt.
+ */
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct fimc_sensor_info *sensor;
+ struct fimc_vid_buffer *buf;
+ struct fimc_md *fmd;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+
+ if (sd == NULL)
+ return;
+
+ sensor = v4l2_get_subdev_hostdata(sd);
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ spin_lock_irqsave(&fmd->slock, flags);
+ fimc = sensor ? sensor->host : NULL;
+
+ if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
+ test_bit(ST_CAPT_PEND, &fimc->state)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fimc->slock, irq_flags);
+ if (!list_empty(&fimc->vid_cap.active_buf_q)) {
+ buf = list_entry(fimc->vid_cap.active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
+ }
+ fimc_capture_irq_handler(fimc, true);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, irq_flags);
+ }
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
+static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt *mf;
+ struct fimc_frame *ff;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf = &fmt->format;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ /* The pixel code is same on both input and output pad */
+ if (!WARN_ON(ctx->s_frame.fmt == NULL))
+ mf->code = ctx->s_frame.fmt->mbus_code;
+ mf->width = ff->f_width;
+ mf->height = ff->f_height;
+ mutex_unlock(&fimc->lock);
+
+ return 0;
+}
+
+static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ struct fimc_fmt *ffmt;
+
+ dbg("pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SOURCE &&
+ vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ mutex_lock(&fimc->lock);
+ ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+ mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ return 0;
+ }
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
+
+ ff = fmt->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ set_frame_bounds(ff, mf->width, mf->height);
+ ff->fmt = ffmt;
+
+ /* Reset the crop rectangle if required. */
+ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP)))
+ set_frame_crop(ff, 0, 0, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SINK)
+ ctx->state &= ~FIMC_DST_CROP;
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_subdev_get_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad);
+ return 0;
+ }
+ ff = crop->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ r->left = ff->offs_h;
+ r->top = ff->offs_v;
+ r->width = ff->width;
+ r->height = ff->height;
+ mutex_unlock(&fimc->lock);
+
+ dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ ff, crop->pad, r->left, r->top, r->width, r->height,
+ ff->f_width, ff->f_height);
+
+ return 0;
+}
+
+static int fimc_subdev_set_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+ unsigned long flags;
+
+ dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height);
+
+ ff = crop->pad == FIMC_SD_PAD_SOURCE ?
+ &ctx->d_frame : &ctx->s_frame;
+
+ mutex_lock(&fimc->lock);
+ fimc_capture_try_crop(ctx, r, crop->pad);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mutex_lock(&fimc->lock);
+ *v4l2_subdev_get_try_crop(fh, crop->pad) = *r;
+ return 0;
+ }
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(ff, r->left, r->top, r->width, r->height);
+ if (crop->pad == FIMC_SD_PAD_SOURCE)
+ ctx->state |= FIMC_DST_CROP;
+
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top,
+ r->width, r->height);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
+ .enum_mbus_code = fimc_subdev_enum_mbus_code,
+ .get_fmt = fimc_subdev_get_fmt,
+ .set_fmt = fimc_subdev_set_fmt,
+ .get_crop = fimc_subdev_get_crop,
+ .set_crop = fimc_subdev_set_crop,
+};
+
+static struct v4l2_subdev_ops fimc_subdev_ops = {
+ .pad = &fimc_subdev_pad_ops,
+};
+
+static int fimc_create_capture_subdev(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+
+ v4l2_subdev_init(sd, &fimc_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads, 0);
+ if (ret)
+ goto me_err;
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret)
+ goto sd_err;
+
+ fimc->vid_cap.subdev = sd;
+ v4l2_set_subdevdata(sd, fimc);
+ sd->entity.ops = &fimc_sd_media_ops;
+ return 0;
+sd_err:
+ media_entity_cleanup(&sd->entity);
+me_err:
+ kfree(sd);
+ return ret;
+}
+
+static void fimc_destroy_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = fimc->vid_cap.subdev;
+
+ if (!sd)
+ return;
+ media_entity_cleanup(&sd->entity);
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ sd = NULL;
+}
+
+/* Set default format at the sensor and host interface */
+static int fimc_capture_set_default_format(struct fimc_dev *fimc)
+{
+ struct v4l2_format fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix_mp = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ };
+
+ return fimc_capture_set_format(fimc, &fmt);
+}
+
/* fimc->lock must be already initialized */
-int fimc_register_capture_device(struct fimc_dev *fimc)
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
{
- struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev;
struct video_device *vfd;
struct fimc_vid_cap *vid_cap;
struct fimc_ctx *ctx;
- struct v4l2_format f;
- struct fimc_frame *fr;
struct vb2_queue *q;
- int ret;
+ int ret = -ENOMEM;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
@@ -812,33 +1413,21 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
ctx->in_path = FIMC_CAMERA;
ctx->out_path = FIMC_DMA;
ctx->state = FIMC_CTX_CAP;
-
- /* Default format of the output frames */
- f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
- fr = &ctx->d_frame;
- fr->fmt = find_format(&f, FMT_FLAGS_M2M);
- fr->width = fr->f_width = fr->o_width = 640;
- fr->height = fr->f_height = fr->o_height = 480;
-
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s.capture", dev_name(&fimc->pdev->dev));
-
- ret = v4l2_device_register(NULL, v4l2_dev);
- if (ret)
- goto err_info;
+ ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
vfd = video_device_alloc();
if (!vfd) {
v4l2_err(v4l2_dev, "Failed to allocate video device\n");
- goto err_v4l2_reg;
+ goto err_vd_alloc;
}
- snprintf(vfd->name, sizeof(vfd->name), "%s:cap",
+ snprintf(vfd->name, sizeof(vfd->name), "%s.capture",
dev_name(&fimc->pdev->dev));
vfd->fops = &fimc_capture_fops;
vfd->ioctl_ops = &fimc_capture_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
@@ -849,8 +1438,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vid_cap->active_buf_cnt = 0;
vid_cap->reqbufs_count = 0;
vid_cap->refcnt = 0;
- /* Default color format for image sensor */
- vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
INIT_LIST_HEAD(&vid_cap->pending_buf_q);
INIT_LIST_HEAD(&vid_cap->active_buf_q);
@@ -868,34 +1455,37 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vb2_queue_init(q);
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(v4l2_dev, "Failed to register video device\n");
- goto err_vd_reg;
- }
-
- v4l2_info(v4l2_dev,
- "FIMC capture driver registered as /dev/video%d\n",
- vfd->num);
+ fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0);
+ if (ret)
+ goto err_ent;
+ ret = fimc_create_capture_subdev(fimc, v4l2_dev);
+ if (ret)
+ goto err_sd_reg;
+ vfd->ctrl_handler = &ctx->ctrl_handler;
return 0;
-err_vd_reg:
+err_sd_reg:
+ media_entity_cleanup(&vfd->entity);
+err_ent:
video_device_release(vfd);
-err_v4l2_reg:
- v4l2_device_unregister(v4l2_dev);
-err_info:
+err_vd_alloc:
kfree(ctx);
- dev_err(&fimc->pdev->dev, "failed to install\n");
return ret;
}
void fimc_unregister_capture_device(struct fimc_dev *fimc)
{
- struct fimc_vid_cap *capture = &fimc->vid_cap;
-
- if (capture->vfd)
- video_unregister_device(capture->vfd);
+ struct video_device *vfd = fimc->vid_cap.vfd;
- kfree(capture->ctx);
+ if (vfd) {
+ media_entity_cleanup(&vfd->entity);
+ /* Can also be called if video device was
+ not registered */
+ video_unregister_device(vfd);
+ }
+ fimc_destroy_capture_subdev(fimc);
+ kfree(fimc->vid_cap.ctx);
+ fimc->vid_cap.ctx = NULL;
}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index aa550666cc0b..19ca6db38b2f 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -27,9 +28,10 @@
#include <media/videobuf2-dma-contig.h>
#include "fimc-core.h"
+#include "fimc-mdevice.h"
static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
- "sclk_fimc", "fimc", "sclk_cam"
+ "sclk_fimc", "fimc"
};
static struct fimc_fmt fimc_formats[] = {
@@ -157,59 +159,28 @@ static struct fimc_fmt fimc_formats[] = {
.memplanes = 2,
.colplanes = 2,
.flags = FMT_FLAGS_M2M,
- },
-};
-
-static struct v4l2_queryctrl fimc_ctrls[] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Horizontal flip",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
}, {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vertical flip",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_ROTATE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Rotation (CCW)",
- .minimum = 0,
- .maximum = 270,
- .step = 90,
- .default_value = 0,
+ .name = "JPEG encoded data",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .color = S5P_FIMC_JPEG,
+ .depth = { 8 },
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
+ .flags = FMT_FLAGS_CAM,
},
};
-
-static struct v4l2_queryctrl *get_ctrl(int id)
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i)
- if (id == fimc_ctrls[i].id)
- return &fimc_ctrls[i];
- return NULL;
-}
+ if (rotation == 90 || rotation == 270)
+ swap(dw, dh);
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot)
-{
- int tx, ty;
-
- if (rot == 90 || rot == 270) {
- ty = dw;
- tx = dh;
- } else {
- tx = dw;
- ty = dh;
- }
+ if (!ctx->scaler.enabled)
+ return (sw == dw && sh == dh) ? 0 : -EINVAL;
- if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty))
+ if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh))
return -EINVAL;
return 0;
@@ -235,10 +206,11 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
int fimc_set_scaler_info(struct fimc_ctx *ctx)
{
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct device *dev = &ctx->fimc_dev->pdev->dev;
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *s_frame = &ctx->s_frame;
struct fimc_frame *d_frame = &ctx->d_frame;
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
int tx, ty, sx, sy;
int ret;
@@ -250,15 +222,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
ty = d_frame->height;
}
if (tx <= 0 || ty <= 0) {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
- "invalid target size: %d x %d", tx, ty);
+ dev_err(dev, "Invalid target size: %dx%d", tx, ty);
return -EINVAL;
}
sx = s_frame->width;
sy = s_frame->height;
if (sx <= 0 || sy <= 0) {
- err("invalid source size: %d x %d", sx, sy);
+ dev_err(dev, "Invalid source size: %dx%d", sx, sy);
return -EINVAL;
}
sc->real_width = sx;
@@ -301,7 +272,6 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
{
struct vb2_buffer *src_vb, *dst_vb;
- struct fimc_dev *fimc = ctx->fimc_dev;
if (!ctx || !ctx->m2m_ctx)
return;
@@ -312,54 +282,68 @@ static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
if (src_vb && dst_vb) {
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
- v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
}
}
/* Complete the transaction which has been scheduled for execution. */
-static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
+static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
{
struct fimc_dev *fimc = ctx->fimc_dev;
int ret;
if (!fimc_m2m_pending(fimc))
- return;
+ return 0;
fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
ret = wait_event_timeout(fimc->irq_queue,
!fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
FIMC_SHUTDOWN_TIMEOUT);
- /*
- * In case of a timeout the buffers are not released in the interrupt
- * handler so return them here with the error flag set, if there are
- * any on the queue.
- */
- if (ret == 0)
- fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
}
static int stop_streaming(struct vb2_queue *q)
{
struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
- fimc_m2m_shutdown(ctx);
+ ret = fimc_m2m_shutdown(ctx);
+ if (ret == -ETIMEDOUT)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ pm_runtime_put(&ctx->fimc_dev->pdev->dev);
return 0;
}
-static void fimc_capture_irq_handler(struct fimc_dev *fimc)
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *v_buf;
struct timeval *tv;
struct timespec ts;
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ return;
+ }
+
if (!list_empty(&cap->active_buf_q) &&
- test_bit(ST_CAPT_RUN, &fimc->state)) {
+ test_bit(ST_CAPT_RUN, &fimc->state) && final) {
ktime_get_real_ts(&ts);
- v_buf = active_queue_pop(cap);
+ v_buf = fimc_active_queue_pop(cap);
tv = &v_buf->vb.v4l2_buf.timestamp;
tv->tv_sec = ts.tv_sec;
@@ -369,19 +353,14 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
- if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- return;
- }
-
if (!list_empty(&cap->pending_buf_q)) {
- v_buf = pending_queue_pop(cap);
+ v_buf = fimc_pending_queue_pop(cap);
fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
v_buf->index = cap->buf_index;
/* Move the buffer to the capture active queue */
- active_queue_add(cap, v_buf);
+ fimc_active_queue_add(cap, v_buf);
dbg("next frame: %d, done frame: %d",
fimc_hw_get_frame_index(fimc), v_buf->index);
@@ -391,7 +370,8 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
}
if (cap->active_buf_cnt == 0) {
- clear_bit(ST_CAPT_RUN, &fimc->state);
+ if (final)
+ clear_bit(ST_CAPT_RUN, &fimc->state);
if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
cap->buf_index = 0;
@@ -399,11 +379,13 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
set_bit(ST_CAPT_RUN, &fimc->state);
}
+ fimc_capture_config_update(cap->ctx);
+
dbg("frame: %d, active_buf_cnt: %d",
fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
}
-static irqreturn_t fimc_isr(int irq, void *priv)
+static irqreturn_t fimc_irq_handler(int irq, void *priv)
{
struct fimc_dev *fimc = priv;
struct fimc_vid_cap *cap = &fimc->vid_cap;
@@ -411,9 +393,17 @@ static irqreturn_t fimc_isr(int irq, void *priv)
fimc_hw_clear_irq(fimc);
+ spin_lock(&fimc->slock);
+
if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) {
+ set_bit(ST_M2M_SUSPENDED, &fimc->state);
+ wake_up(&fimc->irq_queue);
+ goto out;
+ }
ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
if (ctx != NULL) {
+ spin_unlock(&fimc->slock);
fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
spin_lock(&ctx->slock);
@@ -423,21 +413,16 @@ static irqreturn_t fimc_isr(int irq, void *priv)
}
spin_unlock(&ctx->slock);
}
-
return IRQ_HANDLED;
- }
-
- spin_lock(&fimc->slock);
-
- if (test_bit(ST_CAPT_PEND, &fimc->state)) {
- fimc_capture_irq_handler(fimc);
-
+ } else if (test_bit(ST_CAPT_PEND, &fimc->state)) {
+ fimc_capture_irq_handler(fimc,
+ !test_bit(ST_CAPT_JPEG, &fimc->state));
if (cap->active_buf_cnt == 1) {
fimc_deactivate_capture(fimc);
clear_bit(ST_CAPT_STREAM, &fimc->state);
}
}
-
+out:
spin_unlock(&fimc->slock);
return IRQ_HANDLED;
}
@@ -457,7 +442,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
dbg("memplanes= %d, colplanes= %d, pix_size= %d",
frame->fmt->memplanes, frame->fmt->colplanes, pix_size);
- paddr->y = vb2_dma_contig_plane_paddr(vb, 0);
+ paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
if (frame->fmt->memplanes == 1) {
switch (frame->fmt->colplanes) {
@@ -485,10 +470,10 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
}
} else {
if (frame->fmt->memplanes >= 2)
- paddr->cb = vb2_dma_contig_plane_paddr(vb, 1);
+ paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
if (frame->fmt->memplanes == 3)
- paddr->cr = vb2_dma_contig_plane_paddr(vb, 2);
+ paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
}
dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
@@ -498,7 +483,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
}
/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
-static void fimc_set_yuv_order(struct fimc_ctx *ctx)
+void fimc_set_yuv_order(struct fimc_ctx *ctx)
{
/* The one only mode supported in SoC. */
ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
@@ -540,7 +525,7 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx)
dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
}
-static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
{
struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
u32 i, depth = 0;
@@ -606,9 +591,6 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
fimc_set_yuv_order(ctx);
}
- /* Input DMA mode is not allowed when the scaler is disabled. */
- ctx->scaler.enabled = 1;
-
if (flags & FIMC_SRC_ADDR) {
vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
@@ -635,10 +617,10 @@ static void fimc_dma_run(void *priv)
return;
fimc = ctx->fimc_dev;
-
- spin_lock_irqsave(&ctx->slock, flags);
+ spin_lock_irqsave(&fimc->slock, flags);
set_bit(ST_M2M_PEND, &fimc->state);
+ spin_lock(&ctx->slock);
ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
ret = fimc_prepare_config(ctx, ctx->state);
if (ret)
@@ -649,8 +631,6 @@ static void fimc_dma_run(void *priv)
ctx->state |= FIMC_PARAMS;
fimc->m2m.ctx = ctx;
}
-
- spin_lock(&fimc->slock);
fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
if (ctx->state & FIMC_PARAMS) {
@@ -665,7 +645,7 @@ static void fimc_dma_run(void *priv)
fimc_hw_set_mainscaler(ctx);
fimc_hw_set_target_format(ctx);
fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx);
+ fimc_hw_set_effect(ctx, false);
}
fimc_hw_set_output_path(ctx);
@@ -680,10 +660,9 @@ static void fimc_dma_run(void *priv)
ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
FIMC_SRC_FMT | FIMC_DST_FMT);
fimc_hw_activate_input_dma(fimc, true);
- spin_unlock(&fimc->slock);
-
dma_unlock:
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock(&ctx->slock);
+ spin_unlock_irqrestore(&fimc->slock, flags);
}
static void fimc_job_abort(void *priv)
@@ -691,9 +670,9 @@ static void fimc_job_abort(void *priv)
fimc_m2m_shutdown(priv);
}
-static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *allocators[])
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
{
struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
struct fimc_frame *f;
@@ -762,146 +741,296 @@ static struct vb2_ops fimc_qops = {
.wait_prepare = fimc_unlock,
.wait_finish = fimc_lock,
.stop_streaming = stop_streaming,
+ .start_streaming = start_streaming,
};
-static int fimc_m2m_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
+/*
+ * V4L2 controls handling
+ */
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
+
+static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = fimc->variant;
+ unsigned long flags;
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->vflip = ctrl->val;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if (fimc_capture_pending(fimc) ||
+ fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctrl->val);
+ }
+ if (ret) {
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
+ }
+ if ((ctrl->val == 90 || ctrl->val == 270) &&
+ !variant->has_out_rot)
+ return -EINVAL;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->rotation = ctrl->val;
+ break;
+
+ default:
+ v4l2_err(fimc->v4l2_dev, "Invalid control: 0x%X\n", ctrl->id);
+ return -EINVAL;
+ }
+ ctx->state |= FIMC_PARAMS;
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
+ .s_ctrl = fimc_s_ctrl,
+};
+
+int fimc_ctrls_create(struct fimc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy)
+ return 0;
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
+ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+ return ctx->ctrl_handler.error;
+}
+
+void fimc_ctrls_delete(struct fimc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ ctx->ctrls_rdy = false;
+ }
+}
+
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)
+{
+ if (!ctx->ctrls_rdy)
+ return;
+
+ mutex_lock(&ctx->ctrl_handler.lock);
+ v4l2_ctrl_activate(ctx->ctrl_rotate, active);
+ v4l2_ctrl_activate(ctx->ctrl_hflip, active);
+ v4l2_ctrl_activate(ctx->ctrl_vflip, active);
+
+ if (active) {
+ ctx->rotation = ctx->ctrl_rotate->val;
+ ctx->hflip = ctx->ctrl_hflip->val;
+ ctx->vflip = ctx->ctrl_vflip->val;
+ } else {
+ ctx->rotation = 0;
+ ctx->hflip = 0;
+ ctx->vflip = 0;
+ }
+ mutex_unlock(&ctx->ctrl_handler.lock);
+}
+
+/*
+ * V4L2 ioctl handlers
+ */
+static int fimc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
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 |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
-int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
struct fimc_fmt *fmt;
- if (f->index >= ARRAY_SIZE(fimc_formats))
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_M2M, f->index);
+ if (!fmt)
return -EINVAL;
- fmt = &fimc_formats[f->index];
strncpy(f->description, fmt->name, sizeof(f->description) - 1);
f->pixelformat = fmt->fourcc;
-
return 0;
}
-int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pixm;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
int i;
- frame = ctx_get_frame(ctx, f->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- pixm = &f->fmt.pix_mp;
-
- pixm->width = frame->width;
- pixm->height = frame->height;
- pixm->field = V4L2_FIELD_NONE;
- pixm->pixelformat = frame->fmt->fourcc;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
- pixm->num_planes = frame->fmt->memplanes;
+ pixm->width = frame->o_width;
+ pixm->height = frame->o_height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->pixelformat = frame->fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->num_planes = frame->fmt->memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
- int bpl = frame->o_width;
-
+ int bpl = frame->f_width;
if (frame->fmt->colplanes == 1) /* packed formats */
bpl = (bpl * frame->fmt->depth[0]) / 8;
-
pixm->plane_fmt[i].bytesperline = bpl;
-
pixm->plane_fmt[i].sizeimage = (frame->o_width *
frame->o_height * frame->fmt->depth[i]) / 8;
}
-
return 0;
}
-struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask)
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f)
{
- struct fimc_fmt *fmt;
- unsigned int i;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+
+ frame->f_width = pixm->plane_fmt[0].bytesperline;
+ if (frame->fmt->colplanes == 1)
+ frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0];
+ frame->f_height = pixm->height;
+ frame->width = pixm->width;
+ frame->height = pixm->height;
+ frame->o_width = pixm->width;
+ frame->o_height = pixm->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+}
- for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
- fmt = &fimc_formats[i];
- if (fmt->fourcc == f->fmt.pix_mp.pixelformat &&
- (fmt->flags & mask))
- break;
+/**
+ * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane
+ * @fmt: fimc pixel format description (input)
+ * @width: requested pixel width
+ * @height: requested pixel height
+ * @pix: multi-plane format to adjust
+ */
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix)
+{
+ u32 bytesperline = 0;
+ int i;
+
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ pix->field = V4L2_FIELD_NONE;
+ pix->num_planes = fmt->memplanes;
+ pix->height = height;
+ pix->width = width;
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ u32 bpl = pix->plane_fmt[i].bytesperline;
+ u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
+
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
+
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
+
+ if (i == 0) /* Same bytesperline for each plane. */
+ bytesperline = bpl;
+
+ pix->plane_fmt[i].bytesperline = bytesperline;
+ *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
}
+}
- return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
+static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
+
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ return fimc_fill_format(frame, f);
}
-struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
- unsigned int mask)
+/**
+ * fimc_find_format - lookup fimc color format by fourcc or media bus format
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @mask: the color flags to match
+ * @index: offset in the fimc_formats array, ignored if negative
+ */
+struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code,
+ unsigned int mask, int index)
{
- struct fimc_fmt *fmt;
+ struct fimc_fmt *fmt, *def_fmt = NULL;
unsigned int i;
+ int id = 0;
+
+ if (index >= ARRAY_SIZE(fimc_formats))
+ return NULL;
for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
fmt = &fimc_formats[i];
- if (fmt->mbus_code == f->code && (fmt->flags & mask))
- break;
+ if (!(fmt->flags & mask))
+ continue;
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
}
-
- return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
+ return def_fmt;
}
-
-int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
+static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
struct samsung_fimc_variant *variant = fimc->variant;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct fimc_fmt *fmt;
- u32 max_width, mod_x, mod_y, mask;
- int i, is_output = 0;
-
+ u32 max_w, mod_x, mod_y;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx))
- return -EINVAL;
- is_output = 1;
- } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (!IS_M2M(f->type))
return -EINVAL;
- }
dbg("w: %d, h: %d", pix->width, pix->height);
- mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM;
- fmt = find_format(f, mask);
- if (!fmt) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n",
- pix->pixelformat);
+ fmt = fimc_find_format(&pix->pixelformat, NULL, FMT_FLAGS_M2M, 0);
+ if (WARN(fmt == NULL, "Pixel format lookup failed"))
return -EINVAL;
- }
if (pix->field == V4L2_FIELD_ANY)
pix->field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != pix->field)
+ else if (pix->field != V4L2_FIELD_NONE)
return -EINVAL;
- if (is_output) {
- max_width = variant->pix_limit->scaler_dis_w;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ max_w = variant->pix_limit->scaler_dis_w;
mod_x = ffs(variant->min_inp_pixsize) - 1;
} else {
- max_width = variant->pix_limit->out_rot_dis_w;
+ max_w = variant->pix_limit->out_rot_dis_w;
mod_x = ffs(variant->min_out_pixsize) - 1;
}
@@ -914,70 +1043,52 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
else
mod_y = mod_x;
}
+ dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_w);
- dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_width);
-
- v4l_bound_align_image(&pix->width, 16, max_width, mod_x,
+ v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
&pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
- pix->num_planes = fmt->memplanes;
- pix->colorspace = V4L2_COLORSPACE_JPEG;
-
-
- for (i = 0; i < pix->num_planes; ++i) {
- u32 bpl = pix->plane_fmt[i].bytesperline;
- u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
-
- if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
- bpl = pix->width; /* Planar */
-
- if (fmt->colplanes == 1 && /* Packed */
- (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
- bpl = (pix->width * fmt->depth[0]) / 8;
-
- if (i == 0) /* Same bytesperline for each plane. */
- mod_x = bpl;
+ fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
+ return 0;
+}
- pix->plane_fmt[i].bytesperline = mod_x;
- *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
- }
+static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
- return 0;
+ return fimc_try_fmt_mplane(ctx, f);
}
-static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
struct vb2_queue *vq;
struct fimc_frame *frame;
struct v4l2_pix_format_mplane *pix;
int i, ret = 0;
- ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
+ ret = fimc_try_fmt_mplane(ctx, f);
if (ret)
return ret;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (vb2_is_busy(vq)) {
- v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type);
+ v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type);
return -EBUSY;
}
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
frame = &ctx->s_frame;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ else
frame = &ctx->d_frame;
- } else {
- v4l2_err(&fimc->m2m.v4l2_dev,
- "Wrong buffer/video queue type (%d)\n", f->type);
- return -EINVAL;
- }
pix = &f->fmt.pix_mp;
- frame->fmt = find_format(f, FMT_FLAGS_M2M);
+ frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
+ FMT_FLAGS_M2M, 0);
if (!frame->fmt)
return -EINVAL;
@@ -986,15 +1097,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
(pix->width * pix->height * frame->fmt->depth[i]) / 8;
}
- frame->f_width = pix->plane_fmt[0].bytesperline * 8 /
- frame->fmt->depth[0];
- frame->f_height = pix->height;
- frame->width = pix->width;
- frame->height = pix->height;
- frame->o_width = pix->width;
- frame->o_height = pix->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
+ fimc_fill_frame(frame, f);
+
+ ctx->scaler.enabled = 1;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
@@ -1006,39 +1111,42 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
return 0;
}
-static int fimc_m2m_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
+static int fimc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
-static int fimc_m2m_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int fimc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
-static int fimc_m2m_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
+static int fimc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
/* The source and target color format need to be set */
if (V4L2_TYPE_IS_OUTPUT(type)) {
@@ -1051,149 +1159,19 @@ static int fimc_m2m_streamon(struct file *file, void *priv,
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
-static int fimc_m2m_streamoff(struct file *file, void *priv,
+static int fimc_m2m_streamoff(struct file *file, void *fh,
enum v4l2_buf_type type)
{
- struct fimc_ctx *ctx = priv;
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-int fimc_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct fimc_ctx *ctx = priv;
- struct v4l2_queryctrl *c;
- int ret = -EINVAL;
-
- c = get_ctrl(qc->id);
- if (c) {
- *qc = *c;
- return 0;
- }
-
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
- return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
- core, queryctrl, qc);
- }
- return ret;
-}
-
-int fimc_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
- break;
- case V4L2_CID_VFLIP:
- ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
- break;
- case V4L2_CID_ROTATE:
- ctrl->value = ctx->rotation;
- break;
- default:
- if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
- return v4l2_subdev_call(fimc->vid_cap.sd, core,
- g_ctrl, ctrl);
- } else {
- v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
- }
- dbg("ctrl->value= %d", ctrl->value);
-
- return 0;
-}
-
-int check_ctrl_val(struct fimc_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
- || (c->step != 0 && ctrl->value % c->step != 0)) {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
- "Invalid control value\n");
- return -ERANGE;
- }
-
- return 0;
-}
-
-int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
-{
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- if (ctrl->value)
- ctx->flip |= FLIP_X_AXIS;
- else
- ctx->flip &= ~FLIP_X_AXIS;
- break;
-
- case V4L2_CID_VFLIP:
- if (ctrl->value)
- ctx->flip |= FLIP_Y_AXIS;
- else
- ctx->flip &= ~FLIP_Y_AXIS;
- break;
-
- case V4L2_CID_ROTATE:
- if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
- ret = fimc_check_scaler_ratio(ctx->s_frame.width,
- ctx->s_frame.height, ctx->d_frame.width,
- ctx->d_frame.height, ctrl->value);
- }
-
- if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
- return -EINVAL;
- }
-
- /* Check for the output rotator availability */
- if ((ctrl->value == 90 || ctrl->value == 270) &&
- (ctx->in_path == FIMC_DMA && !variant->has_out_rot))
- return -EINVAL;
- ctx->rotation = ctrl->value;
- break;
-
- default:
- v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
-
- fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
-
- return 0;
-}
-
-static int fimc_m2m_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fimc_ctx *ctx = priv;
- int ret = 0;
-
- ret = check_ctrl_val(ctx, ctrl);
- if (ret)
- return ret;
-
- ret = fimc_s_ctrl(ctx, ctrl);
- return 0;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
static int fimc_m2m_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
+ struct v4l2_cropcap *cr)
{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_frame *frame;
- struct fimc_ctx *ctx = fh;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
@@ -1201,8 +1179,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh,
cr->bounds.left = 0;
cr->bounds.top = 0;
- cr->bounds.width = frame->f_width;
- cr->bounds.height = frame->f_height;
+ cr->bounds.width = frame->o_width;
+ cr->bounds.height = frame->o_height;
cr->defrect = cr->bounds;
return 0;
@@ -1210,8 +1188,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh,
static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_frame *frame;
- struct fimc_ctx *ctx = file->private_data;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
@@ -1225,26 +1203,21 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
return 0;
}
-int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
+static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
{
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
u32 min_size, halign, depth = 0;
- bool is_capture_ctx;
int i;
if (cr->c.top < 0 || cr->c.left < 0) {
- v4l2_err(&fimc->m2m.v4l2_dev,
+ v4l2_err(fimc->m2m.vfd,
"doesn't support negative values for top & left\n");
return -EINVAL;
}
-
- is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx);
-
if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- !is_capture_ctx)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
f = &ctx->s_frame;
else
return -EINVAL;
@@ -1253,15 +1226,10 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
/* Get pixel alignment constraints. */
- if (is_capture_ctx) {
- min_size = 16;
- halign = 4;
- } else {
- if (fimc->id == 1 && fimc->variant->pix_hoff)
- halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
- else
- halign = ffs(min_size) - 1;
- }
+ if (fimc->id == 1 && fimc->variant->pix_hoff)
+ halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+ else
+ halign = ffs(min_size) - 1;
for (i = 0; i < f->fmt->colplanes; i++)
depth += f->fmt->depth[i];
@@ -1278,7 +1246,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
cr->c.top = f->o_height - cr->c.height;
cr->c.left = round_down(cr->c.left, min_size);
- cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8);
+ cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
cr->c.left, cr->c.top, cr->c.width, cr->c.height,
@@ -1289,12 +1257,12 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
int ret;
- ret = fimc_try_crop(ctx, cr);
+ ret = fimc_m2m_try_crop(ctx, cr);
if (ret)
return ret;
@@ -1304,18 +1272,16 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
/* Check to see if scaling ratio is within supported range */
if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
- ctx->d_frame.width,
- ctx->d_frame.height,
- ctx->rotation);
+ ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+ cr->c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
} else {
- ret = fimc_check_scaler_ratio(ctx->s_frame.width,
- ctx->s_frame.height,
- cr->c.width, cr->c.height,
- ctx->rotation);
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr->c.width,
+ cr->c.height, ctx->rotation);
}
if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
return -EINVAL;
}
}
@@ -1333,14 +1299,14 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_querycap = fimc_m2m_querycap,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
- .vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
- .vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
- .vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
@@ -1354,10 +1320,6 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_streamon = fimc_m2m_streamon,
.vidioc_streamoff = fimc_m2m_streamoff,
- .vidioc_queryctrl = fimc_vidioc_queryctrl,
- .vidioc_g_ctrl = fimc_vidioc_g_ctrl,
- .vidioc_s_ctrl = fimc_m2m_s_ctrl,
-
.vidioc_g_crop = fimc_m2m_g_crop,
.vidioc_s_crop = fimc_m2m_s_crop,
.vidioc_cropcap = fimc_m2m_cropcap
@@ -1396,7 +1358,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
static int fimc_m2m_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = NULL;
+ struct fimc_ctx *ctx;
+ int ret;
dbg("pid: %d, state: 0x%lx, refcnt: %d",
task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
@@ -1408,19 +1371,24 @@ static int fimc_m2m_open(struct file *file)
if (fimc->vid_cap.refcnt > 0)
return -EBUSY;
- fimc->m2m.refcnt++;
- set_bit(ST_OUTDMA_RUN, &fimc->state);
-
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
- file->private_data = ctx;
ctx->fimc_dev = fimc;
/* Default color format */
ctx->s_frame.fmt = &fimc_formats[0];
ctx->d_frame.fmt = &fimc_formats[0];
- /* Setup the device context for mem2mem mode. */
+ /* Setup the device context for memory-to-memory mode */
ctx->state = FIMC_CTX_M2M;
ctx->flags = 0;
ctx->in_path = FIMC_DMA;
@@ -1429,34 +1397,46 @@ static int fimc_m2m_open(struct file *file)
ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- int err = PTR_ERR(ctx->m2m_ctx);
- kfree(ctx);
- return err;
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_c;
}
+ if (fimc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_RUN, &fimc->state);
return 0;
+
+error_c:
+ fimc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
}
static int fimc_m2m_release(struct file *file)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
struct fimc_dev *fimc = ctx->fimc_dev;
dbg("pid: %d, state: 0x%lx, refcnt= %d",
task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
- kfree(ctx);
- if (--fimc->m2m.refcnt <= 0)
- clear_bit(ST_OUTDMA_RUN, &fimc->state);
+ fimc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_RUN, &fimc->state);
+ kfree(ctx);
return 0;
}
static unsigned int fimc_m2m_poll(struct file *file,
- struct poll_table_struct *wait)
+ struct poll_table_struct *wait)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
}
@@ -1464,7 +1444,7 @@ static unsigned int fimc_m2m_poll(struct file *file,
static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct fimc_ctx *ctx = file->private_data;
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
}
@@ -1483,92 +1463,73 @@ static struct v4l2_m2m_ops m2m_ops = {
.job_abort = fimc_job_abort,
};
-static int fimc_register_m2m_device(struct fimc_dev *fimc)
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
{
struct video_device *vfd;
struct platform_device *pdev;
- struct v4l2_device *v4l2_dev;
int ret = 0;
if (!fimc)
return -ENODEV;
pdev = fimc->pdev;
- v4l2_dev = &fimc->m2m.v4l2_dev;
-
- /* set name if it is empty */
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s.m2m", dev_name(&pdev->dev));
-
- ret = v4l2_device_register(&pdev->dev, v4l2_dev);
- if (ret)
- goto err_m2m_r1;
+ fimc->v4l2_dev = v4l2_dev;
vfd = video_device_alloc();
if (!vfd) {
v4l2_err(v4l2_dev, "Failed to allocate video device\n");
- goto err_m2m_r1;
+ return -ENOMEM;
}
vfd->fops = &fimc_m2m_fops;
vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
- snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
-
+ snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev));
video_set_drvdata(vfd, fimc);
- platform_set_drvdata(pdev, fimc);
fimc->m2m.vfd = vfd;
fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
if (IS_ERR(fimc->m2m.m2m_dev)) {
v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
ret = PTR_ERR(fimc->m2m.m2m_dev);
- goto err_m2m_r2;
- }
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(v4l2_dev,
- "%s(): failed to register video device\n", __func__);
- goto err_m2m_r3;
+ goto err_init;
}
- v4l2_info(v4l2_dev,
- "FIMC m2m driver registered as /dev/video%d\n", vfd->num);
- return 0;
+ ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ if (!ret)
+ return 0;
-err_m2m_r3:
v4l2_m2m_release(fimc->m2m.m2m_dev);
-err_m2m_r2:
+err_init:
video_device_release(fimc->m2m.vfd);
-err_m2m_r1:
- v4l2_device_unregister(v4l2_dev);
-
return ret;
}
-static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+void fimc_unregister_m2m_device(struct fimc_dev *fimc)
{
- if (fimc) {
+ if (!fimc)
+ return;
+
+ if (fimc->m2m.m2m_dev)
v4l2_m2m_release(fimc->m2m.m2m_dev);
+ if (fimc->m2m.vfd) {
+ media_entity_cleanup(&fimc->m2m.vfd->entity);
+ /* Can also be called if video device wasn't registered */
video_unregister_device(fimc->m2m.vfd);
-
- v4l2_device_unregister(&fimc->m2m.v4l2_dev);
}
}
-static void fimc_clk_release(struct fimc_dev *fimc)
+static void fimc_clk_put(struct fimc_dev *fimc)
{
int i;
for (i = 0; i < fimc->num_clocks; i++) {
- if (fimc->clock[i]) {
- clk_disable(fimc->clock[i]);
+ if (fimc->clock[i])
clk_put(fimc->clock[i]);
- }
}
}
@@ -1577,15 +1538,50 @@ static int fimc_clk_get(struct fimc_dev *fimc)
int i;
for (i = 0; i < fimc->num_clocks; i++) {
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
-
- if (!IS_ERR_OR_NULL(fimc->clock[i])) {
- clk_enable(fimc->clock[i]);
+ if (!IS_ERR_OR_NULL(fimc->clock[i]))
continue;
- }
dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
fimc_clocks[i]);
return -ENXIO;
}
+
+ return 0;
+}
+
+static int fimc_m2m_suspend(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+ int timeout;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!fimc_m2m_pending(fimc)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ clear_bit(ST_M2M_SUSPENDED, &fimc->state);
+ set_bit(ST_M2M_SUSPENDING, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ timeout = wait_event_timeout(fimc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ clear_bit(ST_M2M_SUSPENDING, &fimc->state);
+ return timeout == 0 ? -EAGAIN : 0;
+}
+
+static int fimc_m2m_resume(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ /* Clear for full H/W setup in first run after resume */
+ fimc->m2m.ctx = NULL;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))
+ fimc_m2m_job_finish(fimc->m2m.ctx,
+ VB2_BUF_STATE_ERROR);
return 0;
}
@@ -1596,7 +1592,6 @@ static int fimc_probe(struct platform_device *pdev)
struct samsung_fimc_driverdata *drv_data;
struct s5p_platform_fimc *pdata;
int ret = 0;
- int cap_input_index = -1;
dev_dbg(&pdev->dev, "%s():\n", __func__);
@@ -1614,15 +1609,16 @@ static int fimc_probe(struct platform_device *pdev)
return -ENOMEM;
fimc->id = pdev->id;
+
fimc->variant = drv_data->variant[fimc->id];
fimc->pdev = pdev;
pdata = pdev->dev.platform_data;
fimc->pdata = pdata;
- fimc->state = ST_IDLE;
+
+ set_bit(ST_LPM, &fimc->state);
init_waitqueue_head(&fimc->irq_queue);
spin_lock_init(&fimc->slock);
-
mutex_init(&fimc->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1647,71 +1643,51 @@ static int fimc_probe(struct platform_device *pdev)
goto err_req_region;
}
- fimc->num_clocks = MAX_FIMC_CLOCKS - 1;
-
- /* Check if a video capture node needs to be registered. */
- if (pdata && pdata->num_clients > 0) {
- cap_input_index = 0;
- fimc->num_clocks++;
- }
-
- ret = fimc_clk_get(fimc);
- if (ret)
- goto err_regs_unmap;
- clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get IRQ resource\n");
ret = -ENXIO;
- goto err_clk;
+ goto err_regs_unmap;
}
fimc->irq = res->start;
- fimc_hw_reset(fimc);
+ fimc->num_clocks = MAX_FIMC_CLOCKS;
+ ret = fimc_clk_get(fimc);
+ if (ret)
+ goto err_regs_unmap;
+ clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
+ clk_enable(fimc->clock[CLK_BUS]);
- ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
+ platform_set_drvdata(pdev, fimc);
+
+ ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc);
if (ret) {
dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
goto err_clk;
}
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_irq;
/* Initialize contiguous memory allocator */
- fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev);
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
- goto err_irq;
+ goto err_pm;
}
- ret = fimc_register_m2m_device(fimc);
- if (ret)
- goto err_irq;
-
- /* At least one camera sensor is required to register capture node */
- if (cap_input_index >= 0) {
- ret = fimc_register_capture_device(fimc);
- if (ret)
- goto err_m2m;
- clk_disable(fimc->clock[CLK_CAM]);
- }
- /*
- * Exclude the additional output DMA address registers by masking
- * them out on HW revisions that provide extended capabilites.
- */
- if (fimc->variant->out_buf_count > 4)
- fimc_hw_set_dma_seq(fimc, 0xF);
-
- dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
- __func__, fimc->id);
+ dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id);
+ pm_runtime_put(&pdev->dev);
return 0;
-err_m2m:
- fimc_unregister_m2m_device(fimc);
+err_pm:
+ pm_runtime_put(&pdev->dev);
err_irq:
free_irq(fimc->irq, fimc);
err_clk:
- fimc_clk_release(fimc);
+ fimc_clk_put(fimc);
err_regs_unmap:
iounmap(fimc->regs);
err_req_region:
@@ -1719,31 +1695,105 @@ err_req_region:
kfree(fimc->regs_res);
err_info:
kfree(fimc);
+ return ret;
+}
+static int fimc_runtime_resume(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Enable clocks and perform basic initalization */
+ clk_enable(fimc->clock[CLK_GATE]);
+ fimc_hw_reset(fimc);
+ if (fimc->variant->out_buf_count > 4)
+ fimc_hw_set_dma_seq(fimc, 0xF);
+
+ /* Resume the capture or mem-to-mem device */
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
+ else if (fimc_m2m_pending(fimc))
+ return fimc_m2m_resume(fimc);
+ return 0;
+}
+
+static int fimc_runtime_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (fimc_capture_busy(fimc))
+ ret = fimc_capture_suspend(fimc);
+ else
+ ret = fimc_m2m_suspend(fimc);
+ if (!ret)
+ clk_disable(fimc->clock[CLK_GATE]);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
return ret;
}
-static int __devexit fimc_remove(struct platform_device *pdev)
+#ifdef CONFIG_PM_SLEEP
+static int fimc_resume(struct device *dev)
{
- struct fimc_dev *fimc =
- (struct fimc_dev *)platform_get_drvdata(pdev);
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ unsigned long flags;
- free_irq(fimc->irq, fimc);
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Do not resume if the device was idle before system suspend */
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
+ (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
fimc_hw_reset(fimc);
+ if (fimc->variant->out_buf_count > 4)
+ fimc_hw_set_dma_seq(fimc, 0xF);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
- fimc_unregister_m2m_device(fimc);
- fimc_unregister_capture_device(fimc);
+ return fimc_m2m_resume(fimc);
+}
+
+static int fimc_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
- fimc_clk_release(fimc);
+ if (test_and_set_bit(ST_LPM, &fimc->state))
+ return 0;
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_suspend(fimc);
+
+ return fimc_m2m_suspend(fimc);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ fimc_runtime_suspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ clk_disable(fimc->clock[CLK_BUS]);
+ fimc_clk_put(fimc);
+ free_irq(fimc->irq, fimc);
iounmap(fimc->regs);
release_resource(fimc->regs_res);
kfree(fimc->regs_res);
kfree(fimc);
- dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
+ dev_info(&pdev->dev, "driver unloaded\n");
return 0;
}
@@ -1786,6 +1836,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = {
static struct samsung_fimc_variant fimc0_variant_s5p = {
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1794,6 +1845,7 @@ static struct samsung_fimc_variant fimc0_variant_s5p = {
};
static struct samsung_fimc_variant fimc2_variant_s5p = {
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1805,6 +1857,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
@@ -1816,6 +1869,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
@@ -1825,6 +1879,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
};
static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+ .has_cam_if = 1,
.pix_hoff = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
@@ -1837,22 +1892,24 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_cam_if = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
- .hor_offs_align = 1,
+ .hor_offs_align = 2,
.out_buf_count = 32,
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc2_variant_exynos4 = {
+static struct samsung_fimc_variant fimc3_variant_exynos4 = {
.pix_hoff = 1,
+ .has_cam_if = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
- .hor_offs_align = 1,
+ .hor_offs_align = 2,
.out_buf_count = 32,
.pix_limit = &s5p_pix_limit[3],
};
@@ -1885,7 +1942,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
[0] = &fimc0_variant_exynos4,
[1] = &fimc0_variant_exynos4,
[2] = &fimc0_variant_exynos4,
- [3] = &fimc2_variant_exynos4,
+ [3] = &fimc3_variant_exynos4,
},
.num_entities = 4,
.lclk_frequency = 166000000UL,
@@ -1906,33 +1963,28 @@ static struct platform_device_id fimc_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+static const struct dev_pm_ops fimc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
+ SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
+};
+
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
- .remove = __devexit_p(fimc_remove),
+ .remove = __devexit_p(fimc_remove),
.id_table = fimc_driver_ids,
.driver = {
- .name = MODULE_NAME,
+ .name = FIMC_MODULE_NAME,
.owner = THIS_MODULE,
+ .pm = &fimc_pm_ops,
}
};
-static int __init fimc_init(void)
+int __init fimc_register_driver(void)
{
- int ret = platform_driver_register(&fimc_driver);
- if (ret)
- err("platform_driver_register failed: %d\n", ret);
- return ret;
+ return platform_driver_probe(&fimc_driver, fimc_probe);
}
-static void __exit fimc_exit(void)
+void __exit fimc_unregister_driver(void)
{
platform_driver_unregister(&fimc_driver);
}
-
-module_init(fimc_init);
-module_exit(fimc_exit);
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 1f70772daaf0..a6936dad5b10 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -11,12 +11,16 @@
/*#define DEBUG*/
+#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
+
+#include <media/media-entity.h>
#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
@@ -32,38 +36,46 @@
/* Time to wait for next frame VSYNC interrupt while stopping operation. */
#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
-#define MAX_FIMC_CLOCKS 3
-#define MODULE_NAME "s5p-fimc"
+#define MAX_FIMC_CLOCKS 2
+#define FIMC_MODULE_NAME "s5p-fimc"
#define FIMC_MAX_DEVS 4
#define FIMC_MAX_OUT_BUFS 4
#define SCALER_MAX_HRATIO 64
#define SCALER_MAX_VRATIO 64
#define DMA_MIN_SIZE 8
+#define FIMC_CAMIF_MAX_HEIGHT 0x2000
/* indices to the clocks array */
enum {
CLK_BUS,
CLK_GATE,
- CLK_CAM,
};
enum fimc_dev_flags {
- /* for m2m node */
- ST_IDLE,
- ST_OUTDMA_RUN,
+ ST_LPM,
+ /* m2m node */
+ ST_M2M_RUN,
ST_M2M_PEND,
- /* for capture node */
+ ST_M2M_SUSPENDING,
+ ST_M2M_SUSPENDED,
+ /* capture node */
ST_CAPT_PEND,
ST_CAPT_RUN,
ST_CAPT_STREAM,
+ ST_CAPT_ISP_STREAM,
+ ST_CAPT_SUSPENDED,
ST_CAPT_SHUT,
+ ST_CAPT_BUSY,
+ ST_CAPT_APPLY_CFG,
+ ST_CAPT_JPEG,
};
-#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
+#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
+#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
enum fimc_datapath {
FIMC_CAMERA,
@@ -83,9 +95,14 @@ enum fimc_color_fmt {
S5P_FIMC_CBYCRY422,
S5P_FIMC_CRYCBY422,
S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_JPEG = 0x40,
};
-#define fimc_fmt_is_rgb(x) ((x) & 0x10)
+#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
+#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40))
+
+#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
+ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
@@ -104,9 +121,10 @@ enum fimc_color_fmt {
#define FIMC_DST_ADDR (1 << 2)
#define FIMC_SRC_FMT (1 << 3)
#define FIMC_DST_FMT (1 << 4)
-#define FIMC_CTX_M2M (1 << 5)
-#define FIMC_CTX_CAP (1 << 6)
-#define FIMC_CTX_SHUT (1 << 7)
+#define FIMC_DST_CROP (1 << 5)
+#define FIMC_CTX_M2M (1 << 16)
+#define FIMC_CTX_CAP (1 << 17)
+#define FIMC_CTX_SHUT (1 << 18)
/* Image conversion flags */
#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
@@ -122,11 +140,6 @@ enum fimc_color_fmt {
/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
#define FIMC_COLOR_RANGE_NARROW (1 << 3)
-#define FLIP_NONE 0
-#define FLIP_X_AXIS 1
-#define FLIP_Y_AXIS 2
-#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS)
-
/**
* struct fimc_fmt - the driver's internal color format data
* @mbus_code: Media Bus pixel code, -1 if not applicable
@@ -275,26 +288,29 @@ struct fimc_frame {
/**
* struct fimc_m2m_device - v4l2 memory-to-memory device data
* @vfd: the video device node for v4l2 m2m mode
- * @v4l2_dev: v4l2 device for m2m mode
* @m2m_dev: v4l2 memory-to-memory device data
* @ctx: hardware context data
* @refcnt: the reference counter
*/
struct fimc_m2m_device {
struct video_device *vfd;
- struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct fimc_ctx *ctx;
int refcnt;
};
+#define FIMC_SD_PAD_SINK 0
+#define FIMC_SD_PAD_SOURCE 1
+#define FIMC_SD_PADS_NUM 2
+
/**
* struct fimc_vid_cap - camera capture device information
* @ctx: hardware context data
* @vfd: video device node for camera capture mode
- * @v4l2_dev: v4l2_device struct to manage subdevs
- * @sd: pointer to camera sensor subdevice currently in use
- * @fmt: Media Bus format configured at selected image sensor
+ * @subdev: subdev exposing the FIMC processing block
+ * @vd_pad: fimc video capture node pad
+ * @sd_pads: fimc video processing block pads
+ * @mf: media bus format at the FIMC camera input (and the scaler output) pad
* @pending_buf_q: the pending buffer queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vbq: the capture am video buffer queue
@@ -304,14 +320,17 @@ struct fimc_m2m_device {
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
* @input_index: input (camera sensor) index
* @refcnt: driver's private reference counter
+ * @input: capture input type, grp_id of the attached subdev
+ * @user_subdev_api: true if subdevs are not configured by the host driver
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device *vfd;
- struct v4l2_device v4l2_dev;
- struct v4l2_subdev *sd;;
- struct v4l2_mbus_framefmt fmt;
+ struct v4l2_subdev *subdev;
+ struct media_pad vd_pad;
+ struct v4l2_mbus_framefmt mf;
+ struct media_pad sd_pads[FIMC_SD_PADS_NUM];
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vbq;
@@ -321,6 +340,8 @@ struct fimc_vid_cap {
unsigned int reqbufs_count;
int input_index;
int refcnt;
+ u32 input;
+ bool user_subdev_api;
};
/**
@@ -351,6 +372,7 @@ struct fimc_pix_limit {
* @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
* @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
* are present in this IP revision
+ * @has_cam_if: set if this instance has a camera input interface
* @pix_limit: pixel size constraints for the scaler
* @min_inp_pixsize: minimum input pixel size
* @min_out_pixsize: minimum output pixel size
@@ -363,6 +385,7 @@ struct samsung_fimc_variant {
unsigned int has_out_rot:1;
unsigned int has_cistatus2:1;
unsigned int has_mainscaler_ext:1;
+ unsigned int has_cam_if:1;
struct fimc_pix_limit *pix_limit;
u16 min_inp_pixsize;
u16 min_out_pixsize;
@@ -383,6 +406,12 @@ struct samsung_fimc_driverdata {
int num_entities;
};
+struct fimc_pipeline {
+ struct media_pipeline *pipe;
+ struct v4l2_subdev *sensor;
+ struct v4l2_subdev *csis;
+};
+
struct fimc_ctx;
/**
@@ -399,10 +428,12 @@ struct fimc_ctx;
* @regs_res: the resource claimed for IO registers
* @irq: FIMC interrupt number
* @irq_queue: interrupt handler waitqueue
+ * @v4l2_dev: root v4l2_device
* @m2m: memory-to-memory V4L2 device information
* @vid_cap: camera capture device information
* @state: flags used to synchronize m2m and capture mode operation
* @alloc_ctx: videobuf2 memory allocator context
+ * @pipeline: fimc video capture pipeline data structure
*/
struct fimc_dev {
spinlock_t slock;
@@ -417,10 +448,12 @@ struct fimc_dev {
struct resource *regs_res;
int irq;
wait_queue_head_t irq_queue;
+ struct v4l2_device *v4l2_dev;
struct fimc_m2m_device m2m;
struct fimc_vid_cap vid_cap;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
+ struct fimc_pipeline pipeline;
};
/**
@@ -437,11 +470,18 @@ struct fimc_dev {
* @scaler: image scaler properties
* @effect: image effect
* @rotation: image clockwise rotation in degrees
- * @flip: image flip mode
+ * @hflip: indicates image horizontal flip if set
+ * @vflip: indicates image vertical flip if set
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
* @fimc_dev: the FIMC device this context applies to
* @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrl_handler: v4l2 controls handler
+ * @ctrl_rotate image rotation control
+ * @ctrl_hflip horizontal flip control
+ * @ctrl_vflip vartical flip control
+ * @ctrls_rdy: true if the control handler is initialized
*/
struct fimc_ctx {
spinlock_t slock;
@@ -456,13 +496,49 @@ struct fimc_ctx {
struct fimc_scaler scaler;
struct fimc_effect effect;
int rotation;
- u32 flip;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
u32 flags;
u32 state;
struct fimc_dev *fimc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_rotate;
+ struct v4l2_ctrl *ctrl_hflip;
+ struct v4l2_ctrl *ctrl_vflip;
+ bool ctrls_rdy;
};
+#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
+
+static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height)
+{
+ f->o_width = width;
+ f->o_height = height;
+ f->f_width = width;
+ f->f_height = height;
+}
+
+static inline void set_frame_crop(struct fimc_frame *f,
+ u32 left, u32 top, u32 width, u32 height)
+{
+ f->offs_h = left;
+ f->offs_v = top;
+ f->width = width;
+ f->height = height;
+}
+
+static inline u32 fimc_get_format_depth(struct fimc_fmt *ff)
+{
+ u32 i, depth = 0;
+
+ if (ff != NULL)
+ for (i = 0; i < ff->colplanes; i++)
+ depth += ff->depth[i];
+ return depth;
+}
+
static inline bool fimc_capture_active(struct fimc_dev *fimc)
{
unsigned long flags;
@@ -561,7 +637,7 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
frame = &ctx->d_frame;
} else {
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ v4l2_err(ctx->fimc_dev->v4l2_dev,
"Wrong buffer/video queue type (%d)\n", type);
return ERR_PTR(-EINVAL);
}
@@ -595,7 +671,7 @@ void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
void fimc_hw_en_capture(struct fimc_ctx *ctx);
-void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
void fimc_hw_set_input_path(struct fimc_ctx *ctx);
void fimc_hw_set_output_path(struct fimc_ctx *ctx);
@@ -614,36 +690,45 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
/* fimc-core.c */
int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f);
-int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f);
-int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f);
-int fimc_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc);
-int fimc_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl);
-
-int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr);
-int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
-int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
-
-struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask);
-struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
- unsigned int mask);
-
-int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot);
+int fimc_ctrls_create(struct fimc_ctx *ctx);
+void fimc_ctrls_delete(struct fimc_ctx *ctx);
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix);
+struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code,
+ unsigned int mask, int index);
+
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation);
int fimc_set_scaler_info(struct fimc_ctx *ctx);
int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
struct fimc_frame *frame, struct fimc_addr *paddr);
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
+void fimc_set_yuv_order(struct fimc_ctx *ctx);
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done);
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
+void fimc_unregister_m2m_device(struct fimc_dev *fimc);
+int fimc_register_driver(void);
+void fimc_unregister_driver(void);
/* -----------------------------------------------------*/
/* fimc-capture.c */
-int fimc_register_capture_device(struct fimc_dev *fimc);
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
void fimc_unregister_capture_device(struct fimc_dev *fimc);
-int fimc_sensor_sd_init(struct fimc_dev *fimc, int index);
+int fimc_capture_ctrls_create(struct fimc_dev *fimc);
int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
struct fimc_vid_buffer *fimc_vb);
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg);
+int fimc_capture_suspend(struct fimc_dev *fimc);
+int fimc_capture_resume(struct fimc_dev *fimc);
+int fimc_capture_config_update(struct fimc_ctx *ctx);
/* Locking: the caller holds fimc->slock */
static inline void fimc_activate_capture(struct fimc_ctx *ctx)
@@ -661,22 +746,27 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
}
/*
- * Add buf to the capture active buffers queue.
- * Locking: Need to be called with fimc_dev::slock held.
+ * Buffer list manipulation functions. Must be called with fimc.slock held.
*/
-static inline void active_queue_add(struct fimc_vid_cap *vid_cap,
- struct fimc_vid_buffer *buf)
+
+/**
+ * fimc_active_queue_add - add buffer to the capture active buffers queue
+ * @buf: buffer to add to the active buffers list
+ */
+static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
{
list_add_tail(&buf->list, &vid_cap->active_buf_q);
vid_cap->active_buf_cnt++;
}
-/*
- * Pop a video buffer from the capture active buffers queue
- * Locking: Need to be called with fimc_dev::slock held.
+/**
+ * fimc_active_queue_pop - pop buffer from the capture active buffers queue
+ *
+ * The caller must assure the active_buf_q list is not empty.
*/
-static inline struct fimc_vid_buffer *
-active_queue_pop(struct fimc_vid_cap *vid_cap)
+static inline struct fimc_vid_buffer *fimc_active_queue_pop(
+ struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->active_buf_q.next,
@@ -686,16 +776,23 @@ active_queue_pop(struct fimc_vid_cap *vid_cap)
return buf;
}
-/* Add video buffer to the capture pending buffers queue */
+/**
+ * fimc_pending_queue_add - add buffer to the capture pending buffers queue
+ * @buf: buffer to add to the pending buffers list
+ */
static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
struct fimc_vid_buffer *buf)
{
list_add_tail(&buf->list, &vid_cap->pending_buf_q);
}
-/* Add video buffer to the capture pending buffers queue */
-static inline struct fimc_vid_buffer *
-pending_queue_pop(struct fimc_vid_cap *vid_cap)
+/**
+ * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
+ *
+ * The caller must assure the pending_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_pending_queue_pop(
+ struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->pending_buf_q.next,
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
new file mode 100644
index 000000000000..cc337b1de913
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -0,0 +1,858 @@
+/*
+ * S5P/EXYNOS4 SoC series camera host interface media device driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Contact: Sylwester Nawrocki, <s.nawrocki@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/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/media-device.h>
+
+#include "fimc-core.h"
+#include "fimc-mdevice.h"
+#include "mipi-csis.h"
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on);
+/**
+ * fimc_pipeline_prepare - update pipeline information with subdevice pointers
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Caller holds the graph mutex.
+ */
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me)
+{
+ struct media_entity_graph graph;
+ struct v4l2_subdev *sd;
+
+ media_entity_graph_walk_start(&graph, me);
+
+ while ((me = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
+ continue;
+ sd = media_entity_to_v4l2_subdev(me);
+
+ if (sd->grp_id == SENSOR_GROUP_ID)
+ fimc->pipeline.sensor = sd;
+ else if (sd->grp_id == CSIS_GROUP_ID)
+ fimc->pipeline.csis = sd;
+ }
+}
+
+/**
+ * __subdev_set_power - change power state of a single subdev
+ * @sd: subdevice to change power state for
+ * @on: 1 to enable power or 0 to disable
+ *
+ * Return result of s_power subdev operation or -ENXIO if sd argument
+ * is NULL. Return 0 if the subdevice does not implement s_power.
+ */
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int *use_count;
+ int ret;
+
+ if (sd == NULL)
+ return -ENXIO;
+
+ use_count = &sd->entity.use_count;
+ if (on && (*use_count)++ > 0)
+ return 0;
+ else if (!on && (*use_count == 0 || --(*use_count) > 0))
+ return 0;
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+/**
+ * fimc_pipeline_s_power - change power state of all pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @state: 1 to enable power or 0 for power down
+ *
+ * Need to be called with the graph mutex held.
+ */
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor == NULL)
+ return -ENXIO;
+
+ if (state) {
+ ret = __subdev_set_power(fimc->pipeline.csis, 1);
+ if (ret && ret != -ENXIO)
+ return ret;
+ return __subdev_set_power(fimc->pipeline.sensor, 1);
+ }
+
+ ret = __subdev_set_power(fimc->pipeline.sensor, 0);
+ if (ret)
+ return ret;
+ ret = __subdev_set_power(fimc->pipeline.csis, 0);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/**
+ * __fimc_pipeline_initialize - update the pipeline information, enable power
+ * of all pipeline subdevs and the sensor clock
+ * @me: media entity to start graph walk with
+ * @prep: true to acquire sensor (and csis) subdevs
+ *
+ * This function must be called with the graph mutex held.
+ */
+static int __fimc_pipeline_initialize(struct fimc_dev *fimc,
+ struct media_entity *me, bool prep)
+{
+ int ret;
+
+ if (prep)
+ fimc_pipeline_prepare(fimc, me);
+ if (fimc->pipeline.sensor == NULL)
+ return -EINVAL;
+ ret = fimc_md_set_camclk(fimc->pipeline.sensor, true);
+ if (ret)
+ return ret;
+ return fimc_pipeline_s_power(fimc, 1);
+}
+
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool prep)
+{
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_initialize(fimc, me, prep);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Disable power of all subdevs in the pipeline and turn off the external
+ * sensor clock.
+ * Called with the graph mutex held.
+ */
+int __fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor) {
+ ret = fimc_pipeline_s_power(fimc, 0);
+ fimc_md_set_camclk(fimc->pipeline.sensor, false);
+ }
+ return ret == -ENXIO ? 0 : ret;
+}
+
+int fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ struct media_entity *me = &fimc->vid_cap.vfd->entity;
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_shutdown(fimc);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @on: passed as the s_stream call argument
+ */
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on)
+{
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret = 0;
+
+ if (p->sensor == NULL)
+ return -ENODEV;
+
+ if ((on && p->csis) || !on)
+ ret = v4l2_subdev_call(on ? p->csis : p->sensor,
+ video, s_stream, on);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ if ((!on && p->csis) || on)
+ ret = v4l2_subdev_call(on ? p->sensor : p->csis,
+ video, s_stream, on);
+ return ret == -ENOIOCTLCMD ? 0 : ret;
+}
+
+/*
+ * Sensor subdevice helper functions
+ */
+static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info)
+{
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd = NULL;
+
+ if (!s_info || !fmd)
+ return NULL;
+
+ adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
+ if (!adapter)
+ return NULL;
+ sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
+ s_info->pdata->board_info, NULL);
+ if (IS_ERR_OR_NULL(sd)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
+ return NULL;
+ }
+ v4l2_set_subdev_hostdata(sd, s_info);
+ sd->grp_id = SENSOR_GROUP_ID;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
+ s_info->pdata->board_info->type);
+ return sd;
+}
+
+static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!client)
+ return;
+ v4l2_device_unregister_subdev(sd);
+ i2c_unregister_device(client);
+ i2c_put_adapter(client->adapter);
+}
+
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
+{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
+ struct fimc_dev *fd = NULL;
+ int num_clients, ret, i;
+
+ /*
+ * Runtime resume one of the FIMC entities to make sure
+ * the sclk_cam clocks are not globally disabled.
+ */
+ for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
+ if (fmd->fimc[i])
+ fd = fmd->fimc[i];
+ if (!fd)
+ return -ENXIO;
+ ret = pm_runtime_get_sync(&fd->pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
+ num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
+
+ fmd->num_sensors = num_clients;
+ for (i = 0; i < num_clients; i++) {
+ fmd->sensor[i].pdata = &pdata->isp_info[i];
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
+ if (ret)
+ break;
+ fmd->sensor[i].subdev =
+ fimc_md_register_sensor(fmd, &fmd->sensor[i]);
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
+ if (ret)
+ break;
+ }
+ pm_runtime_put(&fd->pdev->dev);
+ return ret;
+}
+
+/*
+ * MIPI CSIS and FIMC platform devices registration.
+ */
+static int fimc_register_callback(struct device *dev, void *p)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ struct fimc_md *fmd = p;
+ int ret;
+
+ if (!fimc || !fimc->pdev)
+ return 0;
+ if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
+ return 0;
+
+ fmd->fimc[fimc->pdev->id] = fimc;
+ ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev);
+ if (ret)
+ return ret;
+ ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev);
+ if (!ret)
+ fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
+ return ret;
+}
+
+static int csis_register_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct platform_device *pdev;
+ struct fimc_md *fmd = p;
+ int id, ret;
+
+ if (!sd)
+ return 0;
+ pdev = v4l2_get_subdevdata(sd);
+ if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
+ return 0;
+ v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
+
+ id = pdev->id < 0 ? 0 : pdev->id;
+ fmd->csis[id].sd = sd;
+ sd->grp_id = CSIS_GROUP_ID;
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret)
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register CSIS subdevice: %d\n", ret);
+ return ret;
+}
+
+/**
+ * fimc_md_register_platform_entities - register FIMC and CSIS media entities
+ */
+static int fimc_md_register_platform_entities(struct fimc_md *fmd)
+{
+ struct device_driver *driver;
+ int ret;
+
+ driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
+ if (!driver)
+ return -ENODEV;
+ ret = driver_for_each_device(driver, NULL, fmd,
+ fimc_register_callback);
+ put_driver(driver);
+ if (ret)
+ return ret;
+
+ driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
+ if (driver) {
+ ret = driver_for_each_device(driver, NULL, fmd,
+ csis_register_callback);
+ put_driver(driver);
+ }
+ return ret;
+}
+
+static void fimc_md_unregister_entities(struct fimc_md *fmd)
+{
+ int i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (fmd->fimc[i] == NULL)
+ continue;
+ fimc_unregister_m2m_device(fmd->fimc[i]);
+ fimc_unregister_capture_device(fmd->fimc[i]);
+ fmd->fimc[i] = NULL;
+ }
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ fmd->csis[i].sd = NULL;
+ }
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
+}
+
+static int fimc_md_register_video_nodes(struct fimc_md *fmd)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
+ if (!fmd->fimc[i])
+ continue;
+
+ if (fmd->fimc[i]->m2m.vfd)
+ ret = video_register_device(fmd->fimc[i]->m2m.vfd,
+ VFL_TYPE_GRABBER, -1);
+ if (ret)
+ break;
+ if (fmd->fimc[i]->vid_cap.vfd)
+ ret = video_register_device(fmd->fimc[i]->vid_cap.vfd,
+ VFL_TYPE_GRABBER, -1);
+ }
+
+ return ret;
+}
+
+/**
+ * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * @fmd: fimc media device
+ * @source: the source entity to create links to all fimc entities from
+ * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
+ * @pad: the source entity pad index
+ * @fimc_id: index of the fimc device for which link should be enabled
+ */
+static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int fimc_id)
+{
+ struct fimc_sensor_info *s_info;
+ struct media_entity *sink;
+ unsigned int flags;
+ int ret, i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ break;
+ /*
+ * Some FIMC variants are not fitted with camera capture
+ * interface. Skip creating a link from sensor for those.
+ */
+ if (sensor->grp_id == SENSOR_GROUP_ID &&
+ !fmd->fimc[i]->variant->has_cam_if)
+ continue;
+
+ flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
+ sink = &fmd->fimc[i]->vid_cap.subdev->entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FIMC_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC capture subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+
+ if (flags == 0)
+ continue;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!WARN_ON(s_info == NULL)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fmd->slock, irq_flags);
+ s_info->host = fmd->fimc[i];
+ spin_unlock_irqrestore(&fmd->slock, irq_flags);
+ }
+ }
+ return 0;
+}
+
+/**
+ * fimc_md_create_links - create default links between registered entities
+ *
+ * Parallel interface sensor entities are connected directly to FIMC capture
+ * entities. The sensors using MIPI CSIS bus are connected through immutable
+ * link with CSI receiver entity specified by mux_id. Any registered CSIS
+ * entity has a link to each registered FIMC capture entity. Enabled links
+ * are created by default between each subsequent registered sensor and
+ * subsequent FIMC capture entity. The number of default active links is
+ * determined by the number of available sensors or FIMC entities,
+ * whichever is less.
+ */
+static int fimc_md_create_links(struct fimc_md *fmd)
+{
+ struct v4l2_subdev *sensor, *csis;
+ struct s5p_fimc_isp_info *pdata;
+ struct fimc_sensor_info *s_info;
+ struct media_entity *source, *sink;
+ int i, pad, fimc_id = 0;
+ int ret = 0;
+ u32 flags;
+
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+
+ sensor = fmd->sensor[i].subdev;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!s_info || !s_info->pdata)
+ continue;
+
+ source = NULL;
+ pdata = s_info->pdata;
+
+ switch (pdata->bus_type) {
+ case FIMC_MIPI_CSI2:
+ if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
+ "Wrong CSI channel id: %d\n", pdata->mux_id))
+ return -EINVAL;
+
+ csis = fmd->csis[pdata->mux_id].sd;
+ if (WARN(csis == NULL,
+ "MIPI-CSI interface specified "
+ "but s5p-csis module is not loaded!\n"))
+ continue;
+
+ ret = media_entity_create_link(&sensor->entity, 0,
+ &csis->entity, CSIS_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
+ sensor->entity.name, csis->entity.name);
+
+ source = &csis->entity;
+ pad = CSIS_PAD_SOURCE;
+ break;
+
+ case FIMC_ITU_601...FIMC_ITU_656:
+ source = &sensor->entity;
+ pad = 0;
+ break;
+
+ default:
+ v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
+ pdata->bus_type);
+ return -EINVAL;
+ }
+ if (source == NULL)
+ continue;
+
+ ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
+ fimc_id++);
+ }
+ /* Create immutable links between each FIMC's subdev and video node */
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ source = &fmd->fimc[i]->vid_cap.subdev->entity;
+ sink = &fmd->fimc[i]->vid_cap.vfd->entity;
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * The peripheral sensor clock management.
+ */
+static int fimc_md_get_clocks(struct fimc_md *fmd)
+{
+ char clk_name[32];
+ struct clk *clock;
+ int i;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
+ clock = clk_get(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clock)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
+ clk_name);
+ return -ENXIO;
+ }
+ fmd->camclk[i].clock = clock;
+ }
+ return 0;
+}
+
+static void fimc_md_put_clocks(struct fimc_md *fmd)
+{
+ int i = FIMC_MAX_CAMCLKS;
+
+ while (--i >= 0) {
+ if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
+ continue;
+ clk_put(fmd->camclk[i].clock);
+ fmd->camclk[i].clock = NULL;
+ }
+}
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on)
+{
+ struct s5p_fimc_isp_info *pdata = s_info->pdata;
+ struct fimc_camclk_info *camclk;
+ int ret = 0;
+
+ if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
+ return -EINVAL;
+
+ if (s_info->clk_on == on)
+ return 0;
+ camclk = &fmd->camclk[pdata->clk_id];
+
+ dbg("camclk %d, f: %lu, clk: %p, on: %d",
+ pdata->clk_id, pdata->clk_frequency, camclk, on);
+
+ if (on) {
+ if (camclk->use_count > 0 &&
+ camclk->frequency != pdata->clk_frequency)
+ return -EINVAL;
+
+ if (camclk->use_count++ == 0) {
+ clk_set_rate(camclk->clock, pdata->clk_frequency);
+ camclk->frequency = pdata->clk_frequency;
+ ret = clk_enable(camclk->clock);
+ }
+ s_info->clk_on = 1;
+ dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
+ clk_get_rate(camclk->clock));
+
+ return ret;
+ }
+
+ if (WARN_ON(camclk->use_count == 0))
+ return 0;
+
+ if (--camclk->use_count == 0) {
+ clk_disable(camclk->clock);
+ s_info->clk_on = 0;
+ dbg("Disabled camclk %d", pdata->clk_id);
+ }
+ return ret;
+}
+
+/**
+ * fimc_md_set_camclk - peripheral sensor clock setup
+ * @sd: sensor subdev to configure sclk_cam clock for
+ * @on: 1 to enable or 0 to disable the clock
+ *
+ * There are 2 separate clock outputs available in the SoC for external
+ * image processors. These clocks are shared between all registered FIMC
+ * devices to which sensors can be attached, either directly or through
+ * the MIPI CSI receiver. The clock is allowed here to be used by
+ * multiple sensors concurrently if they use same frequency.
+ * The per sensor subdev clk_on attribute helps to synchronize accesses
+ * to the sclk_cam clocks from the video and media device nodes.
+ * This function should only be called when the graph mutex is held.
+ */
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
+{
+ struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
+ struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+
+ return __fimc_md_set_camclk(fmd, s_info, on);
+}
+
+static int fimc_md_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ struct v4l2_subdev *sd;
+ struct fimc_dev *fimc;
+ int ret = 0;
+
+ if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(sink->entity);
+ fimc = v4l2_get_subdevdata(sd);
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_pipeline_shutdown(fimc);
+ fimc->pipeline.sensor = NULL;
+ fimc->pipeline.csis = NULL;
+
+ mutex_lock(&fimc->lock);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ mutex_unlock(&fimc->lock);
+ return ret;
+ }
+ /*
+ * Link activation. Enable power of pipeline elements only if the
+ * pipeline is already in use, i.e. its video node is opened.
+ * Recreate the controls destroyed during the link deactivation.
+ */
+ mutex_lock(&fimc->lock);
+ if (fimc->vid_cap.refcnt > 0) {
+ ret = __fimc_pipeline_initialize(fimc, source->entity, true);
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+ }
+ mutex_unlock(&fimc->lock);
+
+ return ret ? -EPIPE : ret;
+}
+
+static ssize_t fimc_md_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (fmd->user_subdev_api)
+ return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
+
+ return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
+}
+
+static ssize_t fimc_md_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+ bool subdev_api;
+ int i;
+
+ if (!strcmp(buf, "vid-dev\n"))
+ subdev_api = false;
+ else if (!strcmp(buf, "sub-dev\n"))
+ subdev_api = true;
+ else
+ return count;
+
+ fmd->user_subdev_api = subdev_api;
+ for (i = 0; i < FIMC_MAX_DEVS; i++)
+ if (fmd->fimc[i])
+ fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
+ return count;
+}
+/*
+ * This device attribute is to select video pipeline configuration method.
+ * There are following valid values:
+ * vid-dev - for V4L2 video node API only, subdevice will be configured
+ * by the host driver.
+ * sub-dev - for media controller API, subdevs must be configured in user
+ * space before starting streaming.
+ */
+static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
+ fimc_md_sysfs_show, fimc_md_sysfs_store);
+
+static int __devinit fimc_md_probe(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev;
+ struct fimc_md *fmd;
+ int ret;
+
+ if (WARN(!pdev->dev.platform_data, "Platform data not specified!\n"))
+ return -EINVAL;
+
+ fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL);
+ if (!fmd)
+ return -ENOMEM;
+
+ spin_lock_init(&fmd->slock);
+ fmd->pdev = pdev;
+
+ strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ sizeof(fmd->media_dev.model));
+ fmd->media_dev.link_notify = fimc_md_link_notify;
+ fmd->media_dev.dev = &pdev->dev;
+
+ v4l2_dev = &fmd->v4l2_dev;
+ v4l2_dev->mdev = &fmd->media_dev;
+ v4l2_dev->notify = fimc_sensor_notify;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
+ dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
+ goto err1;
+ }
+ ret = media_device_register(&fmd->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
+ goto err2;
+ }
+ ret = fimc_md_get_clocks(fmd);
+ if (ret)
+ goto err3;
+
+ fmd->user_subdev_api = false;
+ ret = fimc_md_register_platform_entities(fmd);
+ if (ret)
+ goto err3;
+
+ ret = fimc_md_register_sensor_entities(fmd);
+ if (ret)
+ goto err3;
+ ret = fimc_md_create_links(fmd);
+ if (ret)
+ goto err3;
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+ if (ret)
+ goto err3;
+ ret = fimc_md_register_video_nodes(fmd);
+ if (ret)
+ goto err3;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ if (!ret) {
+ platform_set_drvdata(pdev, fmd);
+ return 0;
+ }
+err3:
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ fimc_md_unregister_entities(fmd);
+err2:
+ v4l2_device_unregister(&fmd->v4l2_dev);
+err1:
+ kfree(fmd);
+ return ret;
+}
+
+static int __devexit fimc_md_remove(struct platform_device *pdev)
+{
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (!fmd)
+ return 0;
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ kfree(fmd);
+ return 0;
+}
+
+static struct platform_driver fimc_md_driver = {
+ .probe = fimc_md_probe,
+ .remove = __devexit_p(fimc_md_remove),
+ .driver = {
+ .name = "s5p-fimc-md",
+ .owner = THIS_MODULE,
+ }
+};
+
+int __init fimc_md_init(void)
+{
+ int ret;
+ request_module("s5p-csis");
+ ret = fimc_register_driver();
+ if (ret)
+ return ret;
+ return platform_driver_register(&fimc_md_driver);
+}
+void __exit fimc_md_exit(void)
+{
+ platform_driver_unregister(&fimc_md_driver);
+ fimc_unregister_driver();
+}
+
+module_init(fimc_md_init);
+module_exit(fimc_md_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
new file mode 100644
index 000000000000..da3780823e7d
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_MDEVICE_H_
+#define FIMC_MDEVICE_H_
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "fimc-core.h"
+#include "mipi-csis.h"
+
+/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */
+#define SENSOR_GROUP_ID (1 << 8)
+#define CSIS_GROUP_ID (1 << 9)
+#define WRITEBACK_GROUP_ID (1 << 10)
+
+#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_CAMCLKS 2
+
+struct fimc_csis_info {
+ struct v4l2_subdev *sd;
+ int id;
+};
+
+struct fimc_camclk_info {
+ struct clk *clock;
+ int use_count;
+ unsigned long frequency;
+};
+
+/**
+ * struct fimc_sensor_info - image data source subdev information
+ * @pdata: sensor's atrributes passed as media device's platform data
+ * @subdev: image sensor v4l2 subdev
+ * @host: fimc device the sensor is currently linked to
+ * @clk_on: sclk_cam clock's state associated with this subdev
+ *
+ * This data structure applies to image sensor and the writeback subdevs.
+ */
+struct fimc_sensor_info {
+ struct s5p_fimc_isp_info *pdata;
+ struct v4l2_subdev *subdev;
+ struct fimc_dev *host;
+ bool clk_on;
+};
+
+/**
+ * struct fimc_md - fimc media device information
+ * @csis: MIPI CSIS subdevs data
+ * @sensor: array of registered sensor subdevs
+ * @num_sensors: actual number of registered sensors
+ * @camclk: external sensor clock information
+ * @fimc: array of registered fimc devices
+ * @media_dev: top level media device
+ * @v4l2_dev: top level v4l2_device holding up the subdevs
+ * @pdev: platform device this media device is hooked up into
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ * @slock: spinlock protecting @sensor array
+ */
+struct fimc_md {
+ struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
+ struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
+ int num_sensors;
+ struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct fimc_dev *fimc[FIMC_MAX_DEVS];
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ bool user_subdev_api;
+ spinlock_t slock;
+};
+
+#define is_subdev_pad(pad) (pad == NULL || \
+ media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+
+#define me_subtype(me) \
+ ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
+
+#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
+
+static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
+{
+ return me->parent == NULL ? NULL :
+ container_of(me->parent, struct fimc_md, media_dev);
+}
+
+static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_lock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_unlock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me);
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool resume);
+int fimc_pipeline_shutdown(struct fimc_dev *fimc);
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state);
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state);
+
+#endif
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 4893b2d91d84..20e664e34163 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -30,7 +30,7 @@ void fimc_hw_reset(struct fimc_dev *dev)
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
writel(cfg, dev->regs + S5P_CIGCTRL);
- udelay(1000);
+ udelay(10);
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg &= ~S5P_CIGCTRL_SWRST;
@@ -41,19 +41,11 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_MSCTRL_FLIP_NORMAL;
- switch (ctx->flip) {
- case FLIP_X_AXIS:
+ if (ctx->hflip)
flip = S5P_MSCTRL_FLIP_X_MIRROR;
- break;
- case FLIP_Y_AXIS:
+ if (ctx->vflip)
flip = S5P_MSCTRL_FLIP_Y_MIRROR;
- break;
- case FLIP_XY_AXIS:
- flip = S5P_MSCTRL_FLIP_180;
- break;
- default:
- break;
- }
+
if (ctx->rotation <= 90)
return flip;
@@ -64,19 +56,11 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
- switch (ctx->flip) {
- case FLIP_X_AXIS:
- flip = S5P_CITRGFMT_FLIP_X_MIRROR;
- break;
- case FLIP_Y_AXIS:
- flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
- break;
- case FLIP_XY_AXIS:
- flip = S5P_CITRGFMT_FLIP_180;
- break;
- default:
- break;
- }
+ if (ctx->hflip)
+ flip |= S5P_CITRGFMT_FLIP_X_MIRROR;
+ if (ctx->vflip)
+ flip |= S5P_CITRGFMT_FLIP_Y_MIRROR;
+
if (ctx->rotation <= 90)
return flip;
@@ -368,17 +352,19 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx)
writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
}
-void fimc_hw_set_effect(struct fimc_ctx *ctx)
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_effect *effect = &ctx->effect;
- u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
-
- cfg |= effect->type;
+ u32 cfg = 0;
- if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
- cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
- cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ if (active) {
+ cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE;
+ cfg |= effect->type;
+ if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+ cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+ cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ }
}
writel(cfg, dev->regs + S5P_CIIMGEFF);
@@ -547,20 +533,24 @@ int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
- S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC);
+ S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC |
+ S5P_CIGCTRL_INVPOLFIELD);
- if (cam->flags & FIMC_CLK_INV_PCLK)
+ if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
cfg |= S5P_CIGCTRL_INVPOLPCLK;
- if (cam->flags & FIMC_CLK_INV_VSYNC)
+ if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLVSYNC;
- if (cam->flags & FIMC_CLK_INV_HREF)
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLHREF;
- if (cam->flags & FIMC_CLK_INV_HSYNC)
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
cfg |= S5P_CIGCTRL_INVPOLHSYNC;
+ if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLFIELD;
+
writel(cfg, fimc->regs + S5P_CIGCTRL);
return 0;
@@ -588,7 +578,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
- if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) {
+ if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) {
cfg = pix_desc[i].cisrcfmt;
bus_width = pix_desc[i].bus_width;
break;
@@ -596,9 +586,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
}
if (i == ARRAY_SIZE(pix_desc)) {
- v4l2_err(&fimc->vid_cap.v4l2_dev,
+ v4l2_err(fimc->vid_cap.vfd,
"Camera color format not supported: %d\n",
- fimc->vid_cap.fmt.code);
+ fimc->vid_cap.mf.code);
return -EINVAL;
}
@@ -608,6 +598,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
else if (bus_width == 16)
cfg |= S5P_CISRCFMT_ITU601_16BIT;
} /* else defaults to ITU-R BT.656 8-bit */
+ } else if (cam->bus_type == FIMC_MIPI_CSI2) {
+ if (fimc_fmt_is_jpeg(f->fmt->color))
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
}
cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
@@ -649,7 +642,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
/* Select ITU B interface, disable Writeback path and test pattern. */
cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
- S5P_CIGCTRL_SELCAM_MIPI_A);
+ S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG);
if (cam->bus_type == FIMC_MIPI_CSI2) {
cfg |= S5P_CIGCTRL_SELCAM_MIPI;
@@ -658,11 +651,18 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
/* TODO: add remaining supported formats. */
- if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) {
+ switch (vid_cap->mf.code) {
+ case V4L2_MBUS_FMT_VYUY8_2X8:
tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
- } else {
- err("camera image format not supported: %d",
- vid_cap->fmt.code);
+ break;
+ case V4L2_MBUS_FMT_JPEG_1X8:
+ tmp = S5P_CSIIMGFMT_USER(1);
+ cfg |= S5P_CIGCTRL_CAM_JPEG;
+ break;
+ default:
+ v4l2_err(fimc->vid_cap.vfd,
+ "Not supported camera pixel format: %d",
+ vid_cap->mf.code);
return -EINVAL;
}
tmp |= (cam->csi_data_align == 32) << 8;
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
index ef056d6605ca..59d79bc2f58a 100644
--- a/drivers/media/video/s5p-fimc/mipi-csis.c
+++ b/drivers/media/video/s5p-fimc/mipi-csis.c
@@ -81,6 +81,12 @@ static char *csi_clock_name[] = {
};
#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
+static const char * const csis_supply_name[] = {
+ "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+ "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+};
+#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+
enum {
ST_POWERED = 1,
ST_STREAMING = 2,
@@ -109,9 +115,9 @@ struct csis_state {
struct platform_device *pdev;
struct resource *regs_res;
void __iomem *regs;
+ struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
int irq;
- struct regulator *supply;
u32 flags;
const struct csis_pix_format *csis_fmt;
struct v4l2_mbus_framefmt format;
@@ -460,6 +466,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
struct resource *regs_res;
struct csis_state *state;
int ret = -ENOMEM;
+ int i;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
@@ -519,14 +526,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
goto e_clkput;
}
- if (!pdata->fixed_phy_vdd) {
- state->supply = regulator_get(&pdev->dev, "vdd");
- if (IS_ERR(state->supply)) {
- ret = PTR_ERR(state->supply);
- state->supply = NULL;
- goto e_clkput;
- }
- }
+ for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = csis_supply_name[i];
+
+ ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto e_clkput;
ret = request_irq(state->irq, s5pcsis_irq_handler, 0,
dev_name(&pdev->dev), state);
@@ -553,7 +559,6 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
/* .. and a pointer to the subdev. */
platform_set_drvdata(pdev, &state->sd);
- state->flags = ST_SUSPENDED;
pm_runtime_enable(&pdev->dev);
return 0;
@@ -561,8 +566,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
e_irqfree:
free_irq(state->irq, state);
e_regput:
- if (state->supply)
- regulator_put(state->supply);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
e_clkput:
clk_disable(state->clock[CSIS_CLK_MUX]);
s5pcsis_clk_put(state);
@@ -575,7 +579,7 @@ e_free:
return ret;
}
-static int s5pcsis_suspend(struct device *dev)
+static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
{
struct s5p_platform_mipi_csis *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev);
@@ -592,21 +596,21 @@ static int s5pcsis_suspend(struct device *dev)
ret = pdata->phy_enable(state->pdev, false);
if (ret)
goto unlock;
- if (state->supply) {
- ret = regulator_disable(state->supply);
- if (ret)
- goto unlock;
- }
+ ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
clk_disable(state->clock[CSIS_CLK_GATE]);
state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
}
- state->flags |= ST_SUSPENDED;
unlock:
mutex_unlock(&state->lock);
return ret ? -EAGAIN : 0;
}
-static int s5pcsis_resume(struct device *dev)
+static int s5pcsis_pm_resume(struct device *dev, bool runtime)
{
struct s5p_platform_mipi_csis *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev);
@@ -618,20 +622,20 @@ static int s5pcsis_resume(struct device *dev)
__func__, state->flags);
mutex_lock(&state->lock);
- if (!(state->flags & ST_SUSPENDED))
+ if (!runtime && !(state->flags & ST_SUSPENDED))
goto unlock;
if (!(state->flags & ST_POWERED)) {
- if (state->supply)
- ret = regulator_enable(state->supply);
+ ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+ state->supplies);
if (ret)
goto unlock;
-
ret = pdata->phy_enable(state->pdev, true);
if (!ret) {
state->flags |= ST_POWERED;
- } else if (state->supply) {
- regulator_disable(state->supply);
+ } else {
+ regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
goto unlock;
}
clk_enable(state->clock[CSIS_CLK_GATE]);
@@ -646,24 +650,26 @@ static int s5pcsis_resume(struct device *dev)
}
#ifdef CONFIG_PM_SLEEP
-static int s5pcsis_pm_suspend(struct device *dev)
+static int s5pcsis_suspend(struct device *dev)
{
- return s5pcsis_suspend(dev);
+ return s5pcsis_pm_suspend(dev, false);
}
-static int s5pcsis_pm_resume(struct device *dev)
+static int s5pcsis_resume(struct device *dev)
{
- int ret;
-
- ret = s5pcsis_resume(dev);
+ return s5pcsis_pm_resume(dev, false);
+}
+#endif
- if (!ret) {
- pm_runtime_disable(dev);
- ret = pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- }
+#ifdef CONFIG_PM_RUNTIME
+static int s5pcsis_runtime_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, true);
+}
- return ret;
+static int s5pcsis_runtime_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, true);
}
#endif
@@ -679,8 +685,7 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
s5pcsis_clk_put(state);
- if (state->supply)
- regulator_put(state->supply);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
media_entity_cleanup(&state->sd.entity);
free_irq(state->irq, state);
@@ -692,8 +697,9 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops s5pcsis_pm_ops = {
- SET_RUNTIME_PM_OPS(s5pcsis_suspend, s5pcsis_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_pm_suspend, s5pcsis_pm_resume)
+ SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
};
static struct platform_driver s5pcsis_driver = {
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
index 0fea3e635d76..c8e3b94bd91d 100644
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -54,12 +54,14 @@
#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define S5P_CIGCTRL_CAM_JPEG (1 << 8)
#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
/* 0 - ITU601; 1 - ITU709 */
#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5)
#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define S5P_CIGCTRL_INVPOLFIELD (1 << 1)
#define S5P_CIGCTRL_INTERLACE (1 << 0)
/* Window offset 2 */
@@ -184,7 +186,6 @@
/* Image effect */
#define S5P_CIIMGEFF 0xd0
-#define S5P_CIIMGEFF_IE_DISABLE (0 << 30)
#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
@@ -286,10 +287,8 @@
#define S5P_CSIIMGFMT_RAW8 0x2a
#define S5P_CSIIMGFMT_RAW10 0x2b
#define S5P_CSIIMGFMT_RAW12 0x2c
-#define S5P_CSIIMGFMT_USER1 0x30
-#define S5P_CSIIMGFMT_USER2 0x31
-#define S5P_CSIIMGFMT_USER3 0x32
-#define S5P_CSIIMGFMT_USER4 0x33
+/* User defined formats. x = 0...16. */
+#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1)
/* Output frame buffer sequence mask */
#define S5P_CIFCNTSEQ 0x1FC
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
index 7dc7eab58b38..8be8b54eb749 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -202,7 +202,7 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
appropraite flags */
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
- if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dec_y_addr) {
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
memcpy(&dst_buf->b->v4l2_buf.timecode,
&src_buf->b->v4l2_buf.timecode,
sizeof(struct v4l2_timecode));
@@ -248,7 +248,7 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
* check which videobuf does it correspond to */
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
/* Check if this is the buffer we're looking for */
- if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dspl_y_addr) {
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) {
list_del(&dst_buf->list);
ctx->dst_queue_cnt--;
dst_buf->b->v4l2_buf.sequence = ctx->sequence;
@@ -940,9 +940,8 @@ static int match_child(struct device *dev, void *data)
return !strcmp(dev_name(dev), (char *)data);
}
-
/* MFC probe function */
-static int __devinit s5p_mfc_probe(struct platform_device *pdev)
+static int s5p_mfc_probe(struct platform_device *pdev)
{
struct s5p_mfc_dev *dev;
struct video_device *vfd;
@@ -1236,7 +1235,7 @@ static const struct dev_pm_ops s5p_mfc_pm_ops = {
NULL)
};
-static struct platform_driver s5p_mfc_pdrv = {
+static struct platform_driver s5p_mfc_driver = {
.probe = s5p_mfc_probe,
.remove = __devexit_p(s5p_mfc_remove),
.driver = {
@@ -1254,15 +1253,15 @@ static int __init s5p_mfc_init(void)
int ret;
pr_info("%s", banner);
- ret = platform_driver_register(&s5p_mfc_pdrv);
+ ret = platform_driver_register(&s5p_mfc_driver);
if (ret)
pr_err("Platform device registration failed.\n");
return ret;
}
-static void __devexit s5p_mfc_exit(void)
+static void __exit s5p_mfc_exit(void)
{
- platform_driver_unregister(&s5p_mfc_pdrv);
+ platform_driver_unregister(&s5p_mfc_driver);
}
module_init(s5p_mfc_init);
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
index 5f4da80051bb..f2481a85e0a2 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -38,7 +38,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
* into kernel. */
mfc_debug_enter();
err = request_firmware((const struct firmware **)&fw_blob,
- "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
if (err != 0) {
mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
return -EINVAL;
@@ -116,7 +116,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
* into kernel. */
mfc_debug_enter();
err = request_firmware((const struct firmware **)&fw_blob,
- "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
if (err != 0) {
mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
return -EINVAL;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
index b2c5052a9c41..725634d9736d 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -165,7 +165,7 @@ static struct mfc_control controls[] = {
.maximum = 32,
.step = 1,
.default_value = 1,
- .is_volatile = 1,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
},
};
@@ -744,9 +744,10 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_g_crop = vidioc_g_crop,
};
-static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count,
- unsigned int *plane_count, unsigned long psize[],
- void *allocators[])
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *buf_count,
+ unsigned int *plane_count, unsigned int psize[],
+ void *allocators[])
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
@@ -824,7 +825,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
return 0;
for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
if (IS_ERR_OR_NULL(ERR_PTR(
- vb2_dma_contig_plane_paddr(vb, i)))) {
+ vb2_dma_contig_plane_dma_addr(vb, i)))) {
mfc_err("Plane mem not allocated\n");
return -EINVAL;
}
@@ -837,13 +838,13 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
i = vb->v4l2_buf.index;
ctx->dst_bufs[i].b = vb;
ctx->dst_bufs[i].cookie.raw.luma =
- vb2_dma_contig_plane_paddr(vb, 0);
+ vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->dst_bufs[i].cookie.raw.chroma =
- vb2_dma_contig_plane_paddr(vb, 1);
+ vb2_dma_contig_plane_dma_addr(vb, 1);
ctx->dst_bufs_cnt++;
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (IS_ERR_OR_NULL(ERR_PTR(
- vb2_dma_contig_plane_paddr(vb, 0)))) {
+ vb2_dma_contig_plane_dma_addr(vb, 0)))) {
mfc_err("Plane memory not allocated\n");
return -EINVAL;
}
@@ -855,7 +856,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
i = vb->v4l2_buf.index;
ctx->src_bufs[i].b = vb;
ctx->src_bufs[i].cookie.stream =
- vb2_dma_contig_plane_paddr(vb, 0);
+ vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->src_bufs_cnt++;
} else {
mfc_err("s5p_mfc_buf_init: unknown queue type\n");
@@ -864,7 +865,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
return 0;
}
-static int s5p_mfc_start_streaming(struct vb2_queue *q)
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1020,7 +1021,7 @@ int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
return ctx->ctrl_handler.error;
}
if (controls[i].is_volatile && ctx->ctrls[i])
- ctx->ctrls[i]->is_volatile = 1;
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
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 fee094a14f4c..ecef127dbc66 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -599,8 +599,8 @@ static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
while (!list_empty(&ctx->ref_queue)) {
mb_entry = list_entry((&ctx->ref_queue)->next,
struct s5p_mfc_buf, list);
- mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
list_del(&mb_entry->list);
ctx->ref_queue_cnt--;
list_add_tail(&mb_entry->list, &ctx->src_queue);
@@ -622,7 +622,7 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -668,14 +668,14 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
spin_lock_irqsave(&dev->irqlock, flags);
src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0);
- src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1);
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
spin_unlock_irqrestore(&dev->irqlock, flags);
spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -703,8 +703,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
if (slice_type >= 0) {
s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
list_for_each_entry(mb_entry, &ctx->src_queue, list) {
- mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
if ((enc_y_addr == mb_y_addr) &&
(enc_c_addr == mb_c_addr)) {
list_del(&mb_entry->list);
@@ -715,8 +715,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
}
}
list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
- mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
if ((enc_y_addr == mb_y_addr) &&
(enc_c_addr == mb_c_addr)) {
list_del(&mb_entry->list);
@@ -1501,20 +1501,21 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
return -EINVAL;
}
for (i = 0; i < fmt->num_planes; i++) {
- if (!vb2_dma_contig_plane_paddr(vb, i)) {
+ if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
mfc_err("failed to get plane cookie\n");
return -EINVAL;
}
mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
vb->v4l2_buf.index, i,
- vb2_dma_contig_plane_paddr(vb, i));
+ vb2_dma_contig_plane_dma_addr(vb, i));
}
return 0;
}
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
- unsigned int *buf_count, unsigned int *plane_count,
- unsigned long psize[], void *allocators[])
+ const struct v4l2_format *fmt,
+ unsigned int *buf_count, unsigned int *plane_count,
+ unsigned int psize[], void *allocators[])
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
@@ -1584,7 +1585,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
i = vb->v4l2_buf.index;
ctx->dst_bufs[i].b = vb;
ctx->dst_bufs[i].cookie.stream =
- vb2_dma_contig_plane_paddr(vb, 0);
+ vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->dst_bufs_cnt++;
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
ret = check_vb_with_fmt(ctx->src_fmt, vb);
@@ -1593,9 +1594,9 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
i = vb->v4l2_buf.index;
ctx->src_bufs[i].b = vb;
ctx->src_bufs[i].cookie.raw.luma =
- vb2_dma_contig_plane_paddr(vb, 0);
+ vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->src_bufs[i].cookie.raw.chroma =
- vb2_dma_contig_plane_paddr(vb, 1);
+ vb2_dma_contig_plane_dma_addr(vb, 1);
ctx->src_bufs_cnt++;
} else {
mfc_err("inavlid queue type: %d\n", vq->type);
@@ -1640,7 +1641,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static int s5p_mfc_start_streaming(struct vb2_queue *q)
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1814,7 +1815,7 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
return ctx->ctrl_handler.error;
}
if (controls[i].is_volatile && ctx->ctrls[i])
- ctx->ctrls[i]->is_volatile = 1;
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
return 0;
}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
index 7b239168c199..e08b21c50ebf 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -1135,7 +1135,7 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
temp_vb->used = 1;
s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_paddr(temp_vb->b, 0), ctx->consumed_stream,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream,
temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
index = temp_vb->b->v4l2_buf.index;
@@ -1172,12 +1172,12 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
}
src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
src_mb->used = 1;
- src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0);
- src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1);
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_mb->used = 1;
- dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -1200,7 +1200,7 @@ static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_dec_desc_buffer(ctx);
mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_paddr(temp_vb->b, 0),
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
0, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
@@ -1219,7 +1219,7 @@ static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_ref_buffer(ctx);
spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
dst_size = vb2_plane_size(dst_mb->b, 0);
s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -1255,7 +1255,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_paddr(temp_vb->b, 0),
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
0, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
index 9c37dee7bc59..f2a09779ec8f 100644
--- a/drivers/media/video/s5p-tv/Kconfig
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -8,7 +8,7 @@
config VIDEO_SAMSUNG_S5P_TV
bool "Samsung TV driver for S5P platform (experimental)"
- depends on PLAT_S5P
+ depends on PLAT_S5P && PM_RUNTIME
depends on EXPERIMENTAL
default n
---help---
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
index 06d6663f4594..0279e6e89feb 100644
--- a/drivers/media/video/s5p-tv/hdmi_drv.c
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -210,20 +210,17 @@ static void hdmi_reg_init(struct hdmi_device *hdev)
/* enable HPD interrupts */
hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
- /* choose HDMI mode */
+ /* choose DVI mode */
hdmi_write_mask(hdev, HDMI_MODE_SEL,
- HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
+ HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
+ hdmi_write_mask(hdev, HDMI_CON_2, ~0,
+ HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN);
/* disable bluescreen */
hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
/* choose bluescreen (fecal) color */
hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
- /* enable AVI packet every vsync, fixes purple line problem */
- hdmi_writeb(hdev, HDMI_AVI_CON, 0x02);
- /* force YUV444, look to CEA-861-D, table 7 for more detail */
- hdmi_writeb(hdev, HDMI_AVI_BYTE(0), 2 << 5);
- hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5);
}
static void hdmi_timing_apply(struct hdmi_device *hdev,
@@ -443,6 +440,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = {
.height = 480,
.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
.field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
},
};
@@ -475,6 +473,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = {
.height = 720,
.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
.field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
},
};
@@ -507,6 +506,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
.height = 1080,
.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
.field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
},
};
@@ -539,6 +539,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
.height = 1080,
.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
.field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
},
};
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h
index e2242243f63d..51ad59b30358 100644
--- a/drivers/media/video/s5p-tv/mixer.h
+++ b/drivers/media/video/s5p-tv/mixer.h
@@ -111,8 +111,6 @@ struct mxr_buffer {
enum mxr_layer_state {
/** layers is not shown */
MXR_LAYER_IDLE = 0,
- /** state between STREAMON and hardware start */
- MXR_LAYER_STREAMING_START,
/** layer is shown */
MXR_LAYER_STREAMING,
/** state before STREAMOFF is finished */
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c
index 58f0ba49580f..de8270c2b6e7 100644
--- a/drivers/media/video/s5p-tv/mixer_grp_layer.c
+++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c
@@ -86,7 +86,7 @@ static void mxr_graph_buffer_set(struct mxr_layer *layer,
dma_addr_t addr = 0;
if (buf)
- addr = vb2_dma_contig_plane_paddr(&buf->vb, 0);
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
mxr_reg_graph_buffer(layer->mdev, layer->idx, addr);
}
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c
index 38dac672aa1c..4800a3cbb297 100644
--- a/drivers/media/video/s5p-tv/mixer_reg.c
+++ b/drivers/media/video/s5p-tv/mixer_reg.c
@@ -90,7 +90,7 @@ void mxr_reg_reset(struct mxr_device *mdev)
mxr_vsync_set_update(mdev, MXR_DISABLE);
/* set output in RGB888 mode */
- mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_YUV444);
+ mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888);
/* 16 beat burst in DMA */
mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST,
@@ -376,6 +376,12 @@ void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
spin_lock_irqsave(&mdev->reg_slock, flags);
mxr_vsync_set_update(mdev, MXR_DISABLE);
+ /* selecting colorspace accepted by output */
+ if (fmt->colorspace == V4L2_COLORSPACE_JPEG)
+ val |= MXR_CFG_OUT_YUV444;
+ else
+ val |= MXR_CFG_OUT_RGB888;
+
/* choosing between interlace and progressive mode */
if (fmt->field == V4L2_FIELD_INTERLACED)
val |= MXR_CFG_SCAN_INTERLACE;
@@ -394,7 +400,8 @@ void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
else
WARN(1, "unrecognized mbus height %u!\n", fmt->height);
- mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK);
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK |
+ MXR_CFG_OUT_MASK);
val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0;
vp_write_mask(mdev, VP_MODE, val,
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
index 43ac22f35bc7..e16d3a4bc1dc 100644
--- a/drivers/media/video/s5p-tv/mixer_video.c
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -727,8 +727,8 @@ static const struct v4l2_file_operations mxr_fops = {
.unlocked_ioctl = video_ioctl2,
};
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
void *alloc_ctxs[])
{
struct mxr_layer *layer = vb2_get_drv_priv(vq);
@@ -764,19 +764,10 @@ static void buf_queue(struct vb2_buffer *vb)
struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
struct mxr_device *mdev = layer->mdev;
unsigned long flags;
- int must_start = 0;
spin_lock_irqsave(&layer->enq_slock, flags);
- if (layer->state == MXR_LAYER_STREAMING_START) {
- layer->state = MXR_LAYER_STREAMING;
- must_start = 1;
- }
list_add_tail(&buffer->list, &layer->enq_list);
spin_unlock_irqrestore(&layer->enq_slock, flags);
- if (must_start) {
- layer->ops.stream_set(layer, MXR_ENABLE);
- mxr_streamer_get(mdev);
- }
mxr_dbg(mdev, "queuing buffer\n");
}
@@ -797,13 +788,19 @@ static void wait_unlock(struct vb2_queue *vq)
mutex_unlock(&layer->mutex);
}
-static int start_streaming(struct vb2_queue *vq)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct mxr_layer *layer = vb2_get_drv_priv(vq);
struct mxr_device *mdev = layer->mdev;
unsigned long flags;
mxr_dbg(mdev, "%s\n", __func__);
+
+ if (count == 0) {
+ mxr_dbg(mdev, "no output buffers queued\n");
+ return -EINVAL;
+ }
+
/* block any changes in output configuration */
mxr_output_get(mdev);
@@ -814,9 +811,12 @@ static int start_streaming(struct vb2_queue *vq)
layer->ops.format_set(layer);
/* enabling layer in hardware */
spin_lock_irqsave(&layer->enq_slock, flags);
- layer->state = MXR_LAYER_STREAMING_START;
+ layer->state = MXR_LAYER_STREAMING;
spin_unlock_irqrestore(&layer->enq_slock, flags);
+ layer->ops.stream_set(layer, MXR_ENABLE);
+ mxr_streamer_get(mdev);
+
return 0;
}
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c
index 6950ed8ac1a0..f3bb2e34cb51 100644
--- a/drivers/media/video/s5p-tv/mixer_vp_layer.c
+++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c
@@ -97,9 +97,9 @@ static void mxr_vp_buffer_set(struct mxr_layer *layer,
mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
return;
}
- luma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 0);
+ luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
if (layer->fmt->num_subframes == 2) {
- chroma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 1);
+ chroma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 1);
} else {
/* FIXME: mxr_get_plane_size compute integer division,
* which is slow and should not be performed in interrupt */
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h
index ac93ad6f2bc3..33247d13e4c0 100644
--- a/drivers/media/video/s5p-tv/regs-hdmi.h
+++ b/drivers/media/video/s5p-tv/regs-hdmi.h
@@ -127,6 +127,10 @@
#define HDMI_BLUE_SCR_EN (1 << 5)
#define HDMI_EN (1 << 0)
+/* HDMI_CON_2 */
+#define HDMI_DVI_PERAMBLE_EN (1 << 5)
+#define HDMI_DVI_BAND_EN (1 << 1)
+
/* HDMI_PHY_STATUS */
#define HDMI_PHY_STATUS_READY (1 << 0)
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h
index 3c8442609c1a..158abb43d0a4 100644
--- a/drivers/media/video/s5p-tv/regs-mixer.h
+++ b/drivers/media/video/s5p-tv/regs-mixer.h
@@ -67,6 +67,7 @@
/* bits for MXR_CFG */
#define MXR_CFG_OUT_YUV444 (0 << 8)
#define MXR_CFG_OUT_RGB888 (1 << 8)
+#define MXR_CFG_OUT_MASK (1 << 8)
#define MXR_CFG_DST_SDO (0 << 7)
#define MXR_CFG_DST_HDMI (1 << 7)
#define MXR_CFG_DST_MASK (1 << 7)
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
index 4dddd6bd635b..8cec67ef48c9 100644
--- a/drivers/media/video/s5p-tv/sdo_drv.c
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -170,6 +170,7 @@ static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
fmt->height = sdev->fmt->height;
fmt->code = V4L2_MBUS_FMT_FIXED;
fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
return 0;
}
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index f2ae405c74ac..5cfdbc78b918 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -793,7 +793,6 @@ static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
else
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
- v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
default:
@@ -1345,35 +1344,57 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat
static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct saa711x_state *state = to_state(sd);
- int reg1e;
+ int reg1f, reg1e;
- *std = V4L2_STD_ALL;
- if (state->ident != V4L2_IDENT_SAA7115) {
- int reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ /*
+ * The V4L2 core already initializes std with all supported
+ * Standards. All driver needs to do is to mask it, to remove
+ * standards that don't apply from the mask
+ */
- if (reg1f & 0x20)
- *std = V4L2_STD_525_60;
- else
- *std = V4L2_STD_625_50;
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
- return 0;
- }
+ /* horizontal/vertical not locked */
+ if (reg1f & 0x40)
+ goto ret;
+
+ if (reg1f & 0x20)
+ *std &= V4L2_STD_525_60;
+ else
+ *std &= V4L2_STD_625_50;
+
+ if (state->ident != V4L2_IDENT_SAA7115)
+ goto ret;
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
switch (reg1e & 0x03) {
case 1:
- *std = V4L2_STD_NTSC;
+ *std &= V4L2_STD_NTSC;
break;
case 2:
- *std = V4L2_STD_PAL;
+ /*
+ * V4L2_STD_PAL just cover the european PAL standards.
+ * This is wrong, as the device could also be using an
+ * other PAL standard.
+ */
+ *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_PAL_M | V4L2_STD_PAL_60;
break;
case 3:
- *std = V4L2_STD_SECAM;
+ *std &= V4L2_STD_SECAM;
break;
default:
+ /* Can't detect anything */
break;
}
+
+ v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
+
+ret:
+ v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
+
return 0;
}
@@ -1601,7 +1622,6 @@ static int saa711x_probe(struct i2c_client *client,
V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
- state->gain->is_volatile = 1;
sd->ctrl_handler = hdl;
if (hdl->error) {
int err = hdl->error;
@@ -1610,8 +1630,7 @@ static int saa711x_probe(struct i2c_client *client,
kfree(state);
return err;
}
- state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
- v4l2_ctrl_cluster(2, &state->agc);
+ v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
state->input = -1;
state->output = SAA7115_IPORT_ON;
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 8a5ff4d3cf15..a646ccf51696 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index bc8d6bba8ee5..9b550687213a 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -843,10 +843,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
int saa7134_ir_start(struct saa7134_dev *dev);
void saa7134_ir_stop(struct saa7134_dev *dev);
#else
-#define saa7134_input_init1(dev) (0)
-#define saa7134_input_fini(dev) (0)
-#define saa7134_input_irq(dev) (0)
-#define saa7134_probe_i2c_ir(dev) (0)
-#define saa7134_ir_start(dev) (0)
-#define saa7134_ir_stop(dev) (0)
+#define saa7134_input_init1(dev) ((void)0)
+#define saa7134_input_fini(dev) ((void)0)
+#define saa7134_input_irq(dev) ((void)0)
+#define saa7134_probe_i2c_ir(dev) ((void)0)
+#define saa7134_ir_start(dev) ((void)0)
+#define saa7134_ir_stop(dev) ((void)0)
#endif
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
index 6303a8e60eac..ecd5811dc486 100644
--- a/drivers/media/video/saa7164/Makefile
+++ b/drivers/media/video/saa7164/Makefile
@@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
-EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c
index 69822a4e7275..971591d6450f 100644
--- a/drivers/media/video/saa7164/saa7164-cards.c
+++ b/drivers/media/video/saa7164/saa7164-cards.c
@@ -203,6 +203,66 @@ struct saa7164_board saa7164_boards[] = {
.i2c_reg_len = REGLEN_8bit,
} },
},
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x1d,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1b,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1c,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
[SAA7164_BOARD_HAUPPAUGE_HVR2250] = {
.name = "Hauppauge WinTV-HVR2250",
.porta = SAA7164_MPEG_DVB,
@@ -387,6 +447,62 @@ struct saa7164_board saa7164_boards[] = {
.i2c_reg_len = REGLEN_8bit,
} },
},
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x23,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x21,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x22,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x24,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x25,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
};
const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards);
@@ -426,6 +542,14 @@ struct saa7164_subid saa7164_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x8851,
.card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8940,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8953,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5,
},
};
const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids);
@@ -469,6 +593,8 @@ void saa7164_gpio_setup(struct saa7164_dev *dev)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
case SAA7164_BOARD_HAUPPAUGE_HVR2250:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
@@ -549,6 +675,8 @@ void saa7164_card_setup(struct saa7164_dev *dev)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
case SAA7164_BOARD_HAUPPAUGE_HVR2250:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
index f65eab63ca87..5c5cc3ebf9bd 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -475,6 +475,8 @@ int saa7164_dvb_register(struct saa7164_port *port)
case SAA7164_BOARD_HAUPPAUGE_HVR2200:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
i2c_bus = &dev->i2c_bus[port->nr + 1];
switch (port->nr) {
case 0:
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 6678bf1e7816..742b34103b5d 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -82,6 +82,8 @@
#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6
#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7
#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10
#define SAA7164_MAX_UNITS 8
#define SAA7164_TS_NUMBER_OF_LINES 312
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index e54089802b6b..f390682629cf 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -90,7 +90,6 @@
struct sh_mobile_ceu_buffer {
struct vb2_buffer vb; /* v4l buffer must be first */
struct list_head queue;
- enum v4l2_mbus_pixelcode code;
};
struct sh_mobile_ceu_dev {
@@ -100,7 +99,8 @@ struct sh_mobile_ceu_dev {
unsigned int irq;
void __iomem *base;
- unsigned long video_limit;
+ size_t video_limit;
+ size_t buf_total;
spinlock_t lock; /* Protects video buffer lists */
struct list_head capture;
@@ -121,7 +121,7 @@ struct sh_mobile_ceu_dev {
};
struct sh_mobile_ceu_cam {
- /* CEU offsets within scaled by the CEU camera output */
+ /* CEU offsets within the camera output, before the CEU scaler */
unsigned int ceu_left;
unsigned int ceu_top;
/* Client output, as seen by the CEU */
@@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
return container_of(vb, struct sh_mobile_ceu_buffer, vb);
}
-static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
-{
- unsigned long flags;
-
- flags = SOCAM_MASTER |
- SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH;
-
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
- flags |= SOCAM_DATAWIDTH_8;
-
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
- flags |= SOCAM_DATAWIDTH_16;
-
- if (flags & SOCAM_DATAWIDTH_MASK)
- return flags;
-
- return 0;
-}
-
static void ceu_write(struct sh_mobile_ceu_dev *priv,
unsigned long reg_offs, u32 data)
{
@@ -216,34 +192,62 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
/*
* Videobuf operations
*/
+
+/*
+ * .queue_setup() is called to check, whether the driver can accept the
+ * requested number of buffers and to fill in plane sizes
+ * for the current frame format if required
+ */
static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
- unsigned long sizes[], void *alloc_ctxs[])
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
+ int bytes_per_line;
+ unsigned int height;
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ height = icd->user_height;
+ }
if (bytes_per_line < 0)
return bytes_per_line;
- *num_planes = 1;
+ sizes[0] = bytes_per_line * height;
- pcdev->sequence = 0;
- sizes[0] = bytes_per_line * icd->user_height;
alloc_ctxs[0] = pcdev->alloc_ctx;
+ if (!vq->num_buffers)
+ pcdev->sequence = 0;
+
if (!*count)
*count = 2;
- if (pcdev->video_limit) {
- if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit)
- *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
+ /* If *num_planes != 0, we have already verified *count. */
+ if (pcdev->video_limit && !*num_planes) {
+ size_t size = PAGE_ALIGN(sizes[0]) * *count;
+
+ if (size + pcdev->buf_total > pcdev->video_limit)
+ *count = (pcdev->video_limit - pcdev->buf_total) /
+ PAGE_ALIGN(sizes[0]);
}
- dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]);
+ *num_planes = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
return 0;
}
@@ -267,6 +271,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
unsigned long top1, top2;
unsigned long bottom1, bottom2;
u32 status;
+ bool planar;
int ret = 0;
/*
@@ -312,19 +317,31 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
bottom2 = CDBCR;
}
- phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0);
-
- ceu_write(pcdev, top1, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->user_width;
- ceu_write(pcdev, bottom1, phys_addr_bottom);
- }
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
+ planar = true;
+ break;
+ default:
+ planar = false;
+ }
+
+ ceu_write(pcdev, top1, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ if (planar)
+ phys_addr_bottom = phys_addr_top + icd->user_width;
+ else
+ phys_addr_bottom = phys_addr_top +
+ soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ ceu_write(pcdev, bottom1, phys_addr_bottom);
+ }
+
+ if (planar) {
phys_addr_top += icd->user_width *
icd->user_height;
ceu_write(pcdev, top2, phys_addr_top);
@@ -341,23 +358,40 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
{
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+
+ /* Added list head initialization on alloc */
+ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+
+ return 0;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
+{
struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
- struct sh_mobile_ceu_buffer *buf;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ unsigned long size;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- unsigned long size;
if (bytes_per_line < 0)
- return bytes_per_line;
+ goto error;
- buf = to_ceu_vb(vb);
+ size = icd->user_height * bytes_per_line;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
- /* Added list head initialization on alloc */
- WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
-
#ifdef DEBUG
/*
* This can be useful if you want to see if we actually fill
@@ -367,31 +401,6 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
#endif
- BUG_ON(NULL == icd->current_fmt);
-
- size = icd->user_height * bytes_per_line;
-
- if (vb2_plane_size(vb, 0) < size) {
- dev_err(icd->parent, "Buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -ENOBUFS;
- }
-
- vb2_set_plane_payload(vb, 0, size);
-
- return 0;
-}
-
-static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
spin_lock_irq(&pcdev->lock);
list_add_tail(&buf->queue, &pcdev->capture);
@@ -405,6 +414,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
sh_mobile_ceu_capture(pcdev);
}
spin_unlock_irq(&pcdev->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
@@ -429,11 +443,23 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
if (buf->queue.next)
list_del_init(&buf->queue);
+ pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
spin_unlock_irq(&pcdev->lock);
}
static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
{
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
/* This is for locking debugging only */
INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
return 0;
@@ -535,19 +561,29 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
pm_runtime_get_sync(ici->v4l2_dev.dev);
+ pcdev->buf_total = 0;
+
ret = sh_mobile_ceu_soft_reset(pcdev);
csi2_sd = find_csi2(pcdev);
+ if (csi2_sd)
+ csi2_sd->grp_id = (long)icd;
ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
- if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) {
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
pm_runtime_put_sync(ici->v4l2_dev.dev);
- } else {
- pcdev->icd = icd;
- ret = 0;
+ return ret;
}
- return ret;
+ /*
+ * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (ret == -ENODEV && csi2_sd)
+ csi2_sd->grp_id = 0;
+ pcdev->icd = icd;
+
+ return 0;
}
/* Called with .video_lock held */
@@ -560,6 +596,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
BUG_ON(icd != pcdev->icd);
v4l2_subdev_call(csi2_sd, core, s_power, 0);
+ if (csi2_sd)
+ csi2_sd->grp_id = 0;
/* disable capture, disable interrupts */
ceu_write(pcdev, CEIER, 0);
sh_mobile_ceu_soft_reset(pcdev);
@@ -628,22 +666,22 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
left_offset = cam->ceu_left;
top_offset = cam->ceu_top;
- /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */
+ WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+ width = icd->user_width;
+
if (pcdev->image_mode) {
in_width = cam->width;
if (!pcdev->is_16bit) {
in_width *= 2;
left_offset *= 2;
}
- width = icd->user_width;
- cdwdr_width = icd->user_width;
+ cdwdr_width = width;
} else {
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ int bytes_per_line = soc_mbus_bytes_per_line(width,
icd->current_fmt->host_fmt);
unsigned int w_factor;
- width = icd->user_width;
-
switch (icd->current_fmt->host_fmt->packing) {
case SOC_MBUS_PACKING_2X8_PADHI:
w_factor = 2;
@@ -653,10 +691,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
}
in_width = cam->width * w_factor;
- left_offset = left_offset * w_factor;
+ left_offset *= w_factor;
if (bytes_per_line < 0)
- cdwdr_width = icd->user_width;
+ cdwdr_width = width;
else
cdwdr_width = bytes_per_line;
}
@@ -664,7 +702,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
height = icd->user_height;
in_height = cam->height;
if (V4L2_FIELD_NONE != pcdev->field) {
- height /= 2;
+ height = (height / 2) & ~3;
in_height /= 2;
top_offset /= 2;
cdwdr_width *= 2;
@@ -686,6 +724,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
ceu_write(pcdev, CAMOR, camor);
ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+ /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
ceu_write(pcdev, CFSZR, (height << 16) | width);
ceu_write(pcdev, CDWDR, cdwdr_width);
}
@@ -723,66 +762,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
ceu_write(pcdev, CAPSR, capsr);
}
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ if (pcdev->csi2_pdev) {
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+ if (csi2_sd && csi2_sd->grp_id == (u32)icd)
+ return csi2_sd;
+ }
+
+ return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
/* Capture is not running, no interrupts, no locking needed */
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret;
- unsigned long camera_flags, common_flags, value;
- int yuv_lineskip;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long value, common_flags = CEU_BUS_FLAGS;
u32 capsr = capture_save_reset(pcdev);
+ unsigned int yuv_lineskip;
+ int ret;
- camera_flags = icd->ops->query_bus_param(icd);
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- make_bus_param(pcdev));
- if (!common_flags)
- return -EINVAL;
+ /*
+ * If the client doesn't implement g_mbus_config, we just use our
+ * platform data
+ */
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ if (!common_flags)
+ return -EINVAL;
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
/* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- switch (common_flags & SOCAM_DATAWIDTH_MASK) {
- case SOCAM_DATAWIDTH_8:
- pcdev->is_16bit = 0;
- break;
- case SOCAM_DATAWIDTH_16:
+ if (icd->current_fmt->host_fmt->bits_per_sample > 8)
pcdev->is_16bit = 1;
- break;
- default:
- return -EINVAL;
- }
+ else
+ pcdev->is_16bit = 0;
ceu_write(pcdev, CRCNTR, 0);
ceu_write(pcdev, CRCMPR, 0);
value = 0x00000010; /* data fetch by default */
- yuv_lineskip = 0;
+ yuv_lineskip = 0x10;
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
- yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+ /* convert 4:2:2 -> 4:2:0 */
+ yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
/* fall-through */
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
@@ -808,8 +874,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
- value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
- value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+ value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
value |= pcdev->is_16bit ? 1 << 12 : 0;
/* CSI2 mode */
@@ -852,9 +918,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
* using 7 we swap the data bytes to match the incoming order:
* D0, D1, D2, D3, D4, D5, D6, D7
*/
- value = 0x00000017;
- if (yuv_lineskip)
- value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+ value = 0x00000007 | yuv_lineskip;
ceu_write(pcdev, CDOCR, value);
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
@@ -875,13 +939,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned long camera_flags, common_flags;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ unsigned long common_flags = CEU_BUS_FLAGS;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ int ret;
- camera_flags = icd->ops->query_bus_param(icd);
- common_flags = soc_camera_bus_param_compatible(camera_flags,
- make_bus_param(pcdev));
- if (!common_flags || buswidth > 16 ||
- (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ else if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (!common_flags || buswidth > 16)
return -EINVAL;
return 0;
@@ -891,26 +961,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
.name = "NV12",
- .bits_per_sample = 12,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.name = "NV21",
- .bits_per_sample = 12,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV16,
.name = "NV16",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.name = "NV61",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_NONE,
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
};
@@ -920,6 +990,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
{
return fmt->packing == SOC_MBUS_PACKING_NONE ||
(fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
+ (fmt->bits_per_sample == 8 &&
fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
(fmt->bits_per_sample > 8 &&
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
@@ -927,6 +999,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct soc_camera_device,
+ ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ ceu_write(pcdev, CLFCR, !ctrl->val);
+ return 0;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+ .s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
struct soc_camera_format_xlate *xlate)
{
@@ -952,6 +1056,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
}
if (!pcdev->pdata->csi2) {
+ /* Are there any restrictions in the CSI-2 case? */
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
return 0;
@@ -962,6 +1067,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
struct v4l2_rect rect;
int shift = 0;
+ /* Add our control */
+ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ if (icd->ctrl_handler.error)
+ return icd->ctrl_handler.error;
+
/* FIXME: subwindow is lost between close / open */
/* Cache current client geometry */
@@ -1004,9 +1115,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
cam->width = mf.width;
cam->height = mf.height;
- cam->width = mf.width;
- cam->height = mf.height;
-
icd->host_priv = cam;
} else {
cam = icd->host_priv;
@@ -1278,6 +1386,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
unsigned int max_width, max_height;
struct v4l2_cropcap cap;
+ bool ceu_1to1;
int ret;
ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
@@ -1287,7 +1396,14 @@ static int client_s_fmt(struct soc_camera_device *icd,
dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
- if ((width == mf->width && height == mf->height) || !ceu_can_scale)
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ ceu_1to1 = true;
+ goto update_cache;
+ }
+
+ ceu_1to1 = false;
+ if (!ceu_can_scale)
goto update_cache;
cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1327,7 +1443,10 @@ update_cache:
if (ret < 0)
return ret;
- update_subrect(cam);
+ if (ceu_1to1)
+ cam->subrect = cam->rect;
+ else
+ update_subrect(cam);
return 0;
}
@@ -1414,7 +1533,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
capsr = capture_save_reset(pcdev);
dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
- /* 1. - 2. Apply iterative camera S_CROP for new input window. */
+ /*
+ * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+ * actual camera rectangle.
+ */
ret = client_s_crop(icd, a, &cam_crop);
if (ret < 0)
return ret;
@@ -1498,8 +1620,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
ceu_write(pcdev, CFLCR, cflcr);
}
- icd->user_width = out_width;
- icd->user_height = out_height;
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ /* Offsets are applied at the CEU scaling filter input */
cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
@@ -1538,7 +1661,7 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
* CEU crop, mapped backed onto the client input (subrect).
*/
static void calculate_client_output(struct soc_camera_device *icd,
- struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
{
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct device *dev = icd->parent;
@@ -1574,8 +1697,8 @@ static void calculate_client_output(struct soc_camera_device *icd,
dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
/*
- * 4. Calculate client output window by applying combined scales to real
- * input window.
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
*/
mf->width = scale_down(cam->rect.width, scale_h);
mf->height = scale_down(cam->rect.height, scale_v);
@@ -1600,8 +1723,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
bool image_mode;
enum v4l2_field field;
- dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
-
switch (pix->field) {
default:
pix->field = V4L2_FIELD_NONE;
@@ -1622,8 +1743,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
- /* 1.-4. Calculate client output geometry */
- calculate_client_output(icd, &f->fmt.pix, &mf);
+ /* 1.-4. Calculate desired client output geometry */
+ calculate_client_output(icd, pix, &mf);
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
@@ -1639,6 +1760,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
image_mode = false;
}
+ dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+ pix->width, pix->height);
+
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
/* 5. - 9. */
@@ -1700,6 +1824,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
pcdev->field = field;
pcdev->image_mode = image_mode;
+ /* CFSZR requirement */
+ pix->width &= ~3;
+ pix->height &= ~3;
+
return 0;
}
@@ -1725,7 +1853,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
/* FIXME: calculate using depth and bus width */
- v4l_bound_align_image(&pix->width, 2, 2560, 1,
+ /* CFSZR requires height and width to be 4-pixel aligned */
+ v4l_bound_align_image(&pix->width, 2, 2560, 2,
&pix->height, 4, 1920, 2, 0);
width = pix->width;
@@ -1778,6 +1907,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
pix->height = height;
}
+ pix->width &= ~3;
+ pix->height &= ~3;
+
dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
__func__, ret, pix->pixelformat, pix->width, pix->height);
@@ -1824,8 +1956,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
out_height != f.fmt.pix.height))
ret = -EINVAL;
if (!ret) {
- icd->user_width = out_width;
- icd->user_height = out_height;
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
ret = sh_mobile_ceu_set_bus_param(icd,
icd->current_fmt->host_fmt->fourcc);
}
@@ -1869,55 +2001,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
return vb2_queue_init(q);
}
-static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- u32 val;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- val = ceu_read(pcdev, CLFCR);
- ctrl->value = val ^ 1;
- return 0;
- }
- return -ENOIOCTLCMD;
-}
-
-static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- ceu_write(pcdev, CLFCR, !ctrl->value);
- return 0;
- }
- return -EINVAL;
- }
- return -ENOIOCTLCMD;
-}
-
-static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Low-pass filter",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
@@ -1929,14 +2012,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.set_livecrop = sh_mobile_ceu_set_livecrop,
.set_fmt = sh_mobile_ceu_set_fmt,
.try_fmt = sh_mobile_ceu_try_fmt,
- .set_ctrl = sh_mobile_ceu_set_ctrl,
- .get_ctrl = sh_mobile_ceu_get_ctrl,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
.set_bus_param = sh_mobile_ceu_set_bus_param,
.init_videobuf2 = sh_mobile_ceu_init_videobuf,
- .controls = sh_mobile_ceu_controls,
- .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
};
struct bus_wait {
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
index 2893a0134c7e..37706eb81f25 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -19,6 +19,7 @@
#include <media/sh_mobile_ceu.h>
#include <media/sh_mobile_csi2.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -35,11 +36,10 @@ struct sh_csi2 {
struct v4l2_subdev subdev;
struct list_head list;
unsigned int irq;
+ unsigned long mipi_flags;
void __iomem *base;
struct platform_device *pdev;
struct sh_csi2_client_config *client;
- unsigned long (*query_bus_param)(struct soc_camera_device *);
- int (*set_bus_param)(struct soc_camera_device *, unsigned long);
};
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
@@ -127,9 +127,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
return 0;
}
+static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+
+ return 0;
+}
+
+static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+ struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
+ .flags = priv->mipi_flags};
+
+ return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
+}
+
static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
.s_mbus_fmt = sh_csi2_s_fmt,
.try_mbus_fmt = sh_csi2_try_fmt,
+ .g_mbus_config = sh_csi2_g_mbus_config,
+ .s_mbus_config = sh_csi2_s_mbus_config,
};
static void sh_csi2_hwinit(struct sh_csi2 *priv)
@@ -144,11 +169,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
udelay(5);
iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
- if (priv->client->lanes & 3)
- tmp |= priv->client->lanes & 3;
- else
- /* Default - both lanes */
- tmp |= 3;
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (priv->client->lanes == 1)
+ tmp |= 1;
+ else
+ /* Default - both lanes */
+ tmp |= 3;
+ break;
+ case SH_CSI2I:
+ if (!priv->client->lanes || priv->client->lanes > 4)
+ /* Default - all 4 lanes */
+ tmp |= 0xf;
+ else
+ tmp |= (1 << priv->client->lanes) - 1;
+ }
if (priv->client->phy == SH_CSI2_PHY_MAIN)
tmp |= 0x8000;
@@ -163,38 +198,18 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
}
-static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
-
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
static int sh_csi2_client_connect(struct sh_csi2 *priv)
{
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
- struct v4l2_subdev *sd, *csi2_sd = &priv->subdev;
- struct soc_camera_device *icd = NULL;
+ struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
struct device *dev = v4l2_get_subdevdata(&priv->subdev);
- int i;
+ struct v4l2_mbus_config cfg;
+ unsigned long common_flags, csi2_flags;
+ int i, ret;
- v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev)
- if (sd->grp_id) {
- icd = (struct soc_camera_device *)sd->grp_id;
- break;
- }
-
- if (!icd)
- return -EINVAL;
+ if (priv->client)
+ return -EBUSY;
for (i = 0; i < pdata->num_clients; i++)
if (&pdata->clients[i].pdev->dev == icd->pdev)
@@ -205,14 +220,41 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
if (i == pdata->num_clients)
return -ENODEV;
- priv->client = pdata->clients + i;
+ /* Check if we can support this camera */
+ csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (pdata->clients[i].lanes != 1)
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case SH_CSI2I:
+ switch (pdata->clients[i].lanes) {
+ default:
+ csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+ case 3:
+ csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+ case 2:
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ }
+ }
- priv->set_bus_param = icd->ops->set_bus_param;
- priv->query_bus_param = icd->ops->query_bus_param;
- icd->ops->set_bus_param = sh_csi2_set_bus_param;
- icd->ops->query_bus_param = sh_csi2_query_bus_param;
+ cfg.type = V4L2_MBUS_CSI2;
+ ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
+ if (ret == -ENOIOCTLCMD)
+ common_flags = csi2_flags;
+ else if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ csi2_flags);
+ else
+ common_flags = 0;
- csi2_sd->grp_id = (long)icd;
+ if (!common_flags)
+ return -EINVAL;
+
+ /* All good: camera MIPI configuration supported */
+ priv->mipi_flags = common_flags;
+ priv->client = pdata->clients + i;
pm_runtime_get_sync(dev);
@@ -223,16 +265,10 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
{
- struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+ if (!priv->client)
+ return;
priv->client = NULL;
- priv->subdev.grp_id = 0;
-
- /* Driver is about to be unbound */
- icd->ops->set_bus_param = priv->set_bus_param;
- icd->ops->query_bus_param = priv->query_bus_param;
- priv->set_bus_param = NULL;
- priv->query_bus_param = NULL;
pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
}
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 5bdfe7e16bc1..b72580c38957 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -50,49 +50,65 @@ static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
-static int soc_camera_power_set(struct soc_camera_device *icd,
- struct soc_camera_link *icl,
- int power_on)
+static int soc_camera_power_on(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
{
- int ret;
-
- if (power_on) {
- ret = regulator_bulk_enable(icl->num_regulators,
- icl->regulators);
- if (ret < 0) {
- dev_err(icd->pdev, "Cannot enable regulators\n");
- return ret;
- }
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = regulator_bulk_enable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Cannot enable regulators\n");
+ return ret;
+ }
- if (icl->power)
- ret = icl->power(icd->pdev, power_on);
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 1);
if (ret < 0) {
dev_err(icd->pdev,
"Platform failed to power-on the camera.\n");
-
- regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
- return ret;
+ goto elinkpwr;
}
- } else {
- ret = 0;
- if (icl->power)
- ret = icl->power(icd->pdev, 0);
+ }
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto esdpwr;
+
+ return 0;
+
+esdpwr:
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+elinkpwr:
+ regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ return ret;
+}
+
+static int soc_camera_power_off(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = v4l2_subdev_call(sd, core, s_power, 0);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 0);
if (ret < 0) {
dev_err(icd->pdev,
"Platform failed to power-off the camera.\n");
return ret;
}
-
- ret = regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
- if (ret < 0) {
- dev_err(icd->pdev, "Cannot disable regulators\n");
- return ret;
- }
}
- return 0;
+ ret = regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0)
+ dev_err(icd->pdev, "Cannot disable regulators\n");
+
+ return ret;
}
const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
@@ -108,38 +124,38 @@ const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
/**
- * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
* @icl: camera platform parameters
- * @flags: flags to be inverted according to platform configuration
+ * @cfg: media bus configuration
* @return: resulting flags
*/
-unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
- unsigned long flags)
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+ const struct v4l2_mbus_config *cfg)
{
- unsigned long f;
+ unsigned long f, flags = cfg->flags;
/* If only one of the two polarities is supported, switch to the opposite */
if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
- f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
- if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW)
- flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW;
+ f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
- f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
- if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW)
- flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW;
+ f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
- f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
- if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING)
- flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING;
+ f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
}
return flags;
}
-EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
+EXPORT_SYMBOL(soc_camera_apply_board_flags);
#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
((x) >> 24) & 0xff
@@ -233,6 +249,14 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
return v4l2_subdev_call(sd, core, s_std, *a);
}
+static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_std, a);
+}
+
static int soc_camera_enum_fsizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
@@ -318,6 +342,32 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK);
}
+static int soc_camera_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_create_bufs(&icd->vb2_vidq, create);
+}
+
+static int soc_camera_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_prepare_buf(&icd->vb2_vidq, b);
+}
+
/* Always entered with .video_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
@@ -448,7 +498,7 @@ static int soc_camera_open(struct file *file)
struct soc_camera_host *ici;
int ret;
- if (!icd->ops)
+ if (!to_soc_camera_control(icd))
/* No device driver attached */
return -ENODEV;
@@ -476,7 +526,7 @@ static int soc_camera_open(struct file *file)
},
};
- ret = soc_camera_power_set(icd, icl, 1);
+ ret = soc_camera_power_on(icd, icl);
if (ret < 0)
goto epower;
@@ -512,6 +562,7 @@ static int soc_camera_open(struct file *file)
if (ret < 0)
goto einitvb;
}
+ v4l2_ctrl_handler_setup(&icd->ctrl_handler);
}
file->private_data = icd;
@@ -529,7 +580,7 @@ esfmt:
eresume:
ici->ops->remove(icd);
eiciadd:
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
epower:
icd->use_count--;
module_put(ici->ops->owner);
@@ -553,7 +604,7 @@ static int soc_camera_close(struct file *file)
if (ici->ops->init_videobuf2)
vb2_queue_release(&icd->vb2_vidq);
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
}
if (icd->streamer == file)
@@ -781,75 +832,6 @@ static int soc_camera_streamoff(struct file *file, void *priv,
return 0;
}
-static int soc_camera_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- int i;
-
- WARN_ON(priv != file->private_data);
-
- if (!qc->id)
- return -EINVAL;
-
- /* First check host controls */
- for (i = 0; i < ici->ops->num_controls; i++)
- if (qc->id == ici->ops->controls[i].id) {
- memcpy(qc, &(ici->ops->controls[i]),
- sizeof(*qc));
- return 0;
- }
-
- /* Then device controls */
- for (i = 0; i < icd->ops->num_controls; i++)
- if (qc->id == icd->ops->controls[i].id) {
- memcpy(qc, &(icd->ops->controls[i]),
- sizeof(*qc));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int soc_camera_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (ici->ops->get_ctrl) {
- ret = ici->ops->get_ctrl(icd, ctrl);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return v4l2_subdev_call(sd, core, g_ctrl, ctrl);
-}
-
-static int soc_camera_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (ici->ops->set_ctrl) {
- ret = ici->ops->set_ctrl(icd, ctrl);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return v4l2_subdev_call(sd, core, s_ctrl, ctrl);
-}
-
static int soc_camera_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *a)
{
@@ -1003,7 +985,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
goto ei2cga;
}
- icl->board_info->platform_data = icd;
+ icl->board_info->platform_data = icl;
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
icl->board_info, NULL);
@@ -1052,12 +1034,29 @@ static int soc_camera_probe(struct soc_camera_device *icd)
dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
+ /*
+ * Currently the subdev with the largest number of controls (13) is
+ * ov6550. So let's pick 16 as a hint for the control handler. Note
+ * that this is a hint only: too large and you waste some memory, too
+ * small and there is a (very) small performance hit when looking up
+ * controls in the internal hash.
+ */
+ ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
+
ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
icl->regulators);
if (ret < 0)
goto ereg;
- ret = soc_camera_power_set(icd, icl, 1);
+ /*
+ * This will not yet call v4l2_subdev_core_ops::s_power(1), because the
+ * subdevice has not been initialised yet. We'll have to call it once
+ * again after initialisation, even though it shouldn't be needed, we
+ * don't do any IO here.
+ */
+ ret = soc_camera_power_on(icd, icl);
if (ret < 0)
goto epower;
@@ -1098,6 +1097,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
if (!control || !control->driver || !dev_get_drvdata(control) ||
!try_module_get(control->driver->owner)) {
icl->del_device(icd);
+ ret = -ENODEV;
goto enodrv;
}
}
@@ -1105,6 +1105,9 @@ static int soc_camera_probe(struct soc_camera_device *icd)
sd = soc_camera_to_subdev(icd);
sd->grp_id = (long)icd;
+ if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler))
+ goto ectrl;
+
/* At this point client .probe() should have run already */
ret = soc_camera_init_user_formats(icd);
if (ret < 0)
@@ -1123,6 +1126,10 @@ static int soc_camera_probe(struct soc_camera_device *icd)
if (ret < 0)
goto evidstart;
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto esdpwr;
+
/* Try to improve our guess of a reasonable window format */
if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
icd->user_width = mf.width;
@@ -1133,16 +1140,19 @@ static int soc_camera_probe(struct soc_camera_device *icd)
ici->ops->remove(icd);
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
mutex_unlock(&icd->video_lock);
return 0;
+esdpwr:
+ video_unregister_device(icd->vdev);
evidstart:
mutex_unlock(&icd->video_lock);
soc_camera_free_user_formats(icd);
eiufmt:
+ectrl:
if (icl->board_info) {
soc_camera_free_i2c(icd);
} else {
@@ -1152,13 +1162,15 @@ eiufmt:
enodrv:
eadddev:
video_device_release(icd->vdev);
+ icd->vdev = NULL;
evdc:
ici->ops->remove(icd);
eadd:
- soc_camera_power_set(icd, icl, 0);
+ soc_camera_power_off(icd, icl);
epower:
regulator_bulk_free(icl->num_regulators, icl->regulators);
ereg:
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
return ret;
}
@@ -1173,6 +1185,7 @@ static int soc_camera_remove(struct soc_camera_device *icd)
BUG_ON(!icd->parent);
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
if (vdev) {
video_unregister_device(vdev);
icd->vdev = NULL;
@@ -1363,24 +1376,24 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_querycap = soc_camera_querycap,
+ .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
.vidioc_enum_input = soc_camera_enum_input,
.vidioc_g_input = soc_camera_g_input,
.vidioc_s_input = soc_camera_s_input,
.vidioc_s_std = soc_camera_s_std,
+ .vidioc_g_std = soc_camera_g_std,
.vidioc_enum_framesizes = soc_camera_enum_fsizes,
.vidioc_reqbufs = soc_camera_reqbufs,
- .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
.vidioc_querybuf = soc_camera_querybuf,
.vidioc_qbuf = soc_camera_qbuf,
.vidioc_dqbuf = soc_camera_dqbuf,
+ .vidioc_create_bufs = soc_camera_create_bufs,
+ .vidioc_prepare_buf = soc_camera_prepare_buf,
.vidioc_streamon = soc_camera_streamon,
.vidioc_streamoff = soc_camera_streamoff,
- .vidioc_queryctrl = soc_camera_queryctrl,
- .vidioc_g_ctrl = soc_camera_g_ctrl,
- .vidioc_s_ctrl = soc_camera_s_ctrl,
.vidioc_cropcap = soc_camera_cropcap,
.vidioc_g_crop = soc_camera_g_crop,
.vidioc_s_crop = soc_camera_s_crop,
@@ -1409,6 +1422,7 @@ static int video_dev_create(struct soc_camera_device *icd)
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
vdev->tvnorms = V4L2_STD_UNKNOWN;
+ vdev->ctrl_handler = &icd->ctrl_handler;
vdev->lock = &icd->video_lock;
icd->vdev = vdev;
@@ -1427,11 +1441,6 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
if (!icd->parent)
return -ENODEV;
- if (!icd->ops ||
- !icd->ops->query_bus_param ||
- !icd->ops->set_bus_param)
- return -EINVAL;
-
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index 8069cd6bc5e8..4402a8a74f7a 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -30,32 +30,12 @@ static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
return container_of(subdev, struct soc_camera_platform_priv, subdev);
}
-static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd)
-{
- struct platform_device *pdev =
- to_platform_device(to_soc_camera_control(icd));
- return pdev->dev.platform_data;
-}
-
static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
return p->set_capture(p, enable);
}
-static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
-{
- return 0;
-}
-
-static unsigned long
-soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_platform_info *p = get_info(icd);
- return p->bus_param;
-}
-
static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
@@ -115,6 +95,17 @@ static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
return 0;
}
+static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ cfg->flags = p->mbus_param;
+ cfg->type = p->mbus_type;
+
+ return 0;
+}
+
static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.s_stream = soc_camera_platform_s_stream,
.enum_mbus_fmt = soc_camera_platform_enum_fmt,
@@ -123,6 +114,7 @@ static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.try_mbus_fmt = soc_camera_platform_fill_fmt,
.g_mbus_fmt = soc_camera_platform_fill_fmt,
.s_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_config = soc_camera_platform_g_mbus_config,
};
static struct v4l2_subdev_ops platform_subdev_ops = {
@@ -130,11 +122,6 @@ static struct v4l2_subdev_ops platform_subdev_ops = {
.video = &platform_subdev_video_ops,
};
-static struct soc_camera_ops soc_camera_platform_ops = {
- .set_bus_param = soc_camera_platform_set_bus_param,
- .query_bus_param = soc_camera_platform_query_bus_param,
-};
-
static int soc_camera_platform_probe(struct platform_device *pdev)
{
struct soc_camera_host *ici;
@@ -163,8 +150,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
/* Set the control device reference */
icd->control = &pdev->dev;
- icd->ops = &soc_camera_platform_ops;
-
ici = to_soc_camera_host(icd->parent);
v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
@@ -178,7 +163,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
return ret;
evdrs:
- icd->ops = NULL;
platform_set_drvdata(pdev, NULL);
kfree(priv);
return ret;
@@ -187,11 +171,10 @@ evdrs:
static int soc_camera_platform_remove(struct platform_device *pdev)
{
struct soc_camera_platform_priv *priv = get_priv(pdev);
- struct soc_camera_platform_info *p = pdev->dev.platform_data;
- struct soc_camera_device *icd = p->icd;
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
+ p->icd->control = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
- icd->ops = NULL;
platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index bea7c9cf4f88..cf7f2194ded4 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -383,6 +383,39 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
}
EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
+unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
+ unsigned int flags)
+{
+ unsigned long common_flags;
+ bool hsync = true, vsync = true, pclk, data, mode;
+ bool mipi_lanes, mipi_clock;
+
+ common_flags = cfg->flags & flags;
+
+ switch (cfg->type) {
+ case V4L2_MBUS_PARALLEL:
+ hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ case V4L2_MBUS_BT656:
+ pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW);
+ mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
+ return (!hsync || !vsync || !pclk || !data || !mode) ?
+ 0 : common_flags;
+ case V4L2_MBUS_CSI2:
+ mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
+ mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
+ return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(soc_mbus_config_compatible);
+
static int __init soc_mbus_init(void)
{
return 0;
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
index 8afb0e8a2e00..10aff3f943a8 100644
--- a/drivers/media/video/sr030pc30.c
+++ b/drivers/media/video/sr030pc30.c
@@ -714,11 +714,6 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd)
return ret;
}
-static int sr030pc30_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -764,7 +759,6 @@ static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
};
static const struct v4l2_subdev_video_ops sr030pc30_video_ops = {
- .s_stream = sr030pc30_s_stream,
.g_mbus_fmt = sr030pc30_g_fmt,
.s_mbus_fmt = sr030pc30_s_fmt,
.try_mbus_fmt = sr030pc30_try_fmt,
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index d1a2cefbf55b..cbc105f975de 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -55,6 +55,8 @@ MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN");
MODULE_DESCRIPTION("Syntek DC1125 webcam driver");
+/* bool for webcam LED management */
+int first_init = 1;
/* Some cameras have audio interfaces, we aren't interested in those */
static struct usb_device_id stkwebcam_table[] = {
@@ -518,7 +520,7 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
return -ENOMEM;
for (i = 0; i < n_sbufs; i++) {
if (stk_setup_siobuf(dev, i))
- return (dev->n_sbufs > 1)? 0 : -ENOMEM;
+ return (dev->n_sbufs > 1 ? 0 : -ENOMEM);
dev->n_sbufs = i+1;
}
}
@@ -558,9 +560,14 @@ static int v4l_stk_open(struct file *fp)
vdev = video_devdata(fp);
dev = vdev_to_camera(vdev);
- if (dev == NULL || !is_present(dev)) {
+ if (dev == NULL || !is_present(dev))
return -ENXIO;
- }
+
+ if (!first_init)
+ stk_camera_write_reg(dev, 0x0, 0x24);
+ else
+ first_init = 0;
+
fp->private_data = dev;
usb_autopm_get_interface(dev->interface);
@@ -574,10 +581,12 @@ static int v4l_stk_release(struct file *fp)
if (dev->owner == fp) {
stk_stop_stream(dev);
stk_free_buffers(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */
+ unset_initialised(dev);
dev->owner = NULL;
}
- if(is_present(dev))
+ if (is_present(dev))
usb_autopm_put_interface(dev->interface);
return 0;
@@ -654,7 +663,7 @@ static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
return POLLERR;
if (!list_empty(&dev->sio_full))
- return (POLLIN | POLLRDNORM);
+ return POLLIN | POLLRDNORM;
return 0;
}
@@ -891,9 +900,9 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
struct stk_camera *dev = priv;
int i;
- for (i = 0; i < ARRAY_SIZE(stk_sizes)
- && stk_sizes[i].m != dev->vsettings.mode;
- i++);
+ for (i = 0; i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode; i++)
+ ;
if (i == ARRAY_SIZE(stk_sizes)) {
STK_ERROR("ERROR: mode invalid\n");
return -EINVAL;
@@ -1305,9 +1314,8 @@ static int stk_camera_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
err = stk_register_video_device(dev);
- if (err) {
+ if (err)
goto error;
- }
return 0;
@@ -1350,6 +1358,7 @@ static int stk_camera_resume(struct usb_interface *intf)
return 0;
unset_initialised(dev);
stk_initialise(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49);
stk_setup_format(dev);
if (is_streaming(dev))
stk_start_stream(dev);
diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile
index 81bb7fdd1e3d..ea09b9af2d30 100644
--- a/drivers/media/video/tlg2300/Makefile
+++ b/drivers/media/video/tlg2300/Makefile
@@ -2,8 +2,8 @@ poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
-EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/staging/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig
index 114eec8a630a..114eec8a630a 100644
--- a/drivers/staging/tm6000/Kconfig
+++ b/drivers/media/video/tm6000/Kconfig
diff --git a/drivers/staging/tm6000/Makefile b/drivers/media/video/tm6000/Makefile
index 395515b4a888..395515b4a888 100644
--- a/drivers/staging/tm6000/Makefile
+++ b/drivers/media/video/tm6000/Makefile
diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c
index bd5fa89af07c..7d675c72fd47 100644
--- a/drivers/staging/tm6000/tm6000-alsa.c
+++ b/drivers/media/video/tm6000/tm6000-alsa.c
@@ -80,7 +80,7 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
dprintk(1, "Starting audio DMA\n");
/* Enables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40);
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
tm6000_set_audio_bitrate(core, 48000);
@@ -97,7 +97,7 @@ static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
dprintk(1, "Stopping audio DMA\n");
/* Disables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40);
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
return 0;
}
@@ -304,6 +304,7 @@ static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
schedule_work(&core->wq_trigger);
}
+ dsp_buffer_free(substream);
return 0;
}
@@ -397,7 +398,7 @@ static struct snd_pcm_ops snd_tm6000_pcm_ops = {
/*
* Alsa Constructor - Component probe
*/
-int tm6000_audio_init(struct tm6000_core *dev)
+static int tm6000_audio_init(struct tm6000_core *dev)
{
struct snd_card *card;
struct snd_tm6000_card *chip;
@@ -490,7 +491,7 @@ static int tm6000_audio_fini(struct tm6000_core *dev)
return 0;
}
-struct tm6000_ops audio_ops = {
+static struct tm6000_ops audio_ops = {
.type = TM6000_AUDIO,
.name = "TM6000 Audio Extension",
.init = tm6000_audio_init,
diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c
index 9227db5d8955..ec2578a0fdf7 100644
--- a/drivers/staging/tm6000/tm6000-cards.c
+++ b/drivers/media/video/tm6000/tm6000-cards.c
@@ -87,7 +87,7 @@ struct tm6000_board {
char *ir_codes;
};
-struct tm6000_board tm6000_boards[] = {
+static struct tm6000_board tm6000_boards[] = {
[TM6000_BOARD_UNKNOWN] = {
.name = "Unknown tm6000 video grabber",
.caps = {
@@ -394,7 +394,7 @@ struct tm6000_board tm6000_boards[] = {
.has_zl10353 = 1,
.has_eeprom = 1,
.has_remote = 1,
- .has_radio = 1.
+ .has_radio = 1,
},
.gpio = {
.tuner_reset = TM6010_GPIO_0,
@@ -468,6 +468,7 @@ struct tm6000_board tm6000_boards[] = {
.has_zl10353 = 1,
.has_eeprom = 1,
.has_remote = 1,
+ .has_radio = 1,
},
.gpio = {
.tuner_reset = TM6010_GPIO_2,
@@ -493,6 +494,10 @@ struct tm6000_board tm6000_boards[] = {
.amux = TM6000_AMUX_ADC2,
},
},
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_SIF1,
+ },
},
[TM5600_BOARD_TERRATEC_GRABSTER] = {
.name = "Terratec Grabster AV 150/250 MX",
@@ -611,7 +616,7 @@ struct tm6000_board tm6000_boards[] = {
};
/* table of devices that work with this driver */
-struct usb_device_id tm6000_id_table[] = {
+static struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
{ USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
{ USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
@@ -632,7 +637,7 @@ struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
{ USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
{ USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
- { },
+ { }
};
/* Control power led for show some activity */
@@ -780,6 +785,11 @@ int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
rc = tm6000_i2c_reset(dev, 100);
break;
}
+ break;
+ case XC2028_I2C_FLUSH:
+ tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+ tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
+ break;
}
return rc;
}
@@ -787,8 +797,6 @@ EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
int tm6000_cards_setup(struct tm6000_core *dev)
{
- int i, rc;
-
/*
* Board-specific initialization sequence. Handles all GPIO
* initialization sequences that are board-specific.
@@ -860,6 +868,9 @@ int tm6000_cards_setup(struct tm6000_core *dev)
*/
if (dev->gpio.tuner_reset) {
+ int rc;
+ int i;
+
for (i = 0; i < 2; i++) {
rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.tuner_reset, 0x00);
@@ -988,6 +999,16 @@ static int fill_board_specific_data(struct tm6000_core *dev)
dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
dev->rinput = tm6000_boards[dev->model].rinput;
+ /* setup per-model quirks */
+ switch (dev->model) {
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
+ break;
+
+ default:
+ break;
+ }
+
/* initialize hardware */
rc = tm6000_init(dev);
if (rc < 0)
@@ -1162,13 +1183,14 @@ static int tm6000_usb_probe(struct usb_interface *interface,
return -ENOMEM;
}
spin_lock_init(&dev->slock);
+ mutex_init(&dev->usb_lock);
/* Increment usage count */
- tm6000_devused |= 1<<nr;
+ set_bit(nr, &tm6000_devused);
snprintf(dev->name, 29, "tm6000 #%d", nr);
dev->model = id->driver_info;
- if ((card[nr] >= 0) && (card[nr] < ARRAY_SIZE(tm6000_boards)))
+ if (card[nr] < ARRAY_SIZE(tm6000_boards))
dev->model = card[nr];
dev->udev = usbdev;
@@ -1189,8 +1211,6 @@ static int tm6000_usb_probe(struct usb_interface *interface,
speed = "unknown";
}
-
-
/* Get endpoints */
for (i = 0; i < interface->num_altsetting; i++) {
int ep;
@@ -1274,7 +1294,6 @@ static int tm6000_usb_probe(struct usb_interface *interface,
printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
rc = tm6000_init_dev(dev);
-
if (rc < 0)
goto err;
@@ -1283,7 +1302,7 @@ static int tm6000_usb_probe(struct usb_interface *interface,
err:
printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
- tm6000_devused &= ~(1<<nr);
+ clear_bit(nr, &tm6000_devused);
usb_put_dev(usbdev);
kfree(dev);
@@ -1341,6 +1360,7 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
tm6000_close_extension(dev);
tm6000_remove_from_devlist(dev);
+ clear_bit(dev->devno, &tm6000_devused);
kfree(dev);
}
diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c
index d7eb2e23cdbd..9783616a0da2 100644
--- a/drivers/staging/tm6000/tm6000-core.c
+++ b/drivers/media/video/tm6000/tm6000-core.c
@@ -39,10 +39,11 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
unsigned int pipe;
u8 *data = NULL;
+ mutex_lock(&dev->usb_lock);
+
if (len)
data = kzalloc(len, GFP_KERNEL);
-
if (req_type & USB_DIR_IN)
pipe = usb_rcvctrlpipe(dev->udev, 0);
else {
@@ -51,18 +52,18 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
}
if (tm6000_debug & V4L2_DEBUG_I2C) {
- printk("(dev %p, pipe %08x): ", dev->udev, pipe);
+ printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe);
- printk("%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
(req_type & USB_DIR_IN) ? " IN" : "OUT",
req_type, req, value&0xff, value>>8, index&0xff,
index>>8, len&0xff, len>>8);
if (!(req_type & USB_DIR_IN)) {
- printk(">>> ");
+ printk(KERN_CONT ">>> ");
for (i = 0; i < len; i++)
- printk(" %02x", buf[i]);
- printk("\n");
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
}
}
@@ -75,21 +76,21 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
if (tm6000_debug & V4L2_DEBUG_I2C) {
if (ret < 0) {
if (req_type & USB_DIR_IN)
- printk("<<< (len=%d)\n", len);
+ printk(KERN_DEBUG "<<< (len=%d)\n", len);
- printk("%s: Error #%d\n", __FUNCTION__, ret);
+ printk(KERN_CONT "%s: Error #%d\n", __func__, ret);
} else if (req_type & USB_DIR_IN) {
- printk("<<< ");
+ printk(KERN_CONT "<<< ");
for (i = 0; i < len; i++)
- printk(" %02x", buf[i]);
- printk("\n");
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
}
}
kfree(data);
-
msleep(5);
+ mutex_unlock(&dev->usb_lock);
return ret;
}
@@ -188,11 +189,11 @@ void tm6000_set_fourcc_format(struct tm6000_core *dev)
if (dev->dev_type == TM6010) {
int val;
- val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0) & 0xfc;
+ val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc;
if (dev->fourcc == V4L2_PIX_FMT_UYVY)
- tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val);
else
- tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val | 1);
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1);
} else {
if (dev->fourcc == V4L2_PIX_FMT_UYVY)
tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
@@ -268,9 +269,14 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
struct v4l2_frequency f;
if (dev->dev_type == TM6010) {
+ u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE;
+
+ if (!dev->radio)
+ active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE;
+
/* Enable video and audio */
- tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
- 0x60, 0x60);
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
+ active, 0x60);
/* Disable TS input */
tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
0x00, 0x40);
@@ -308,7 +314,7 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
* FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
* for more than a few seconds. Not sure why, as this behavior does
* not happen on other devices with xc3028. So, I suspect that it
- * is yet another bug at tm6000. After start sleeping, decoding
+ * is yet another bug at tm6000. After start sleeping, decoding
* doesn't start automatically. Instead, it requires some
* I2C commands to wake it up. As we want to have image at the
* beginning, we needed to add this hack. The better would be to
@@ -335,7 +341,7 @@ int tm6000_init_digital_mode(struct tm6000_core *dev)
{
if (dev->dev_type == TM6010) {
/* Disable video and audio */
- tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
0x00, 0x60);
/* Enable TS input */
tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
@@ -390,7 +396,7 @@ struct reg_init {
};
/* The meaning of those initializations are unknown */
-struct reg_init tm6000_init_tab[] = {
+static struct reg_init tm6000_init_tab[] = {
/* REG VALUE */
{ TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f },
{ TM6010_REQ07_RFF_SOFT_RESET, 0x08 },
@@ -458,12 +464,12 @@ struct reg_init tm6000_init_tab[] = {
{ TM6010_REQ05_R18_IMASK7, 0x00 },
};
-struct reg_init tm6010_init_tab[] = {
+static struct reg_init tm6010_init_tab[] = {
{ TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 },
{ TM6010_REQ07_RC4_HSTART0, 0xa0 },
{ TM6010_REQ07_RC6_HEND0, 0x40 },
{ TM6010_REQ07_RCA_VEND0, 0x31 },
- { TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0xe1 },
+ { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 },
{ TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 },
{ TM6010_REQ07_RFE_POWER_DOWN, 0x7f },
@@ -593,6 +599,56 @@ int tm6000_init(struct tm6000_core *dev)
return rc;
}
+int tm6000_reset(struct tm6000_core *dev)
+{
+ int pipe;
+ int err;
+
+ msleep(500);
+
+ err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 0);
+ if (err < 0) {
+ tm6000_err("failed to select interface %d, alt. setting 0\n",
+ dev->isoc_in.bInterfaceNumber);
+ return err;
+ }
+
+ err = usb_reset_configuration(dev->udev);
+ if (err < 0) {
+ tm6000_err("failed to reset configuration\n");
+ return err;
+ }
+
+ if ((dev->quirks & TM6000_QUIRK_NO_USB_DELAY) == 0)
+ msleep(5);
+
+ /*
+ * Not all devices have int_in defined
+ */
+ if (!dev->int_in.endp)
+ return 0;
+
+ err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 2);
+ if (err < 0) {
+ tm6000_err("failed to select interface %d, alt. setting 2\n",
+ dev->isoc_in.bInterfaceNumber);
+ return err;
+ }
+
+ msleep(5);
+
+ pipe = usb_rcvintpipe(dev->udev,
+ dev->int_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ err = usb_clear_halt(dev->udev, pipe);
+ if (err < 0) {
+ tm6000_err("usb_clear_halt failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
{
int val = 0;
@@ -687,7 +743,7 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
return 0;
}
-void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
+static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;
@@ -697,7 +753,7 @@ void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
}
-void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
+static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;
@@ -749,7 +805,7 @@ int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
return 0;
}
-void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
+static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
{
u8 vol_reg;
@@ -762,7 +818,7 @@ void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
}
-void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
+static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
{
u8 vol_reg;
diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c
index 0e0dfce05821..5e6c129a4beb 100644
--- a/drivers/staging/tm6000/tm6000-dvb.c
+++ b/drivers/media/video/tm6000/tm6000-dvb.c
@@ -105,7 +105,7 @@ static void tm6000_urb_received(struct urb *urb)
}
}
-int tm6000_start_stream(struct tm6000_core *dev)
+static int tm6000_start_stream(struct tm6000_core *dev)
{
int ret;
unsigned int pipe, size;
@@ -166,7 +166,7 @@ int tm6000_start_stream(struct tm6000_core *dev)
return 0;
}
-void tm6000_stop_stream(struct tm6000_core *dev)
+static void tm6000_stop_stream(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
@@ -180,7 +180,7 @@ void tm6000_stop_stream(struct tm6000_core *dev)
}
}
-int tm6000_start_feed(struct dvb_demux_feed *feed)
+static int tm6000_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct tm6000_core *dev = demux->priv;
@@ -199,7 +199,7 @@ int tm6000_start_feed(struct dvb_demux_feed *feed)
return 0;
}
-int tm6000_stop_feed(struct dvb_demux_feed *feed)
+static int tm6000_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct tm6000_core *dev = demux->priv;
@@ -222,7 +222,7 @@ int tm6000_stop_feed(struct dvb_demux_feed *feed)
return 0;
}
-int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
+static int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
@@ -247,7 +247,7 @@ int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int register_dvb(struct tm6000_core *dev)
+static int register_dvb(struct tm6000_core *dev)
{
int ret = -1;
struct tm6000_dvb *dvb = dev->dvb;
@@ -330,7 +330,7 @@ int register_dvb(struct tm6000_core *dev)
dvb->demux.write_to_decoder = NULL;
ret = dvb_dmx_init(&dvb->demux);
if (ret < 0) {
- printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
+ printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
goto frontend_err;
}
@@ -340,7 +340,7 @@ int register_dvb(struct tm6000_core *dev)
ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (ret < 0) {
- printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
+ printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
goto dvb_dmx_err;
}
@@ -359,7 +359,7 @@ err:
return ret;
}
-void unregister_dvb(struct tm6000_core *dev)
+static void unregister_dvb(struct tm6000_core *dev)
{
struct tm6000_dvb *dvb = dev->dvb;
diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c
index 5a651ea5f60a..0290bbf00c3e 100644
--- a/drivers/staging/tm6000/tm6000-i2c.c
+++ b/drivers/media/video/tm6000/tm6000-i2c.c
@@ -189,7 +189,7 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
/* 1 or 2 byte write followed by a read */
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
i2c_dprintk(2, "; joined to read %s len=%d:",
i == num - 2 ? "stop" : "nonstop",
msgs[i + 1].len);
@@ -211,22 +211,17 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
}
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
} else {
/* write bytes */
if (i2c_debug >= 2)
for (byte = 0; byte < msgs[i].len; byte++)
- printk(" %02x", msgs[i].buf[byte]);
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0],
msgs[i].buf + 1, msgs[i].len - 1);
-
- if (addr == dev->tuner_addr << 1) {
- tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
- tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
- }
}
if (i2c_debug >= 2)
- printk("\n");
+ printk(KERN_CONT "\n");
if (rc < 0)
goto err;
}
@@ -264,7 +259,7 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev)
p++;
if (0 == (i % 16))
printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
- printk(" %02x", dev->eedata[i]);
+ printk(KERN_CONT " %02x", dev->eedata[i]);
if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z'))
bytes[i%16] = dev->eedata[i];
else
@@ -274,14 +269,14 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev)
if (0 == (i % 16)) {
bytes[16] = '\0';
- printk(" %s\n", bytes);
+ printk(KERN_CONT " %s\n", bytes);
}
}
if (0 != (i%16)) {
bytes[i%16] = '\0';
for (i %= 16; i < 16; i++)
- printk(" ");
- printk(" %s\n", bytes);
+ printk(KERN_CONT " ");
+ printk(KERN_CONT " %s\n", bytes);
}
return 0;
diff --git a/drivers/staging/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
index 70a2c5f557c1..405d12729d05 100644
--- a/drivers/staging/tm6000/tm6000-input.c
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -284,7 +284,7 @@ static void tm6000_ir_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
}
-int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
{
struct tm6000_IR *ir = rc->priv;
diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h
index 5375a8347374..7f491b6de933 100644
--- a/drivers/staging/tm6000/tm6000-regs.h
+++ b/drivers/media/video/tm6000/tm6000-regs.h
@@ -90,7 +90,7 @@
*/
enum {
- TM6000_URB_MSG_VIDEO=1,
+ TM6000_URB_MSG_VIDEO = 1,
TM6000_URB_MSG_AUDIO,
TM6000_URB_MSG_VBI,
TM6000_URB_MSG_PTS,
@@ -270,7 +270,9 @@ enum {
#define TM6010_REQ07_RCA_VEND0 0x07, 0xca
#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb
/* ONLY for TM6010 */
-#define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc
+#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc
+#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5)
+#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6)
#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0
#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1
#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c
new file mode 100644
index 000000000000..9a4145dc3d87
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-stds.c
@@ -0,0 +1,659 @@
+/*
+ * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@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 version 2
+ *
+ * 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/kernel.h>
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+static unsigned int tm6010_a_mode;
+module_param(tm6010_a_mode, int, 0644);
+MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode");
+
+struct tm6000_reg_settings {
+ unsigned char req;
+ unsigned char reg;
+ unsigned char value;
+};
+
+
+struct tm6000_std_settings {
+ v4l2_std_id id;
+ struct tm6000_reg_settings *common;
+};
+
+static struct tm6000_reg_settings composite_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings composite_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = composite_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = composite_pal, },
+ { .id = V4L2_STD_SECAM, .common = composite_secam, },
+ { .id = V4L2_STD_NTSC, .common = composite_ntsc, },
+};
+
+static struct tm6000_reg_settings svideo_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings svideo_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = svideo_pal, },
+ { .id = V4L2_STD_SECAM, .common = svideo_secam, },
+ { .id = V4L2_STD_NTSC, .common = svideo_ntsc, },
+};
+
+static int tm6000_set_audio_std(struct tm6000_core *dev)
+{
+ uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
+ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
+ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
+ uint8_t nicam_flag = 0; /* No NICAM */
+
+ if (dev->radio) {
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+ /* set mono or stereo */
+ if (dev->amode == V4L2_TUNER_MODE_MONO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+ else if (dev->amode == V4L2_TUNER_MODE_STEREO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff);
+ return 0;
+ }
+
+ switch (tm6010_a_mode) {
+ /* auto */
+ case 0:
+ switch (dev->norm) {
+ case V4L2_STD_NTSC_M_KR:
+ areg_05 |= 0x00;
+ break;
+ case V4L2_STD_NTSC_M_JP:
+ areg_05 |= 0x40;
+ break;
+ case V4L2_STD_NTSC_M:
+ case V4L2_STD_PAL_M:
+ case V4L2_STD_PAL_N:
+ areg_05 |= 0x20;
+ break;
+ case V4L2_STD_PAL_Nc:
+ areg_05 |= 0x60;
+ break;
+ case V4L2_STD_SECAM_L:
+ areg_05 |= 0x00;
+ break;
+ case V4L2_STD_DK:
+ areg_05 |= 0x10;
+ break;
+ }
+ break;
+ /* A2 */
+ case 1:
+ switch (dev->norm) {
+ case V4L2_STD_B:
+ case V4L2_STD_GH:
+ areg_05 = 0x05;
+ break;
+ case V4L2_STD_DK:
+ areg_05 = 0x09;
+ break;
+ }
+ break;
+ /* NICAM */
+ case 2:
+ switch (dev->norm) {
+ case V4L2_STD_B:
+ case V4L2_STD_GH:
+ areg_05 = 0x07;
+ break;
+ case V4L2_STD_DK:
+ areg_05 = 0x06;
+ break;
+ case V4L2_STD_PAL_I:
+ areg_05 = 0x08;
+ break;
+ case V4L2_STD_SECAM_L:
+ areg_05 = 0x0a;
+ areg_02 = 0x02;
+ break;
+ }
+ nicam_flag = 1;
+ break;
+ /* other */
+ case 3:
+ switch (dev->norm) {
+ /* DK3_A2 */
+ case V4L2_STD_DK:
+ areg_05 = 0x0b;
+ break;
+ /* Korea */
+ case V4L2_STD_NTSC_M_KR:
+ areg_05 = 0x04;
+ break;
+ /* EIAJ */
+ case V4L2_STD_NTSC_M_JP:
+ areg_05 = 0x03;
+ break;
+ default:
+ areg_05 = 0x02;
+ break;
+ }
+ break;
+ }
+
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05);
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06);
+ tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
+ tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
+ tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
+ tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
+ tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
+ tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
+ tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
+ tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+
+ return 0;
+}
+
+void tm6000_get_std_res(struct tm6000_core *dev)
+{
+ /* Currently, those are the only supported resoltions */
+ if (dev->norm & V4L2_STD_525_60)
+ dev->height = 480;
+ else
+ dev->height = 576;
+
+ dev->width = 720;
+}
+
+static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set)
+{
+ int i, rc;
+
+ /* Load board's initialization table */
+ for (i = 0; set[i].req; i++) {
+ rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i while setting "
+ "req %d, reg %d to value %d\n",
+ rc, set[i].req, set[i].reg, set[i].value);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int tm6000_set_standard(struct tm6000_core *dev)
+{
+ struct tm6000_input *input;
+ int i, rc = 0;
+ u8 reg_07_fe = 0x8a;
+ u8 reg_08_f1 = 0xfc;
+ u8 reg_08_e2 = 0xf0;
+ u8 reg_08_e6 = 0x0f;
+
+ tm6000_get_std_res(dev);
+
+ if (!dev->radio)
+ input = &dev->vinput[dev->input];
+ else
+ input = &dev->rinput;
+
+ if (dev->dev_type == TM6010) {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc);
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8);
+ reg_08_e6 = 0x00;
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x00, 0x0f);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x08, 0x0f);
+ break;
+ case TM6000_AMUX_SIF1:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ break;
+ case TM6000_AMUX_SIF2:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ break;
+ default:
+ break;
+ }
+ tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2);
+ tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe);
+ } else {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f);
+ break;
+ default:
+ break;
+ }
+ }
+ if (input->type == TM6000_INPUT_SVIDEO) {
+ for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) {
+ if (dev->norm & svideo_stds[i].id) {
+ rc = tm6000_load_std(dev, svideo_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(composite_stds); i++) {
+ if (dev->norm & composite_stds[i].id) {
+ rc = tm6000_load_std(dev, composite_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ }
+
+ret:
+ if (rc < 0)
+ return rc;
+
+ if ((dev->dev_type == TM6010) &&
+ ((input->amux == TM6000_AMUX_SIF1) ||
+ (input->amux == TM6000_AMUX_SIF2)))
+ tm6000_set_audio_std(dev);
+
+ msleep(40);
+
+ return 0;
+}
diff --git a/drivers/staging/tm6000/tm6000-usb-isoc.h b/drivers/media/video/tm6000/tm6000-usb-isoc.h
index 084c2a8904a3..99d15a55aa03 100644
--- a/drivers/staging/tm6000/tm6000-usb-isoc.h
+++ b/drivers/media/video/tm6000/tm6000-usb-isoc.h
@@ -46,5 +46,5 @@ struct usb_isoc_ctl {
int tmp_buf_len;
/* Stores already requested buffers */
- struct tm6000_buffer *buf;
+ struct tm6000_buffer *buf;
};
diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
index 8d8b939915d8..1e5ace0b5d10 100644
--- a/drivers/staging/tm6000/tm6000-video.c
+++ b/drivers/media/video/tm6000/tm6000-video.c
@@ -19,6 +19,7 @@
* 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/delay.h>
#include <linux/errno.h>
@@ -202,17 +203,6 @@ static inline void buffer_filled(struct tm6000_core *dev,
wake_up(&buf->vb.done);
}
-const char *tm6000_msg_type[] = {
- "unknown(0)", /* 0 */
- "video", /* 1 */
- "audio", /* 2 */
- "vbi", /* 3 */
- "pts", /* 4 */
- "err", /* 5 */
- "unknown(6)", /* 6 */
- "unknown(7)", /* 7 */
-};
-
/*
* Identify the tm5600/6000 buffer header type and properly handles
*/
@@ -286,17 +276,18 @@ static int copy_streams(u8 *data, unsigned long len,
if (size > TM6000_URB_MSG_LEN)
size = TM6000_URB_MSG_LEN;
pktsize = TM6000_URB_MSG_LEN;
- /* calculate position in buffer
- * and change the buffer
+ /*
+ * calculate position in buffer and change the buffer
*/
switch (cmd) {
case TM6000_URB_MSG_VIDEO:
if (!dev->radio) {
if ((dev->isoc_ctl.vfield != field) &&
(field == 1)) {
- /* Announces that a new buffer
- * were filled
- */
+ /*
+ * Announces that a new buffer
+ * were filled
+ */
buffer_filled(dev, dma_q, vbuf);
dprintk(dev, V4L2_DEBUG_ISOC,
"new buffer filled\n");
@@ -321,7 +312,7 @@ static int copy_streams(u8 *data, unsigned long len,
break;
case TM6000_URB_MSG_AUDIO:
case TM6000_URB_MSG_PTS:
- size = pktsize; /* Size is always 180 bytes */
+ size = pktsize; /* Size is always 180 bytes */
break;
}
} else {
@@ -363,7 +354,8 @@ static int copy_streams(u8 *data, unsigned long len,
}
}
if (ptr + pktsize > endp) {
- /* End of URB packet, but cmd processing is not
+ /*
+ * End of URB packet, but cmd processing is not
* complete. Preserve the state for a next packet
*/
dev->isoc_ctl.pos = pos + cpysize;
@@ -521,9 +513,21 @@ static void tm6000_irq_callback(struct urb *urb)
struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
int i;
- if (!dev)
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
return;
+ default:
+ tm6000_err("urb completion error %d.\n", urb->status);
+ break;
+ }
+
spin_lock(&dev->slock);
tm6000_isoc_copy(urb);
spin_unlock(&dev->slock);
@@ -750,7 +754,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct tm6000_fh *fh = vq->priv_data;
struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
struct tm6000_core *dev = fh->dev;
- int rc = 0, urb_init = 0;
+ int rc = 0;
BUG_ON(NULL == fh->fmt);
@@ -776,13 +780,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc != 0)
goto fail;
- urb_init = 1;
}
- if (!dev->isoc_ctl.num_bufs)
- urb_init = 1;
-
- if (urb_init) {
+ if (!dev->isoc_ctl.num_bufs) {
rc = tm6000_prepare_isoc(dev);
if (rc < 0)
goto fail;
@@ -1035,8 +1035,8 @@ 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 tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1050,11 +1050,12 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+
if (i != fh->type)
return -EINVAL;
@@ -1067,7 +1068,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
int rc = 0;
- struct tm6000_fh *fh = priv;
+ struct tm6000_fh *fh = priv;
struct tm6000_core *dev = fh->dev;
dev->norm = *norm;
@@ -1464,9 +1465,6 @@ static int tm6000_open(struct file *file)
int i, rc;
int radio = 0;
- printk(KERN_INFO "tm6000: open called (dev=%s)\n",
- video_device_node_name(vdev));
-
dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
video_device_node_name(vdev));
@@ -1507,12 +1505,13 @@ static int tm6000_open(struct file *file)
tm6000_get_std_res(dev);
- fh->width = dev->width;
- fh->height = dev->height;
+ fh->width = dev->width;
+ fh->height = dev->height;
dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, "
"dev->vidq=0x%08lx\n",
- (unsigned long)fh, (unsigned long)dev, (unsigned long)&dev->vidq);
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
"queued=%d\n", list_empty(&dev->vidq.queued));
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
@@ -1531,13 +1530,13 @@ static int tm6000_open(struct file *file)
dev->mode = TM6000_MODE_ANALOG;
}
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
- NULL, &dev->slock,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct tm6000_buffer), fh, &dev->lock);
-
- if (fh->radio) {
+ if (!fh->radio) {
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tm6000_buffer), fh, &dev->lock);
+ } else {
dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
dev->input = 5;
tm6000_set_audio_rinput(dev);
@@ -1583,8 +1582,7 @@ tm6000_poll(struct file *file, struct poll_table_struct *wait)
buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
} else {
/* read() capture */
- return videobuf_poll_stream(file, &fh->vb_vidq,
- wait);
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE ||
@@ -1605,9 +1603,18 @@ static int tm6000_release(struct file *file)
dev->users--;
res_free(dev, fh);
+
if (!dev->users) {
+ int err;
+
tm6000_uninit_isoc(dev);
- videobuf_mmap_free(&fh->vb_vidq);
+
+ if (!fh->radio)
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ err = tm6000_reset(dev);
+ if (err < 0)
+ dev_err(&vdev->dev, "reset failed: %d\n", err);
}
kfree(fh);
@@ -1617,22 +1624,19 @@ static int tm6000_release(struct file *file)
static int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
{
- struct tm6000_fh *fh = file->private_data;
- int ret;
-
- ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ struct tm6000_fh *fh = file->private_data;
- return ret;
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
}
static struct v4l2_file_operations tm6000_fops = {
- .owner = THIS_MODULE,
- .open = tm6000_open,
- .release = tm6000_release,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .read = tm6000_read,
- .poll = tm6000_poll,
- .mmap = tm6000_mmap,
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .read = tm6000_read,
+ .poll = tm6000_poll,
+ .mmap = tm6000_mmap,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -1693,7 +1697,7 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
.vidioc_s_frequency = vidioc_s_frequency,
};
-struct video_device tm6000_radio_template = {
+static struct video_device tm6000_radio_template = {
.name = "tm6000",
.fops = &radio_fops,
.ioctl_ops = &radio_ioctl_ops,
diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h
index c56da628dbef..2777e514eff2 100644
--- a/drivers/staging/tm6000/tm6000.h
+++ b/drivers/media/video/tm6000/tm6000.h
@@ -20,9 +20,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/* Use the tm6000-hack, instead of the proper initialization code i*/
-/* #define HACK 1 */
-
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
@@ -30,7 +27,7 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <media/v4l2-device.h>
-#include <linux/version.h>
+
#include <linux/dvb/frontend.h>
#include "dvb_demux.h"
#include "dvb_frontend.h"
@@ -172,6 +169,8 @@ struct tm6000_endpoint {
unsigned maxsize;
};
+#define TM6000_QUIRK_NO_USB_DELAY (1 << 0)
+
struct tm6000_core {
/* generic device properties */
char name[30]; /* name (including minor) of the device */
@@ -248,6 +247,7 @@ struct tm6000_core {
/* locks */
struct mutex lock;
+ struct mutex usb_lock;
/* usb transfer */
struct usb_device *udev; /* the usb device */
@@ -262,6 +262,8 @@ struct tm6000_core {
struct usb_isoc_ctl isoc_ctl;
spinlock_t slock;
+
+ unsigned long quirks;
};
enum tm6000_ops_type {
@@ -313,6 +315,7 @@ int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
u16 index, u16 mask);
int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep);
int tm6000_init(struct tm6000_core *dev);
+int tm6000_reset(struct tm6000_core *dev);
int tm6000_init_analog_mode(struct tm6000_core *dev);
int tm6000_init_digital_mode(struct tm6000_core *dev);
@@ -381,7 +384,7 @@ extern int tm6000_debug;
#define dprintk(dev, level, fmt, arg...) do {\
if (tm6000_debug & level) \
printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \
- dev->name, __FUNCTION__ , ##arg); } while (0)
+ dev->name, __func__ , ##arg); } while (0)
#define V4L2_DEBUG_REG 0x0004
#define V4L2_DEBUG_I2C 0x0008
@@ -392,4 +395,4 @@ extern int tm6000_debug;
#define tm6000_err(fmt, arg...) do {\
printk(KERN_ERR "tm6000 %s :"fmt, \
- __FUNCTION__ , ##arg); } while (0)
+ __func__ , ##arg); } while (0)
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index c46a3bb95852..f22dbef9b95b 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -1695,14 +1695,17 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
case V4L2_CID_AUDIO_BALANCE:
{
int volume, balance;
+
if (!(desc->flags & CHIP_HAS_VOLUME))
break;
- volume = max(chip->left,chip->right);
+ volume = max(chip->left, chip->right);
balance = ctrl->value;
+ chip->left = (min(65536 - balance, 32768) * volume) / 32768;
+ chip->right = (min(balance, volume * (__u16)32768)) / 32768;
- chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
- chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+ chip_write(chip, desc->leftreg, desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg, desc->volfunc(chip->right));
return 0;
}
diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h
index 4240043c0b2a..25a994944918 100644
--- a/drivers/media/video/tvp5150_reg.h
+++ b/drivers/media/video/tvp5150_reg.h
@@ -45,7 +45,22 @@
/* Reserved 1Fh-27h */
-#define TVP5150_VIDEO_STD 0x28 /* Video standard */
+#define VIDEO_STD_MASK (0x07 >> 1)
+#define TVP5150_VIDEO_STD 0x28 /* Video standard */
+#define VIDEO_STD_AUTO_SWITCH_BIT 0x00
+#define VIDEO_STD_NTSC_MJ_BIT 0x02
+#define VIDEO_STD_PAL_BDGHIN_BIT 0x04
+#define VIDEO_STD_PAL_M_BIT 0x06
+#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08
+#define VIDEO_STD_NTSC_4_43_BIT 0x0a
+#define VIDEO_STD_SECAM_BIT 0x0c
+
+#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01
+#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03
+#define VIDEO_STD_PAL_M_BIT_AS 0x05
+#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07
+#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09
+#define VIDEO_STD_SECAM_BIT_AS 0x0b
/* Reserved 29h-2bh */
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index b799851bf3d0..2e6059a52e9f 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -128,7 +128,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
{ TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE },
{ TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
{ TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE },
- { TVP7002_RGB_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
+ { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE },
{ TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE },
{ TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
{ 0x32, 0x18, TVP7002_RESERVED },
@@ -182,7 +182,6 @@ static const struct i2c_reg_value tvp7002_parms_480P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE },
@@ -204,7 +203,6 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE },
@@ -226,7 +224,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -248,7 +245,6 @@ static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -270,7 +266,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
@@ -292,7 +287,6 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
@@ -314,7 +308,6 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
- { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
@@ -687,6 +680,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
u8 cpl_msb;
int index;
+ /* Return invalid preset if no active input is detected */
+ qpreset->preset = V4L2_DV_INVALID;
+
device = to_tvp7002(sd);
/* Read standards from device registers */
@@ -720,8 +716,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
if (index == NUM_PRESETS) {
v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n",
lpfr, cpln);
- /* Could not detect a signal, so return the 'invalid' preset */
- qpreset->preset = V4L2_DV_INVALID;
return 0;
}
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 742482e30011..a514fa61116c 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -22,11 +22,13 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
+
#include <media/soc_camera.h>
#include <media/tw9910.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
#define GET_ID(val) ((val & 0xF8) >> 3)
#define GET_REV(val) (val & 0x07)
@@ -203,6 +205,10 @@
#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+/* HSYNC start and end are constant for now */
+#define HSYNC_START 0x0260
+#define HSYNC_END 0x0300
+
/*
* structure
*/
@@ -220,22 +226,11 @@ struct tw9910_scale_ctrl {
u16 vscale;
};
-struct tw9910_cropping_ctrl {
- u16 vdelay;
- u16 vactive;
- u16 hdelay;
- u16 hactive;
-};
-
-struct tw9910_hsync_ctrl {
- u16 start;
- u16 end;
-};
-
struct tw9910_priv {
struct v4l2_subdev subdev;
struct tw9910_video_info *info;
const struct tw9910_scale_ctrl *scale;
+ v4l2_std_id norm;
u32 revision;
};
@@ -329,11 +324,6 @@ static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
},
};
-static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
- .start = 0x0260,
- .end = 0x0300,
-};
-
/*
* general function
*/
@@ -378,21 +368,20 @@ static int tw9910_set_scale(struct i2c_client *client,
return ret;
}
-static int tw9910_set_hsync(struct i2c_client *client,
- const struct tw9910_hsync_ctrl *hsync)
+static int tw9910_set_hsync(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
int ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSBEGIN,
- (hsync->start & 0x07F8) >> 3);
+ (HSYNC_START & 0x07F8) >> 3);
if (ret < 0)
return ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSEND,
- (hsync->end & 0x07F8) >> 3);
+ (HSYNC_END & 0x07F8) >> 3);
if (ret < 0)
return ret;
@@ -400,8 +389,8 @@ static int tw9910_set_hsync(struct i2c_client *client,
/* bit 2 - 0 */
if (1 == priv->revision)
ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
- (hsync->start & 0x0007) << 4 |
- (hsync->end & 0x0007));
+ (HSYNC_START & 0x0007) << 4 |
+ (HSYNC_END & 0x0007));
return ret;
}
@@ -433,12 +422,11 @@ static int tw9910_power(struct i2c_client *client, int enable)
return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
}
-static const struct tw9910_scale_ctrl*
-tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+ u32 width, u32 height)
{
const struct tw9910_scale_ctrl *scale;
const struct tw9910_scale_ctrl *ret = NULL;
- v4l2_std_id norm = icd->vdev->current_norm;
__u32 diff = 0xffffffff, tmp;
int size, i;
@@ -465,7 +453,7 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
}
/*
- * soc_camera_ops function
+ * subdevice operations
*/
static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
{
@@ -507,49 +495,27 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
return tw9910_power(client, enable);
}
-static int tw9910_set_bus_param(struct soc_camera_device *icd,
- unsigned long flags)
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 val = VSSL_VVALID | HSSL_DVALID;
+ struct tw9910_priv *priv = to_tw9910(client);
- /*
- * set OUTCTR1
- *
- * We use VVALID and DVALID signals to control VSYNC and HSYNC
- * outputs, in this mode their polarity is inverted.
- */
- if (flags & SOCAM_HSYNC_ACTIVE_LOW)
- val |= HSP_HI;
+ *norm = priv->norm;
- if (flags & SOCAM_VSYNC_ACTIVE_LOW)
- val |= VSP_HI;
-
- return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+ return 0;
}
-static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_LOW | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
- return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- int ret = -EINVAL;
+ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+ return -EINVAL;
- if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL))
- ret = 0;
+ priv->norm = norm;
- return ret;
+ return 0;
}
static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
@@ -600,19 +566,17 @@ static int tw9910_s_register(struct v4l2_subdev *sd,
}
#endif
-static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
{
- struct v4l2_rect *rect = &a->c;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- int ret = -EINVAL;
- u8 val;
+ int ret = -EINVAL;
+ u8 val;
/*
* select suitable norm
*/
- priv->scale = tw9910_select_norm(icd, rect->width, rect->height);
+ priv->scale = tw9910_select_norm(priv->norm, *width, *height);
if (!priv->scale)
goto tw9910_set_fmt_error;
@@ -670,14 +634,12 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
/*
* set hsync
*/
- ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl);
+ ret = tw9910_set_hsync(client);
if (ret < 0)
goto tw9910_set_fmt_error;
- rect->width = priv->scale->width;
- rect->height = priv->scale->height;
- rect->left = 0;
- rect->top = 0;
+ *width = priv->scale->width;
+ *height = priv->scale->height;
return ret;
@@ -694,25 +656,15 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
- if (!priv->scale) {
- int ret;
- struct v4l2_crop crop = {
- .c = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- };
- ret = tw9910_s_crop(sd, &crop);
- if (ret < 0)
- return ret;
- }
-
a->c.left = 0;
a->c.top = 0;
- a->c.width = priv->scale->width;
- a->c.height = priv->scale->height;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->c.width = 640;
+ a->c.height = 480;
+ } else {
+ a->c.width = 768;
+ a->c.height = 576;
+ }
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
@@ -720,14 +672,19 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
a->bounds.left = 0;
a->bounds.top = 0;
- a->bounds.width = 768;
- a->bounds.height = 576;
- a->defrect.left = 0;
- a->defrect.top = 0;
- a->defrect.width = 640;
- a->defrect.height = 480;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->bounds.width = 640;
+ a->bounds.height = 480;
+ } else {
+ a->bounds.width = 768;
+ a->bounds.height = 576;
+ }
+ a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
@@ -743,15 +700,8 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
if (!priv->scale) {
int ret;
- struct v4l2_crop crop = {
- .c = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- };
- ret = tw9910_s_crop(sd, &crop);
+ u32 width = 640, height = 480;
+ ret = tw9910_set_frame(sd, &width, &height);
if (ret < 0)
return ret;
}
@@ -768,17 +718,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
static int tw9910_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- /* See tw9910_s_crop() - no proper cropping support */
- struct v4l2_crop a = {
- .c = {
- .left = 0,
- .top = 0,
- .width = mf->width,
- .height = mf->height,
- },
- };
+ u32 width = mf->width, height = mf->height;
int ret;
WARN_ON(mf->field != V4L2_FIELD_ANY &&
@@ -792,10 +732,10 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd,
mf->colorspace = V4L2_COLORSPACE_JPEG;
- ret = tw9910_s_crop(sd, &a);
+ ret = tw9910_set_frame(sd, &width, &height);
if (!ret) {
- mf->width = priv->scale->width;
- mf->height = priv->scale->height;
+ mf->width = width;
+ mf->height = height;
}
return ret;
}
@@ -804,7 +744,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = client->dev.platform_data;
+ struct tw9910_priv *priv = to_tw9910(client);
const struct tw9910_scale_ctrl *scale;
if (V4L2_FIELD_ANY == mf->field) {
@@ -820,7 +760,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
/*
* select suitable norm
*/
- scale = tw9910_select_norm(icd, mf->width, mf->height);
+ scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
if (!scale)
return -EINVAL;
@@ -830,16 +770,11 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int tw9910_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+static int tw9910_video_probe(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
s32 id;
- /* We must have a parent by now. And it cannot be a wrong one. */
- BUG_ON(!icd->parent ||
- to_soc_camera_host(icd->parent)->nr != icd->iface);
-
/*
* tw9910 only use 8 or 16 bit bus width
*/
@@ -868,20 +803,15 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
dev_info(&client->dev,
"tw9910 Product ID %0x:%0x\n", id, priv->revision);
- icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
- icd->vdev->current_norm = V4L2_STD_NTSC;
+ priv->norm = V4L2_STD_NTSC;
return 0;
}
-static struct soc_camera_ops tw9910_ops = {
- .set_bus_param = tw9910_set_bus_param,
- .query_bus_param = tw9910_query_bus_param,
-};
-
static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
.g_chip_ident = tw9910_g_chip_ident,
.s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tw9910_g_register,
.s_register = tw9910_s_register,
@@ -898,6 +828,45 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
return 0;
}
+static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ u8 val = VSSL_VVALID | HSSL_DVALID;
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+
+ /*
+ * set OUTCTR1
+ *
+ * We use VVALID and DVALID signals to control VSYNC and HSYNC
+ * outputs, in this mode their polarity is inverted.
+ */
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ val |= HSP_HI;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ val |= VSP_HI;
+
+ return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+}
+
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.s_stream = tw9910_s_stream,
.g_mbus_fmt = tw9910_g_fmt,
@@ -905,8 +874,9 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.try_mbus_fmt = tw9910_try_fmt,
.cropcap = tw9910_cropcap,
.g_crop = tw9910_g_crop,
- .s_crop = tw9910_s_crop,
.enum_mbus_fmt = tw9910_enum_fmt,
+ .g_mbus_config = tw9910_g_mbus_config,
+ .s_mbus_config = tw9910_s_mbus_config,
};
static struct v4l2_subdev_ops tw9910_subdev_ops = {
@@ -922,23 +892,18 @@ static int tw9910_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct tw9910_priv *priv;
- struct tw9910_video_info *info;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct i2c_adapter *adapter =
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct i2c_adapter *adapter =
to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
- int ret;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
- if (!icd) {
- dev_err(&client->dev, "TW9910: missing soc-camera data!\n");
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "TW9910: missing platform data!\n");
return -EINVAL;
}
- icl = to_soc_camera_link(icd);
- if (!icl || !icl->priv)
- return -EINVAL;
-
info = icl->priv;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -956,14 +921,9 @@ static int tw9910_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
- icd->ops = &tw9910_ops;
- icd->iface = icl->bus_id;
-
- ret = tw9910_video_probe(icd, client);
- if (ret) {
- icd->ops = NULL;
+ ret = tw9910_video_probe(client);
+ if (ret)
kfree(priv);
- }
return ret;
}
@@ -971,9 +931,7 @@ static int tw9910_probe(struct i2c_client *client,
static int tw9910_remove(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
kfree(priv);
return 0;
}
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
index 338718750945..aea1e3b5f06b 100644
--- a/drivers/media/video/usbvision/Makefile
+++ b/drivers/media/video/usbvision/Makefile
@@ -2,5 +2,5 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-
obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
-EXTRA_CFLAGS += -Idrivers/media/video
-EXTRA_CFLAGS += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index e4100b1f68df..656d4c9e3b9f 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -114,6 +114,11 @@ static struct uvc_format_desc uvc_fmts[] = {
.guid = UVC_GUID_FORMAT_RGBP,
.fcc = V4L2_PIX_FMT_RGB565,
},
+ {
+ .name = "H.264",
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
};
/* ------------------------------------------------------------------------
@@ -2331,6 +2336,14 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_DEF },
+ /* The Imaging Source USB CCD cameras */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x199e,
+ .idProduct = 0x8102,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
/* Bodelin ProScopeHR */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_DEV_HI
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index ea71d5f1f6db..dadf11f704dc 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -32,7 +32,7 @@
* UVC ioctls
*/
static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
- struct uvc_xu_control_mapping *xmap, int old)
+ struct uvc_xu_control_mapping *xmap)
{
struct uvc_control_mapping *map;
unsigned int size;
@@ -58,13 +58,6 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
break;
case V4L2_CTRL_TYPE_MENU:
- if (old) {
- uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not "
- "supported for UVCIOC_CTRL_MAP_OLD.\n");
- ret = -EINVAL;
- goto done;
- }
-
size = xmap->menu_count * sizeof(*map->menu_info);
map->menu_info = kmalloc(size, GFP_KERNEL);
if (map->menu_info == NULL) {
@@ -538,20 +531,6 @@ static int uvc_v4l2_release(struct file *file)
return 0;
}
-static void uvc_v4l2_ioctl_warn(void)
-{
- static int warned;
-
- if (warned)
- return;
-
- uvc_printk(KERN_INFO, "Deprecated UVCIOC_CTRL_{ADD,MAP_OLD,GET,SET} "
- "ioctls will be removed in 2.6.42.\n");
- uvc_printk(KERN_INFO, "See http://www.ideasonboard.org/uvc/upgrade/ "
- "for upgrade instructions.\n");
- warned = 1;
-}
-
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
@@ -1032,37 +1011,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
return -EINVAL;
- /* Dynamic controls. UVCIOC_CTRL_ADD, UVCIOC_CTRL_MAP_OLD,
- * UVCIOC_CTRL_GET and UVCIOC_CTRL_SET are deprecated and scheduled for
- * removal in 2.6.42.
- */
- case __UVCIOC_CTRL_ADD:
- uvc_v4l2_ioctl_warn();
- return -EEXIST;
-
- case __UVCIOC_CTRL_MAP_OLD:
- uvc_v4l2_ioctl_warn();
- case __UVCIOC_CTRL_MAP:
case UVCIOC_CTRL_MAP:
- return uvc_ioctl_ctrl_map(chain, arg,
- cmd == __UVCIOC_CTRL_MAP_OLD);
-
- case __UVCIOC_CTRL_GET:
- case __UVCIOC_CTRL_SET:
- {
- struct uvc_xu_control *xctrl = arg;
- struct uvc_xu_control_query xqry = {
- .unit = xctrl->unit,
- .selector = xctrl->selector,
- .query = cmd == __UVCIOC_CTRL_GET
- ? UVC_GET_CUR : UVC_SET_CUR,
- .size = xctrl->size,
- .data = xctrl->data,
- };
-
- uvc_v4l2_ioctl_warn();
- return uvc_xu_ctrl_query(chain, &xqry);
- }
+ return uvc_ioctl_ctrl_map(chain, arg);
case UVCIOC_CTRL_QUERY:
return uvc_xu_ctrl_query(chain, arg);
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index ffd1158628b6..b015e8e5e8b0 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -790,8 +790,12 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
for (i = 0; i < UVC_URBS; ++i) {
if (stream->urb_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
usb_free_coherent(stream->dev->udev, stream->urb_size,
stream->urb_buffer[i], stream->urb_dma[i]);
+#else
+ kfree(stream->urb_buffer[i]);
+#endif
stream->urb_buffer[i] = NULL;
}
}
@@ -831,9 +835,14 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
stream->urb_size = psize * npackets;
+#ifndef CONFIG_DMA_NONCOHERENT
stream->urb_buffer[i] = usb_alloc_coherent(
stream->dev->udev, stream->urb_size,
gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+#else
+ stream->urb_buffer[i] =
+ kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
+#endif
if (!stream->urb_buffer[i]) {
uvc_free_urb_buffers(stream);
break;
@@ -908,10 +917,14 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
urb->context = stream;
urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
+#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = stream->urb_buffer[i];
- urb->transfer_dma = stream->urb_dma[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
@@ -969,8 +982,10 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
stream->urb_buffer[i], size, uvc_video_complete,
stream);
+#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = stream->urb_dma[i];
+#endif
stream->urb[i] = urb;
}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index cbdd49bf8b67..4c1392ebcd4b 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -1,106 +1,16 @@
#ifndef _USB_VIDEO_H_
#define _USB_VIDEO_H_
-#include <linux/kernel.h>
-#include <linux/videodev2.h>
-
-#ifndef __KERNEL__
-/*
- * This header provides binary compatibility with applications using the private
- * uvcvideo API. This API is deprecated and will be removed in 2.6.42.
- * Applications should be recompiled against the public linux/uvcvideo.h header.
- */
-#warn "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
-
-/*
- * Dynamic controls
- */
-
-/* Data types for UVC control data */
-#define UVC_CTRL_DATA_TYPE_RAW 0
-#define UVC_CTRL_DATA_TYPE_SIGNED 1
-#define UVC_CTRL_DATA_TYPE_UNSIGNED 2
-#define UVC_CTRL_DATA_TYPE_BOOLEAN 3
-#define UVC_CTRL_DATA_TYPE_ENUM 4
-#define UVC_CTRL_DATA_TYPE_BITMASK 5
-
-/* Control flags */
-#define UVC_CONTROL_SET_CUR (1 << 0)
-#define UVC_CONTROL_GET_CUR (1 << 1)
-#define UVC_CONTROL_GET_MIN (1 << 2)
-#define UVC_CONTROL_GET_MAX (1 << 3)
-#define UVC_CONTROL_GET_RES (1 << 4)
-#define UVC_CONTROL_GET_DEF (1 << 5)
-#define UVC_CONTROL_RESTORE (1 << 6)
-#define UVC_CONTROL_AUTO_UPDATE (1 << 7)
-
-#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
- UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
- UVC_CONTROL_GET_DEF)
-
-struct uvc_menu_info {
- __u32 value;
- __u8 name[32];
-};
-
-struct uvc_xu_control_mapping {
- __u32 id;
- __u8 name[32];
- __u8 entity[16];
- __u8 selector;
-
- __u8 size;
- __u8 offset;
- __u32 v4l2_type;
- __u32 data_type;
-
- struct uvc_menu_info __user *menu_info;
- __u32 menu_count;
-
- __u32 reserved[4];
-};
-
-#endif
-
-struct uvc_xu_control_info {
- __u8 entity[16];
- __u8 index;
- __u8 selector;
- __u16 size;
- __u32 flags;
-};
-
-struct uvc_xu_control_mapping_old {
- __u8 reserved[64];
-};
-
-struct uvc_xu_control {
- __u8 unit;
- __u8 selector;
- __u16 size;
- __u8 __user *data;
-};
-
#ifndef __KERNEL__
-#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
-#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
-#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
-#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
-#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
-#else
-#define __UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
-#define __UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
-#define __UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
-#define __UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
-#define __UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
-#endif
-
-#ifdef __KERNEL__
+#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
+#endif /* __KERNEL__ */
+#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/usb/video.h>
#include <linux/uvcvideo.h>
+#include <linux/videodev2.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
@@ -179,6 +89,10 @@ struct uvc_xu_control {
{ 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_H264 \
+ { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
/* ------------------------------------------------------------------------
* Driver specific constants.
*/
@@ -698,6 +612,4 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf);
-#endif /* __KERNEL__ */
-
#endif
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 61979b70f388..c68531b88279 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -159,11 +159,25 @@ struct v4l2_format32 {
} fmt;
};
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index: on return, index of the first created buffer
+ * @count: entry: number of requested buffers,
+ * return: number of created buffers
+ * @memory: buffer memory type
+ * @format: frame format, for which buffers are requested
+ * @reserved: future extensions
+ */
+struct v4l2_create_buffers32 {
+ __u32 index;
+ __u32 count;
+ enum v4l2_memory memory;
+ struct v4l2_format32 format;
+ __u32 reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
- get_user(kp->type, &up->type))
- return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -192,11 +206,24 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
}
}
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+ get_user(kp->type, &up->type))
+ return -EFAULT;
+ return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
- put_user(kp->type, &up->type))
- return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -225,6 +252,22 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
}
}
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ put_user(kp->type, &up->type))
+ return -EFAULT;
+ return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __put_v4l2_format32(&kp->format, &up->format);
+}
+
struct v4l2_standard32 {
__u32 index;
__u32 id[2]; /* __u64 would get the alignment wrong */
@@ -702,6 +745,8 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32)
#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
@@ -721,6 +766,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
struct v4l2_standard v2s;
struct v4l2_ext_controls v2ecs;
struct v4l2_event v2ev;
+ struct v4l2_create_buffers v2crt;
unsigned long vx;
int vi;
} karg;
@@ -751,6 +797,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+ case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
}
switch (cmd) {
@@ -775,6 +823,12 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
compatible_arg = 0;
break;
+ case VIDIOC_CREATE_BUFS:
+ err = get_v4l2_create32(&karg.v2crt, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_PREPARE_BUF:
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
@@ -860,6 +914,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
err = put_v4l2_format32(&karg.v2f, up);
break;
+ case VIDIOC_CREATE_BUFS:
+ err = put_v4l2_create32(&karg.v2crt, up);
+ break;
+
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
@@ -959,6 +1017,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_DQEVENT32:
case VIDIOC_SUBSCRIBE_EVENT:
case VIDIOC_UNSUBSCRIBE_EVENT:
+ case VIDIOC_CREATE_BUFS32:
+ case VIDIOC_PREPARE_BUF32:
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 06b6014d4fb4..5552f8137571 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -43,7 +43,7 @@ struct v4l2_ctrl_helper {
};
/* Small helper function to determine if the autocluster is set to manual
- mode. In that case the is_volatile flag should be ignored. */
+ mode. */
static bool is_cur_manual(const struct v4l2_ctrl *master)
{
return master->is_auto && master->cur.val == master->manual_mode_value;
@@ -210,6 +210,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"Disabled",
"50 Hz",
"60 Hz",
+ "Auto",
NULL
};
static const char * const camera_exposure_auto[] = {
@@ -937,9 +938,14 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
break;
}
if (update_inactive) {
- ctrl->flags &= ~V4L2_CTRL_FLAG_INACTIVE;
- if (!is_cur_manual(ctrl->cluster[0]))
+ /* Note: update_inactive can only be true for auto clusters. */
+ ctrl->flags &=
+ ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+ if (!is_cur_manual(ctrl->cluster[0])) {
ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ctrl->cluster[0]->has_volatiles)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
}
if (changed || update_inactive) {
/* If a control was changed that was not one of the controls
@@ -1394,10 +1400,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
type, min, max,
is_menu ? cfg->menu_skip_mask : step,
def, flags, qmenu, priv);
- if (ctrl) {
+ if (ctrl)
ctrl->is_private = cfg->is_private;
- ctrl->is_volatile = cfg->is_volatile;
- }
return ctrl;
}
EXPORT_SYMBOL(v4l2_ctrl_new_custom);
@@ -1491,6 +1495,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_handler);
/* Cluster controls */
void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
{
+ bool has_volatiles = false;
int i;
/* The first control is the master control and it must not be NULL */
@@ -1500,8 +1505,11 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
if (controls[i]) {
controls[i]->cluster = controls;
controls[i]->ncontrols = ncontrols;
+ if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+ has_volatiles = true;
}
}
+ controls[0]->has_volatiles = has_volatiles;
}
EXPORT_SYMBOL(v4l2_ctrl_cluster);
@@ -1509,22 +1517,25 @@ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
u8 manual_val, bool set_volatile)
{
struct v4l2_ctrl *master = controls[0];
- u32 flag;
+ u32 flag = 0;
int i;
v4l2_ctrl_cluster(ncontrols, controls);
WARN_ON(ncontrols <= 1);
WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+ WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
master->is_auto = true;
+ master->has_volatiles = set_volatile;
master->manual_mode_value = manual_val;
master->flags |= V4L2_CTRL_FLAG_UPDATE;
- flag = is_cur_manual(master) ? 0 : V4L2_CTRL_FLAG_INACTIVE;
+
+ if (!is_cur_manual(master))
+ flag = V4L2_CTRL_FLAG_INACTIVE |
+ (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
for (i = 1; i < ncontrols; i++)
- if (controls[i]) {
- controls[i]->is_volatile = set_volatile;
+ if (controls[i])
controls[i]->flags |= flag;
- }
}
EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
@@ -1579,9 +1590,6 @@ EXPORT_SYMBOL(v4l2_ctrl_grab);
static void log_ctrl(const struct v4l2_ctrl *ctrl,
const char *prefix, const char *colon)
{
- int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
- int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
-
if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
return;
if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
@@ -1612,14 +1620,17 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
printk(KERN_CONT "unknown type %d", ctrl->type);
break;
}
- if (fl_inact && fl_grabbed)
- printk(KERN_CONT " (inactive, grabbed)\n");
- else if (fl_inact)
- printk(KERN_CONT " (inactive)\n");
- else if (fl_grabbed)
- printk(KERN_CONT " (grabbed)\n");
- else
- printk(KERN_CONT "\n");
+ if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
+ V4L2_CTRL_FLAG_GRABBED |
+ V4L2_CTRL_FLAG_VOLATILE)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ printk(KERN_CONT " inactive");
+ if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ printk(KERN_CONT " grabbed");
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
+ printk(KERN_CONT " volatile");
+ }
+ printk(KERN_CONT "\n");
}
/* Log all controls owned by the handler */
@@ -1959,7 +1970,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the new control values */
- if (has_op(master, g_volatile_ctrl) && !is_cur_manual(master)) {
+ if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master))) {
for (j = 0; j < master->ncontrols; j++)
cur_to_new(master->cluster[j]);
ret = call_op(master, g_volatile_ctrl);
@@ -2004,7 +2016,7 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */
- if (ctrl->is_volatile && !is_cur_manual(master)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
for (i = 0; i < master->ncontrols; i++)
cur_to_new(master->cluster[i]);
ret = call_op(master, g_volatile_ctrl);
@@ -2120,6 +2132,20 @@ static int validate_ctrls(struct v4l2_ext_controls *cs,
return 0;
}
+/* Obtain the current volatile values of an autocluster and mark them
+ as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+ int i;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ if (!call_op(master, g_volatile_ctrl))
+ for (i = 1; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 1;
+}
+
/* Try or try-and-set controls */
static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
struct v4l2_ext_controls *cs,
@@ -2165,6 +2191,31 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
if (master->cluster[j])
master->cluster[j]->is_new = 0;
+ /* For volatile autoclusters that are currently in auto mode
+ we need to discover if it will be set to manual mode.
+ If so, then we have to copy the current volatile values
+ first since those will become the new manual values (which
+ may be overwritten by explicit new values from this set
+ of controls). */
+ if (master->is_auto && master->has_volatiles &&
+ !is_cur_manual(master)) {
+ /* Pick an initial non-manual value */
+ s32 new_auto_val = master->manual_mode_value + 1;
+ u32 tmp_idx = idx;
+
+ do {
+ /* Check if the auto control is part of the
+ list, and remember the new value. */
+ if (helpers[tmp_idx].ctrl == master)
+ new_auto_val = cs->controls[tmp_idx].value;
+ tmp_idx = helpers[tmp_idx].next;
+ } while (tmp_idx);
+ /* If the new value == the manual value, then copy
+ the current volatile values. */
+ if (new_auto_val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ }
+
/* Copy the new caller-supplied control values.
user_to_new() sets 'is_new' to 1. */
do {
@@ -2235,6 +2286,12 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
if (master->cluster[i])
master->cluster[i]->is_new = 0;
+ /* For autoclusters with volatiles that are switched from auto to
+ manual mode we have to update the current volatile values since
+ those will become the initial manual values after such a switch. */
+ if (master->is_auto && master->has_volatiles && ctrl == master &&
+ !is_cur_manual(master) && *val == master->manual_mode_value)
+ update_from_auto_cluster(master);
ctrl->val = *val;
ctrl->is_new = 1;
ret = try_or_set_cluster(fh, master, true);
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index e6a2c3b302d4..9fc0ae8a526a 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
+#include <linux/slab.h>
#if defined(CONFIG_SPI)
#include <linux/spi/spi.h>
#endif
@@ -193,6 +194,13 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+static void v4l2_device_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
@@ -206,22 +214,40 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
- vdev = &sd->devnode;
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ err = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
- vdev->release = video_device_release_empty;
+ vdev->release = v4l2_device_release_subdev_node;
vdev->ctrl_handler = sd->ctrl_handler;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
- if (err < 0)
- return err;
+ if (err < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.v4l.major = VIDEO_MAJOR;
sd->entity.v4l.minor = vdev->minor;
#endif
+ sd->devnode = vdev;
}
return 0;
+
+clean_up:
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!sd->devnode)
+ break;
+ video_unregister_device(sd->devnode);
+ }
+
+ return err;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
@@ -247,7 +273,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
if (v4l2_dev->mdev)
media_device_unregister_entity(&sd->entity);
#endif
- video_unregister_device(&sd->devnode);
+ video_unregister_device(sd->devnode);
module_put(sd->owner);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 002ce1363443..e1da8fc9dd2f 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -55,6 +55,19 @@
memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+#define have_fmt_ops(foo) ( \
+ ops->vidioc_##foo##_fmt_vid_cap || \
+ ops->vidioc_##foo##_fmt_vid_out || \
+ ops->vidioc_##foo##_fmt_vid_cap_mplane || \
+ ops->vidioc_##foo##_fmt_vid_out_mplane || \
+ ops->vidioc_##foo##_fmt_vid_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_cap || \
+ ops->vidioc_##foo##_fmt_vid_out_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_out || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_cap || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_out || \
+ ops->vidioc_##foo##_fmt_type_private)
+
struct std_descr {
v4l2_std_id std;
const char *descr;
@@ -260,6 +273,8 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT",
[_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT",
[_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT",
+ [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS",
+ [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF",
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -477,63 +492,6 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
return -EINVAL;
}
-/**
- * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane
- * equivalent
- */
-static int fmt_sp_to_mp(const struct v4l2_format *f_sp,
- struct v4l2_format *f_mp)
-{
- struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
- const struct v4l2_pix_format *pix = &f_sp->fmt.pix;
-
- if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- else
- return -EINVAL;
-
- pix_mp->width = pix->width;
- pix_mp->height = pix->height;
- pix_mp->pixelformat = pix->pixelformat;
- pix_mp->field = pix->field;
- pix_mp->colorspace = pix->colorspace;
- pix_mp->num_planes = 1;
- pix_mp->plane_fmt[0].sizeimage = pix->sizeimage;
- pix_mp->plane_fmt[0].bytesperline = pix->bytesperline;
-
- return 0;
-}
-
-/**
- * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar
- * equivalent
- */
-static int fmt_mp_to_sp(const struct v4l2_format *f_mp,
- struct v4l2_format *f_sp)
-{
- const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
- struct v4l2_pix_format *pix = &f_sp->fmt.pix;
-
- if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- else
- return -EINVAL;
-
- pix->width = pix_mp->width;
- pix->height = pix_mp->height;
- pix->pixelformat = pix_mp->pixelformat;
- pix->field = pix_mp->field;
- pix->colorspace = pix_mp->colorspace;
- pix->sizeimage = pix_mp->plane_fmt[0].sizeimage;
- pix->bytesperline = pix_mp->plane_fmt[0].bytesperline;
-
- return 0;
-}
-
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
@@ -541,8 +499,8 @@ static long __video_do_ioctl(struct file *file,
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
void *fh = file->private_data;
struct v4l2_fh *vfh = NULL;
- struct v4l2_format f_copy;
int use_fh_prio = 0;
+ long ret_prio = 0;
long ret = -ENOTTY;
if (ops == NULL) {
@@ -562,39 +520,8 @@ static long __video_do_ioctl(struct file *file,
use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
}
- if (use_fh_prio) {
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_OUTPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_S_FMT:
- case VIDIOC_S_CROP:
- case VIDIOC_S_AUDIO:
- case VIDIOC_S_AUDOUT:
- case VIDIOC_S_EXT_CTRLS:
- case VIDIOC_S_FBUF:
- case VIDIOC_S_PRIORITY:
- case VIDIOC_S_DV_PRESET:
- case VIDIOC_S_DV_TIMINGS:
- case VIDIOC_S_JPEGCOMP:
- case VIDIOC_S_MODULATOR:
- case VIDIOC_S_PARM:
- case VIDIOC_S_HW_FREQ_SEEK:
- case VIDIOC_ENCODER_CMD:
- case VIDIOC_OVERLAY:
- case VIDIOC_REQBUFS:
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- ret = v4l2_prio_check(vfd->prio, vfh->prio);
- if (ret)
- goto exit_prio;
- ret = -EINVAL;
- break;
- }
- }
+ if (use_fh_prio)
+ ret_prio = v4l2_prio_check(vfd->prio, vfh->prio);
switch (cmd) {
@@ -638,12 +565,14 @@ static long __video_do_ioctl(struct file *file,
enum v4l2_priority *p = arg;
if (!ops->vidioc_s_priority && !use_fh_prio)
- break;
+ break;
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);
+ ret = ret_prio ? ret_prio :
+ v4l2_prio_change(&vfd->v4l2_dev->prio,
+ &vfh->prio, *p);
break;
}
@@ -654,37 +583,37 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_enum_fmt_vid_cap)
+ 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 (ops->vidioc_enum_fmt_vid_cap_mplane)
+ if (likely(ops->vidioc_enum_fmt_vid_cap_mplane))
ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (ops->vidioc_enum_fmt_vid_overlay)
+ if (likely(ops->vidioc_enum_fmt_vid_overlay))
ret = ops->vidioc_enum_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_enum_fmt_vid_out)
+ if (likely(ops->vidioc_enum_fmt_vid_out))
ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (ops->vidioc_enum_fmt_vid_out_mplane)
+ if (likely(ops->vidioc_enum_fmt_vid_out_mplane))
ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_enum_fmt_type_private)
+ if (likely(ops->vidioc_enum_fmt_type_private))
ret = ops->vidioc_enum_fmt_type_private(file,
fh, f);
break;
default:
break;
}
- if (!ret)
+ 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,
@@ -693,6 +622,14 @@ static long __video_do_ioctl(struct file *file,
(f->pixelformat >> 16) & 0xff,
(f->pixelformat >> 24) & 0xff,
f->description);
+ else if (ret == -ENOTTY &&
+ (ops->vidioc_enum_fmt_vid_cap ||
+ ops->vidioc_enum_fmt_vid_out ||
+ ops->vidioc_enum_fmt_vid_cap_mplane ||
+ ops->vidioc_enum_fmt_vid_out_mplane ||
+ ops->vidioc_enum_fmt_vid_overlay ||
+ ops->vidioc_enum_fmt_type_private))
+ ret = -EINVAL;
break;
}
case VIDIOC_G_FMT:
@@ -704,119 +641,67 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_g_fmt_vid_cap) {
+ if (ops->vidioc_g_fmt_vid_cap)
ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_g_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- /* Driver is currently in multi-planar format,
- * we can't return it in single-planar API*/
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- ret = -EBUSY;
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (ops->vidioc_g_fmt_vid_cap_mplane) {
+ if (ops->vidioc_g_fmt_vid_cap_mplane)
ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_g_fmt_vid_cap) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (ops->vidioc_g_fmt_vid_overlay)
+ if (likely(ops->vidioc_g_fmt_vid_overlay))
ret = ops->vidioc_g_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_g_fmt_vid_out) {
+ if (ops->vidioc_g_fmt_vid_out)
ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_g_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- /* Driver is currently in multi-planar format,
- * we can't return it in single-planar API*/
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- ret = -EBUSY;
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (ops->vidioc_g_fmt_vid_out_mplane) {
+ if (ops->vidioc_g_fmt_vid_out_mplane)
ret = ops->vidioc_g_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_g_fmt_vid_out) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_g_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (ops->vidioc_g_fmt_vid_out_overlay)
+ if (likely(ops->vidioc_g_fmt_vid_out_overlay))
ret = ops->vidioc_g_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_vbi_cap)
+ if (likely(ops->vidioc_g_fmt_vbi_cap))
ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_vbi_out)
+ if (likely(ops->vidioc_g_fmt_vbi_out))
ret = ops->vidioc_g_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_cap))
ret = ops->vidioc_g_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_out))
ret = ops->vidioc_g_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_g_fmt_type_private)
+ if (likely(ops->vidioc_g_fmt_type_private))
ret = ops->vidioc_g_fmt_type_private(file,
fh, f);
break;
}
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(g)))
+ ret = -EINVAL;
break;
}
@@ -824,6 +709,14 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_format *f = (struct v4l2_format *)arg;
+ if (!have_fmt_ops(s))
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
+
/* FIXME: Should be one dump per type */
dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
@@ -831,44 +724,15 @@ static long __video_do_ioctl(struct file *file,
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) {
+ if (ops->vidioc_s_fmt_vid_cap)
ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_s_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
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) {
+ if (ops->vidioc_s_fmt_vid_cap_mplane)
ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_s_fmt_vid_cap &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -879,44 +743,15 @@ static long __video_do_ioctl(struct file *file,
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) {
+ if (ops->vidioc_s_fmt_vid_out)
ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_s_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh,
- &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
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) {
+ if (ops->vidioc_s_fmt_vid_out_mplane)
ret = ops->vidioc_s_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_s_fmt_vid_out &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_s_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_mp_to_sp(&f_copy, f);
- }
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -926,29 +761,30 @@ static long __video_do_ioctl(struct file *file,
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_s_fmt_vbi_cap)
+ if (likely(ops->vidioc_s_fmt_vbi_cap))
ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_s_fmt_vbi_out)
+ if (likely(ops->vidioc_s_fmt_vbi_out))
ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_s_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_cap))
ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_s_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_out))
ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
fh, f);
+
break;
case V4L2_BUF_TYPE_PRIVATE:
/* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (ops->vidioc_s_fmt_type_private)
+ if (likely(ops->vidioc_s_fmt_type_private))
ret = ops->vidioc_s_fmt_type_private(file,
fh, f);
break;
@@ -965,132 +801,77 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_cap) {
+ if (ops->vidioc_try_fmt_vid_cap)
ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
- } else if (ops->vidioc_try_fmt_vid_cap_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
- fh, &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_cap_mplane) {
+ if (ops->vidioc_try_fmt_vid_cap_mplane)
ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
fh, f);
- } else if (ops->vidioc_try_fmt_vid_cap &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_cap(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_try_fmt_vid_overlay)
+ if (likely(ops->vidioc_try_fmt_vid_overlay))
ret = ops->vidioc_try_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_out) {
+ if (ops->vidioc_try_fmt_vid_out)
ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
- } else if (ops->vidioc_try_fmt_vid_out_mplane) {
- if (fmt_sp_to_mp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_out_mplane(file,
- fh, &f_copy);
- if (ret)
- break;
-
- if (f_copy.fmt.pix_mp.num_planes > 1) {
- /* Drivers shouldn't adjust from 1-plane
- * to more than 1-plane formats */
- ret = -EBUSY;
- WARN_ON(1);
- break;
- }
- ret = fmt_mp_to_sp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_out_mplane) {
+ if (ops->vidioc_try_fmt_vid_out_mplane)
ret = ops->vidioc_try_fmt_vid_out_mplane(file,
fh, f);
- } else if (ops->vidioc_try_fmt_vid_out &&
- f->fmt.pix_mp.num_planes == 1) {
- if (fmt_mp_to_sp(f, &f_copy))
- break;
- ret = ops->vidioc_try_fmt_vid_out(file,
- fh, &f_copy);
- if (ret)
- break;
-
- ret = fmt_sp_to_mp(&f_copy, f);
- }
if (!ret)
v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_try_fmt_vid_out_overlay)
+ if (likely(ops->vidioc_try_fmt_vid_out_overlay))
ret = ops->vidioc_try_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_try_fmt_vbi_cap)
+ if (likely(ops->vidioc_try_fmt_vbi_cap))
ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (ops->vidioc_try_fmt_vbi_out)
+ if (likely(ops->vidioc_try_fmt_vbi_out))
ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_try_fmt_sliced_vbi_cap)
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_cap))
ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (ops->vidioc_try_fmt_sliced_vbi_out)
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_out))
ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
/* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (ops->vidioc_try_fmt_type_private)
+ if (likely(ops->vidioc_try_fmt_type_private))
ret = ops->vidioc_try_fmt_type_private(file,
fh, f);
break;
}
-
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(try)))
+ ret = -EINVAL;
break;
}
/* FIXME: Those buf reqs could be handled here,
@@ -1103,6 +884,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_reqbufs)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -1168,6 +953,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_overlay)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_overlay(file, fh, *i);
break;
@@ -1193,6 +982,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_fbuf)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
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);
@@ -1205,6 +998,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_streamon)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamon(file, fh, i);
break;
@@ -1215,6 +1012,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_streamoff)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamoff(file, fh, i);
break;
@@ -1227,6 +1028,10 @@ static long __video_do_ioctl(struct file *file,
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
@@ -1262,16 +1067,15 @@ static long __video_do_ioctl(struct file *file,
{
v4l2_std_id *id = arg;
- ret = 0;
/* Calls the specific handler */
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, id);
- else if (vfd->current_norm)
+ else if (vfd->current_norm) {
+ ret = 0;
*id = vfd->current_norm;
- else
- ret = -EINVAL;
+ }
- if (!ret)
+ if (likely(!ret))
dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
break;
}
@@ -1281,15 +1085,20 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
+ if (!ops->vidioc_s_std)
+ break;
+
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
norm = (*id) & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
break;
/* Calls the specific handler */
- if (ops->vidioc_s_std)
- ret = ops->vidioc_s_std(file, fh, &norm);
- else
- ret = -EINVAL;
+ ret = ops->vidioc_s_std(file, fh, &norm);
/* Updates standard information */
if (ret >= 0)
@@ -1302,6 +1111,14 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_querystd)
break;
+ /*
+ * 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",
@@ -1358,6 +1175,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_input)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_input(file, fh, *i);
break;
@@ -1410,6 +1231,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_output)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_output(file, fh, *i);
break;
@@ -1479,6 +1304,10 @@ static long __video_do_ioctl(struct file *file,
if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
@@ -1504,6 +1333,8 @@ static long __video_do_ioctl(struct file *file,
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:
@@ -1515,8 +1346,10 @@ static long __video_do_ioctl(struct file *file,
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 && check_ext_ctrls(p, 0))
- ret = ops->vidioc_g_ext_ctrls(file, fh, 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);
@@ -1530,6 +1363,10 @@ static long __video_do_ioctl(struct file *file,
if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
!ops->vidioc_s_ext_ctrls)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
v4l_print_ext_ctrls(cmd, vfd, p, 1);
if (vfh && vfh->ctrl_handler)
ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
@@ -1537,6 +1374,8 @@ static long __video_do_ioctl(struct file *file,
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:
@@ -1554,6 +1393,8 @@ static long __video_do_ioctl(struct file *file,
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:
@@ -1614,6 +1455,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_audio)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
"mode=0x%x\n", p->index, p->name,
p->capability, p->mode);
@@ -1654,6 +1499,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_audout)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"mode=%d\n", p->index, p->name,
p->capability, p->mode);
@@ -1683,6 +1532,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_modulator)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"rangelow=%d, rangehigh=%d, txsubchans=%d\n",
p->index, p->name, p->capability, p->rangelow,
@@ -1709,6 +1562,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_crop)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
dbgrect(vfd, "", &p->c);
ret = ops->vidioc_s_crop(file, fh, p);
@@ -1752,11 +1609,15 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_g_jpegcomp)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
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);
+ ret = ops->vidioc_s_jpegcomp(file, fh, p);
break;
}
case VIDIOC_G_ENC_INDEX:
@@ -1777,6 +1638,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_encoder_cmd)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = ops->vidioc_encoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1797,6 +1662,8 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_streamparm *p = arg;
+ if (!ops->vidioc_g_parm && !vfd->current_norm)
+ break;
if (ops->vidioc_g_parm) {
ret = check_fmt(ops, p->type);
if (ret)
@@ -1805,14 +1672,13 @@ static long __video_do_ioctl(struct file *file,
} else {
v4l2_std_id std = vfd->current_norm;
+ ret = -EINVAL;
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
break;
ret = 0;
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, &std);
- else if (std == 0)
- ret = -EINVAL;
if (ret == 0)
v4l2_video_std_frame_period(std,
&p->parm.capture.timeperframe);
@@ -1827,6 +1693,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_parm)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -1862,6 +1732,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_tuner)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd, "index=%d, name=%s, type=%d, "
@@ -1896,6 +1770,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_frequency)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
p->tuner, p->type, p->frequency);
ret = ops->vidioc_s_frequency(file, fh, p);
@@ -1970,6 +1848,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_hw_freq_seek)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd,
@@ -2074,6 +1956,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_dv_preset)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
dbgarg(cmd, "preset=%d\n", p->preset);
ret = ops->vidioc_s_dv_preset(file, fh, p);
@@ -2109,6 +1995,10 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_s_dv_timings)
break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
switch (p->type) {
case V4L2_DV_BT_656_1120:
@@ -2216,20 +2106,47 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "type=0x%8.8x", sub->type);
break;
}
- default:
+ case VIDIOC_CREATE_BUFS:
{
- bool valid_prio = true;
+ struct v4l2_create_buffers *create = arg;
- if (!ops->vidioc_default)
+ if (!ops->vidioc_create_bufs)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = check_fmt(ops, create->format.type);
+ if (ret)
break;
- if (use_fh_prio)
- valid_prio = v4l2_prio_check(vfd->prio, vfh->prio) >= 0;
- ret = ops->vidioc_default(file, fh, valid_prio, cmd, arg);
+
+ ret = ops->vidioc_create_bufs(file, fh, create);
+
+ dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
break;
}
+ case VIDIOC_PREPARE_BUF:
+ {
+ struct v4l2_buffer *b = arg;
+
+ if (!ops->vidioc_prepare_buf)
+ break;
+ ret = check_fmt(ops, b->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_prepare_buf(file, fh, b);
+
+ dbgarg(cmd, "index=%d", b->index);
+ break;
+ }
+ default:
+ if (!ops->vidioc_default)
+ break;
+ ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg);
+ break;
} /* switch */
-exit_prio:
if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
if (ret < 0) {
v4l_print_ioctl(vfd->name, cmd);
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index 3b15bf5892a8..975d0fa938c6 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -97,11 +97,12 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- if (list_empty(&q_ctx->rdy_queue))
- goto end;
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
-end:
spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
return &b->vb;
}
@@ -117,12 +118,13 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
unsigned long flags;
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- if (!list_empty(&q_ctx->rdy_queue)) {
- b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer,
- list);
- list_del(&b->list);
- q_ctx->num_rdy--;
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
}
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+ list_del(&b->list);
+ q_ctx->num_rdy--;
spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
return &b->vb;
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index b7967c9dc4ae..179e20e23fc4 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -173,6 +173,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_UNSUBSCRIBE_EVENT:
return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, g_register, p);
+ }
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, s_register, p);
+ }
+#endif
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
case VIDIOC_SUBDEV_G_FMT: {
struct v4l2_subdev_format *format = arg;
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 3015e6000946..979e544388cb 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -38,13 +38,13 @@ module_param(debug, int, 0644);
(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
- V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+ V4L2_BUF_FLAG_PREPARED)
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
*/
-static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
- unsigned long *plane_sizes)
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
@@ -53,13 +53,13 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
/* Allocate memory for all planes in this buffer */
for (plane = 0; plane < vb->num_planes; ++plane) {
mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
- plane_sizes[plane]);
+ q->plane_sizes[plane]);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
vb->planes[plane].mem_priv = mem_priv;
- vb->v4l2_planes[plane].length = plane_sizes[plane];
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
}
return 0;
@@ -110,13 +110,22 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
* every buffer on the queue
*/
-static void __setup_offsets(struct vb2_queue *q)
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
- unsigned long off = 0;
+ unsigned long off;
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (q->num_buffers) {
+ struct v4l2_plane *p;
+ vb = q->bufs[q->num_buffers - 1];
+ p = &vb->v4l2_planes[vb->num_planes - 1];
+ off = PAGE_ALIGN(p->m.mem_offset + p->length);
+ } else {
+ off = 0;
+ }
+
+ for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
@@ -141,8 +150,7 @@ static void __setup_offsets(struct vb2_queue *q)
* Returns the number of buffers successfully allocated.
*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
- unsigned int num_buffers, unsigned int num_planes,
- unsigned long plane_sizes[])
+ unsigned int num_buffers, unsigned int num_planes)
{
unsigned int buffer;
struct vb2_buffer *vb;
@@ -163,13 +171,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
- vb->v4l2_buf.index = buffer;
+ vb->v4l2_buf.index = q->num_buffers + buffer;
vb->v4l2_buf.type = q->type;
vb->v4l2_buf.memory = memory;
/* Allocate video buffer memory for the MMAP type */
if (memory == V4L2_MEMORY_MMAP) {
- ret = __vb2_buf_mem_alloc(vb, plane_sizes);
+ ret = __vb2_buf_mem_alloc(vb);
if (ret) {
dprintk(1, "Failed allocating memory for "
"buffer %d\n", buffer);
@@ -191,15 +199,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
}
}
- q->bufs[buffer] = vb;
+ q->bufs[q->num_buffers + buffer] = vb;
}
- q->num_buffers = buffer;
-
- __setup_offsets(q);
+ __setup_offsets(q, buffer);
dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
- q->num_buffers, num_planes);
+ buffer, num_planes);
return buffer;
}
@@ -207,12 +213,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
/**
* __vb2_free_mem() - release all video buffer memory for a given queue
*/
-static void __vb2_free_mem(struct vb2_queue *q)
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;
struct vb2_buffer *vb;
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
@@ -226,17 +233,18 @@ static void __vb2_free_mem(struct vb2_queue *q)
}
/**
- * __vb2_queue_free() - free the queue - video memory and related information
- * and return the queue to an uninitialized state. Might be called even if the
- * queue has already been freed.
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
*/
-static void __vb2_queue_free(struct vb2_queue *q)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;
/* Call driver-provided cleanup function for each buffer, if provided */
if (q->ops->buf_cleanup) {
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
if (NULL == q->bufs[buffer])
continue;
q->ops->buf_cleanup(q->bufs[buffer]);
@@ -244,23 +252,25 @@ static void __vb2_queue_free(struct vb2_queue *q)
}
/* Release video buffer memory */
- __vb2_free_mem(q);
+ __vb2_free_mem(q, buffers);
/* Free videobuf buffers */
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
kfree(q->bufs[buffer]);
q->bufs[buffer] = NULL;
}
- q->num_buffers = 0;
- q->memory = 0;
+ q->num_buffers -= buffers;
+ if (!q->num_buffers)
+ q->memory = 0;
}
/**
* __verify_planes_array() - verify that the planes array passed in struct
* v4l2_buffer from userspace can be safely used
*/
-static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
/* Is memory for copying plane information present? */
if (NULL == b->m.planes) {
@@ -279,13 +289,48 @@ static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
}
/**
+ * __buffer_in_use() - return true if the buffer is in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ unsigned int plane;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /*
+ * If num_users() has not been provided, call_memop
+ * will return 0, apparently nobody cares about this
+ * case anyway. If num_users() returns more than 1,
+ * we are not the only user of the plane's memory.
+ */
+ if (call_memop(q, plane, num_users,
+ vb->planes[plane].mem_priv) > 1)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+ unsigned int buffer;
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (__buffer_in_use(q, q->bufs[buffer]))
+ return true;
+ }
+ return false;
+}
+
+/**
* __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
* returned to userspace
*/
static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
struct vb2_queue *q = vb->vb2_queue;
- int ret = 0;
+ int ret;
/* Copy back data such as timestamp, flags, input, etc. */
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
@@ -332,15 +377,18 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
case VB2_BUF_STATE_DONE:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
+ case VB2_BUF_STATE_PREPARED:
+ b->flags |= V4L2_BUF_FLAG_PREPARED;
+ break;
case VB2_BUF_STATE_DEQUEUED:
/* nothing */
break;
}
- if (vb->num_planes_mapped == vb->num_planes)
+ if (__buffer_in_use(q, vb))
b->flags |= V4L2_BUF_FLAG_MAPPED;
- return ret;
+ return 0;
}
/**
@@ -402,33 +450,6 @@ static int __verify_mmap_ops(struct vb2_queue *q)
}
/**
- * __buffers_in_use() - return true if any buffers on the queue are in use and
- * the queue cannot be freed (by the means of REQBUFS(0)) call
- */
-static bool __buffers_in_use(struct vb2_queue *q)
-{
- unsigned int buffer, plane;
- struct vb2_buffer *vb;
-
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
- vb = q->bufs[buffer];
- for (plane = 0; plane < vb->num_planes; ++plane) {
- /*
- * If num_users() has not been provided, call_memop
- * will return 0, apparently nobody cares about this
- * case anyway. If num_users() returns more than 1,
- * we are not the only user of the plane's memory.
- */
- if (call_memop(q, plane, num_users,
- vb->planes[plane].mem_priv) > 1)
- return true;
- }
- }
-
- return false;
-}
-
-/**
* vb2_reqbufs() - Initiate streaming
* @q: videobuf2 queue
* @req: struct passed from userspace to vidioc_reqbufs handler in driver
@@ -453,8 +474,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
*/
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
- unsigned int num_buffers, num_planes;
- unsigned long plane_sizes[VIDEO_MAX_PLANES];
+ unsigned int num_buffers, allocated_buffers, num_planes = 0;
int ret = 0;
if (q->fileio) {
@@ -502,7 +522,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return -EBUSY;
}
- __vb2_queue_free(q);
+ __vb2_queue_free(q, q->num_buffers);
/*
* In case of REQBUFS(0) return immediately without calling
@@ -516,7 +536,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
* Make sure the requested values and current defaults are sane.
*/
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
- memset(plane_sizes, 0, sizeof(plane_sizes));
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
q->memory = req->memory;
@@ -524,57 +544,180 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
- ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
- plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
+ q->plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
- ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
- plane_sizes);
+ ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
if (ret == 0) {
dprintk(1, "Memory allocation failed\n");
return -ENOMEM;
}
+ allocated_buffers = ret;
+
/*
* Check if driver can handle the allocated number of buffers.
*/
- if (ret < num_buffers) {
- unsigned int orig_num_buffers;
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
- orig_num_buffers = num_buffers = ret;
- ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
- plane_sizes, q->alloc_ctx);
- if (ret)
- goto free_mem;
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
- if (orig_num_buffers < num_buffers) {
+ if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
- goto free_mem;
- }
/*
- * Ok, driver accepted smaller number of buffers.
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
*/
- ret = num_buffers;
+ }
+
+ q->num_buffers = allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
}
/*
* Return the number of successfully allocated buffers
* to the userspace.
*/
- req->count = ret;
+ req->count = allocated_buffers;
return 0;
-
-free_mem:
- __vb2_queue_free(q);
- return ret;
}
EXPORT_SYMBOL_GPL(vb2_reqbufs);
/**
+ * vb2_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
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * 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)
+{
+ 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;
+ }
+
+ if (q->num_buffers == VIDEO_MAX_FRAME) {
+ dprintk(1, "%s(): maximum number of buffers already allocated\n",
+ __func__);
+ 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));
+ q->memory = create->memory;
+ }
+
+ num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+ /*
+ * Ask the driver, whether the requested number of buffers, planes per
+ * buffer and their sizes are acceptable
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* 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;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the so far allocated number of buffers.
+ */
+ if (ret < num_buffers) {
+ num_buffers = ret;
+
+ /*
+ * q->num_buffers contains the total number of buffers, that the
+ * queue driver has set up
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers += allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ create->count = allocated_buffers;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
* vb2_plane_vaddr() - Return a kernel virtual address of a given plane
* @vb: vb2_buffer to which the plane in question belongs to
* @plane_no: plane number for which the address is to be returned
@@ -658,7 +801,7 @@ EXPORT_SYMBOL_GPL(vb2_buffer_done);
* __fill_vb2_buffer() - fill a vb2_buffer with information provided in
* a v4l2_buffer by the userspace
*/
-static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
struct v4l2_plane *v4l2_planes)
{
unsigned int plane;
@@ -722,7 +865,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
/**
* __qbuf_userptr() - handle qbuf of a USERPTR buffer
*/
-static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
struct v4l2_plane planes[VIDEO_MAX_PLANES];
struct vb2_queue *q = vb->vb2_queue;
@@ -745,12 +888,20 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
dprintk(3, "qbuf: userspace address for plane %d changed, "
"reacquiring memory\n", plane);
+ /* Check if the provided plane buffer is large enough */
+ if (planes[plane].length < q->plane_sizes[plane]) {
+ ret = -EINVAL;
+ goto err;
+ }
+
/* Release previously acquired memory if present */
if (vb->planes[plane].mem_priv)
call_memop(q, plane, put_userptr,
vb->planes[plane].mem_priv);
vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
/* Acquire each plane's memory */
if (q->mem_ops->get_userptr) {
@@ -788,10 +939,13 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
return 0;
err:
/* In case of errors, release planes that were already acquired */
- for (; plane > 0; --plane) {
- call_memop(q, plane, put_userptr,
- vb->planes[plane - 1].mem_priv);
- vb->planes[plane - 1].mem_priv = NULL;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, plane, put_userptr,
+ vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
}
return ret;
@@ -800,7 +954,7 @@ err:
/**
* __qbuf_mmap() - handle qbuf of an MMAP buffer
*/
-static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
}
@@ -817,6 +971,95 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
q->ops->buf_queue(vb);
}
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret;
+
+ switch (q->memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = __qbuf_mmap(vb, b);
+ break;
+ case V4L2_MEMORY_USERPTR:
+ ret = __qbuf_userptr(vb, b);
+ break;
+ default:
+ WARN(1, "Invalid queue type\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ ret = call_qop(q, buf_prepare, vb);
+ if (ret)
+ dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+ else
+ vb->state = VB2_BUF_STATE_PREPARED;
+
+ return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_prepare_buf
+ * handler in driver
+ *
+ * Should be called from vidioc_prepare_buf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ * driver-specific buffer initialization can be performed,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "%s(): invalid buffer type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "%s(): buffer index out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "%s(): buffer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "%s(): invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+ return -EINVAL;
+ }
+
+ ret = __buf_prepare(vb, b);
+ if (ret < 0)
+ return ret;
+
+ __fill_v4l2_buffer(vb, b);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
/**
* vb2_qbuf() - Queue a buffer from userspace
* @q: videobuf2 queue
@@ -826,8 +1069,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
* Should be called from vidioc_qbuf ioctl handler of a driver.
* This function:
* 1) verifies the passed buffer,
- * 2) calls buf_prepare callback in the driver (if provided), in which
- * driver-specific buffer initialization can be performed,
+ * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
+ * which driver-specific buffer initialization can be performed,
* 3) if streaming is on, queues the buffer in driver by the means of buf_queue
* callback for processing.
*
@@ -837,7 +1080,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
- int ret = 0;
+ int ret;
if (q->fileio) {
dprintk(1, "qbuf: file io in progress\n");
@@ -866,29 +1109,18 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
return -EINVAL;
}
- if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ switch (vb->state) {
+ case VB2_BUF_STATE_DEQUEUED:
+ ret = __buf_prepare(vb, b);
+ if (ret)
+ return ret;
+ case VB2_BUF_STATE_PREPARED:
+ break;
+ default:
dprintk(1, "qbuf: buffer already in use\n");
return -EINVAL;
}
- if (q->memory == V4L2_MEMORY_MMAP)
- ret = __qbuf_mmap(vb, b);
- else if (q->memory == V4L2_MEMORY_USERPTR)
- ret = __qbuf_userptr(vb, b);
- else {
- WARN(1, "Invalid queue type\n");
- return -EINVAL;
- }
-
- if (ret)
- return ret;
-
- ret = call_qop(q, buf_prepare, vb);
- if (ret) {
- dprintk(1, "qbuf: buffer preparation failed\n");
- return ret;
- }
-
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
@@ -903,6 +1135,9 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
if (q->streaming)
__enqueue_in_driver(vb);
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+
dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
return 0;
}
@@ -1096,6 +1331,43 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
EXPORT_SYMBOL_GPL(vb2_dqbuf);
/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+ unsigned int i;
+
+ /*
+ * Tell driver to stop all transactions and release all queued
+ * buffers.
+ */
+ if (q->streaming)
+ call_qop(q, stop_streaming, q);
+ q->streaming = 0;
+
+ /*
+ * Remove all buffers from videobuf's list...
+ */
+ INIT_LIST_HEAD(&q->queued_list);
+ /*
+ * ...and done list; userspace will not receive any buffers it
+ * has not already dequeued before initiating cancel.
+ */
+ INIT_LIST_HEAD(&q->done_list);
+ atomic_set(&q->queued_count, 0);
+ wake_up_all(&q->done_wq);
+
+ /*
+ * Reinitialize all buffers for next use.
+ */
+ for (i = 0; i < q->num_buffers; ++i)
+ q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
* vb2_streamon - start streaming
* @q: videobuf2 queue
* @type: type argument passed from userspace to vidioc_streamon handler
@@ -1103,7 +1375,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf);
* Should be called from vidioc_streamon handler of a driver.
* This function:
* 1) verifies current state
- * 2) starts streaming and passes any previously queued buffers to the driver
+ * 2) passes any previously queued buffers to the driver and starts streaming
*
* The return values from this function are intended to be directly returned
* from vidioc_streamon handler in the driver.
@@ -1129,75 +1401,29 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
}
/*
- * Cannot start streaming on an OUTPUT device if no buffers have
- * been queued yet.
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
*/
- if (V4L2_TYPE_IS_OUTPUT(q->type)) {
- if (list_empty(&q->queued_list)) {
- dprintk(1, "streamon: no output buffers queued\n");
- return -EINVAL;
- }
- }
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
/*
* Let driver notice that streaming state has been enabled.
*/
- ret = call_qop(q, start_streaming, q);
+ ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
if (ret) {
dprintk(1, "streamon: driver refused to start streaming\n");
+ __vb2_queue_cancel(q);
return ret;
}
q->streaming = 1;
- /*
- * If any buffers were queued before streamon,
- * we can now pass them to driver for processing.
- */
- list_for_each_entry(vb, &q->queued_list, queued_entry)
- __enqueue_in_driver(vb);
-
dprintk(3, "Streamon successful\n");
return 0;
}
EXPORT_SYMBOL_GPL(vb2_streamon);
-/**
- * __vb2_queue_cancel() - cancel and stop (pause) streaming
- *
- * Removes all queued buffers from driver's queue and all buffers queued by
- * userspace from videobuf's queue. Returns to state after reqbufs.
- */
-static void __vb2_queue_cancel(struct vb2_queue *q)
-{
- unsigned int i;
-
- /*
- * Tell driver to stop all transactions and release all queued
- * buffers.
- */
- if (q->streaming)
- call_qop(q, stop_streaming, q);
- q->streaming = 0;
-
- /*
- * Remove all buffers from videobuf's list...
- */
- INIT_LIST_HEAD(&q->queued_list);
- /*
- * ...and done list; userspace will not receive any buffers it
- * has not already dequeued before initiating cancel.
- */
- INIT_LIST_HEAD(&q->done_list);
- atomic_set(&q->queued_count, 0);
- wake_up_all(&q->done_wq);
-
- /*
- * Reinitialize all buffers for next use.
- */
- for (i = 0; i < q->num_buffers; ++i)
- q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
-}
/**
* vb2_streamoff - stop streaming
@@ -1336,14 +1562,42 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
if (ret)
return ret;
- vb_plane->mapped = 1;
- vb->num_planes_mapped++;
-
dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
return 0;
}
EXPORT_SYMBOL_GPL(vb2_mmap);
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ unsigned long off = pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+ int ret;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace.
+ */
+ ret = __find_plane_by_offset(q, off, &buffer, &plane);
+ if (ret)
+ return ret;
+
+ vb = q->bufs[buffer];
+
+ return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
static int __vb2_init_fileio(struct vb2_queue *q, int read);
static int __vb2_cleanup_fileio(struct vb2_queue *q);
@@ -1461,7 +1715,7 @@ void vb2_queue_release(struct vb2_queue *q)
{
__vb2_cleanup_fileio(q);
__vb2_queue_cancel(q);
- __vb2_queue_free(q);
+ __vb2_queue_free(q, q->num_buffers);
}
EXPORT_SYMBOL_GPL(vb2_queue_release);
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
index a790a5f8c06f..f17ad98fcc5f 100644
--- a/drivers/media/video/videobuf2-dma-contig.c
+++ b/drivers/media/video/videobuf2-dma-contig.c
@@ -24,7 +24,7 @@ struct vb2_dc_conf {
struct vb2_dc_buf {
struct vb2_dc_conf *conf;
void *vaddr;
- dma_addr_t paddr;
+ dma_addr_t dma_addr;
unsigned long size;
struct vm_area_struct *vma;
atomic_t refcount;
@@ -42,7 +42,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
if (!buf)
return ERR_PTR(-ENOMEM);
- buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr,
+ buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
GFP_KERNEL);
if (!buf->vaddr) {
dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
@@ -69,7 +69,7 @@ static void vb2_dma_contig_put(void *buf_priv)
if (atomic_dec_and_test(&buf->refcount)) {
dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
- buf->paddr);
+ buf->dma_addr);
kfree(buf);
}
}
@@ -78,7 +78,7 @@ static void *vb2_dma_contig_cookie(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
- return &buf->paddr;
+ return &buf->dma_addr;
}
static void *vb2_dma_contig_vaddr(void *buf_priv)
@@ -106,7 +106,7 @@ static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
return -EINVAL;
}
- return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+ return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
&vb2_common_vm_ops, &buf->handler);
}
@@ -115,14 +115,14 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
{
struct vb2_dc_buf *buf;
struct vm_area_struct *vma;
- dma_addr_t paddr = 0;
+ dma_addr_t dma_addr = 0;
int ret;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
- ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
+ ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
if (ret) {
printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
vaddr);
@@ -131,7 +131,7 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
}
buf->size = size;
- buf->paddr = paddr;
+ buf->dma_addr = dma_addr;
buf->vma = vma;
return buf;
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
index 065f468faf8f..3bad8b105fea 100644
--- a/drivers/media/video/videobuf2-dma-sg.c
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -75,12 +75,6 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
__func__, buf->sg_desc.num_pages);
-
- if (!buf->vaddr)
- buf->vaddr = vm_map_ram(buf->pages,
- buf->sg_desc.num_pages,
- -1,
- PAGE_KERNEL);
return buf;
fail_pages_alloc:
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
index 569eeb3dfd50..71a7a78c3fc0 100644
--- a/drivers/media/video/videobuf2-memops.c
+++ b/drivers/media/video/videobuf2-memops.c
@@ -68,12 +68,12 @@ void vb2_put_vma(struct vm_area_struct *vma)
if (!vma)
return;
- if (vma->vm_file)
- fput(vma->vm_file);
-
if (vma->vm_ops && vma->vm_ops->close)
vma->vm_ops->close(vma);
+ if (vma->vm_file)
+ fput(vma->vm_file);
+
kfree(vma);
}
EXPORT_SYMBOL_GPL(vb2_put_vma);
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index a848bd2af97f..7d754fbcccbf 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -650,9 +650,9 @@ static void vivi_stop_generating(struct vivi_dev *dev)
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
- unsigned int *nplanes, unsigned long sizes[],
- void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
@@ -766,7 +766,7 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&dev->slock, flags);
}
-static int start_streaming(struct vb2_queue *vq)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
dprintk(dev, 1, "%s\n", __func__);
@@ -852,6 +852,11 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
(f->fmt.pix.width * dev->fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ dev->fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
@@ -885,6 +890,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
(f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
@@ -948,6 +958,14 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
return vb2_streamoff(&dev->vb_vidq, i);
}
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name);
+ return 0;
+}
+
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
@@ -1191,6 +1209,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = vidioc_log_status,
.vidioc_subscribe_event = vidioc_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index ca372eb911d0..e5cad6ff64a1 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -331,7 +331,7 @@ static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pst
if (pstd)
*pstd = std;
if (pstatus)
- *pstatus = status;
+ *pstatus = res;
return 0;
}
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index c492846c1c5a..e78cf94f491e 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -1638,6 +1638,9 @@ static int zr364xx_probe(struct usb_interface *intf,
if (!cam->read_endpoint) {
dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
+ video_device_release(cam->vdev);
+ kfree(cam);
+ cam = NULL;
return -ENOMEM;
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a67adcbd0fa1..f1391c21ef26 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2,23 +2,8 @@
# Multifunction miscellaneous devices
#
-menuconfig MFD_SUPPORT
- bool "Multifunction device drivers"
- depends on HAS_IOMEM
- default y
- help
- Multifunction devices embed several functions (e.g. GPIOs,
- touchscreens, keyboards, current regulators, power management chips,
- etc...) in one single integrated circuit. They usually talk to the
- main CPU through one or more IRQ lines and low speed data busses (SPI,
- I2C, etc..). They appear as one single device to the main system
- through the data bus and the MFD framework allows for sub devices
- (a.k.a. functions) to appear as discrete platform devices.
- MFDs are typically found on embedded platforms.
-
- This option alone does not add any kernel code.
-
-if MFD_SUPPORT
+if HAS_IOMEM
+menu "Multifunction device drivers"
config MFD_CORE
tristate
@@ -390,6 +375,7 @@ config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
depends on I2C
+ select REGMAP_I2C
help
Support for the Wolfson Microelecronics WM8400 PMIC and audio
CODEC. This driver provides common support for accessing
@@ -503,6 +489,7 @@ config MFD_WM8994
config MFD_PCF50633
tristate "Support for NXP PCF50633"
depends on I2C
+ select REGMAP_I2C
help
Say yes here if you have NXP PCF50633 chip on your board.
This core driver provides register access and IRQ handling
@@ -579,6 +566,23 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
+config AB5500_CORE
+ bool "ST-Ericsson AB5500 Mixed Signal Power Management chip"
+ depends on ABX500_CORE && MFD_DB5500_PRCMU
+ select MFD_CORE
+ help
+ Select this option to enable access to AB5500 power management
+ chip. This connects to the db5500 chip via the I2C bus via PRCMU.
+ This chip embeds various other multimedia funtionalities as well.
+
+config AB5500_DEBUG
+ bool "Enable debug info via debugfs"
+ depends on AB5500_CORE && DEBUG_FS
+ default y if DEBUG_FS
+ help
+ Select this option if you want debug information from the AB5500
+ using the debug filesystem, debugfs.
+
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on GENERIC_HARDIRQS && ABX500_CORE
@@ -615,20 +619,6 @@ config AB8500_GPADC
help
AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
-config AB3550_CORE
- bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
- select MFD_CORE
- depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE
- help
- Select this to enable the AB3550 Mixed Signal IC core
- functionality. This connects to a AB3550 on the I2C bus
- and expose a number of symbols needed for dependent devices
- to read and write registers and subscribe to events from
- this multi-functional IC. This is needed to use other features
- of the AB3550 such as battery-backed RTC, charging control,
- LEDs, vibrator, system power and temperature, power management
- and ALSA sound.
-
config MFD_DB8500_PRCMU
bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
depends on UX500_SOC_DB8500
@@ -773,7 +763,17 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
-endif # MFD_SUPPORT
+config MFD_INTEL_MSIC
+ bool "Support for Intel MSIC"
+ depends on INTEL_SCU_IPC
+ select MFD_CORE
+ help
+ Select this option to enable access to Intel MSIC (Avatele
+ Passage) chip. This chip embeds audio, battery, GPIO, etc.
+ devices used in Intel Medfield platforms.
+
+endmenu
+endif
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c58020303d18..b2292eb75242 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -79,7 +79,8 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
-obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
+obj-$(CONFIG_AB5500_CORE) += ab5500-core.o
+obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
@@ -102,3 +103,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 345dc658ef06..02c42015ba51 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -295,7 +295,7 @@ static ssize_t aat2870_reg_write_file(struct file *file,
{
struct aat2870_data *aat2870 = file->private_data;
char buf[32];
- int buf_size;
+ ssize_t buf_size;
char *start = buf;
unsigned long addr, val;
int ret;
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index a20e1c41bed2..4f5725508ac0 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -809,7 +809,7 @@ struct ab_family_id {
char *name;
};
-static const struct ab_family_id ids[] __devinitdata = {
+static const struct ab_family_id ids[] __devinitconst = {
/* AB3100 */
{
.id = 0xc0,
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
deleted file mode 100644
index 56ba1943c91d..000000000000
--- a/drivers/mfd/ab3550-core.c
+++ /dev/null
@@ -1,1380 +0,0 @@
-/*
- * Copyright (C) 2007-2010 ST-Ericsson
- * License terms: GNU General Public License (GPL) version 2
- * Low-level core for exclusive access to the AB3550 IC on the I2C bus
- * and some basic chip-configuration.
- * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
- * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
- * Author: Mattias Wallin <mattias.wallin@stericsson.com>
- * Author: Rickard Andersson <rickard.andersson@stericsson.com>
- */
-
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/random.h>
-#include <linux/workqueue.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#include <linux/mfd/abx500.h>
-#include <linux/list.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/mfd/core.h>
-
-#define AB3550_NAME_STRING "ab3550"
-#define AB3550_ID_FORMAT_STRING "AB3550 %s"
-#define AB3550_NUM_BANKS 2
-#define AB3550_NUM_EVENT_REG 5
-
-/* These are the only registers inside AB3550 used in this main file */
-
-/* Chip ID register */
-#define AB3550_CID_REG 0x20
-
-/* Interrupt event registers */
-#define AB3550_EVENT_BANK 0
-#define AB3550_EVENT_REG 0x22
-
-/* Read/write operation values. */
-#define AB3550_PERM_RD (0x01)
-#define AB3550_PERM_WR (0x02)
-
-/* Read/write permissions. */
-#define AB3550_PERM_RO (AB3550_PERM_RD)
-#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR)
-
-/**
- * struct ab3550
- * @access_mutex: lock out concurrent accesses to the AB registers
- * @i2c_client: I2C client for this chip
- * @chip_name: name of this chip variant
- * @chip_id: 8 bit chip ID for this chip variant
- * @mask_work: a worker for writing to mask registers
- * @event_lock: a lock to protect the event_mask
- * @event_mask: a local copy of the mask event registers
- * @startup_events: a copy of the first reading of the event registers
- * @startup_events_read: whether the first events have been read
- */
-struct ab3550 {
- struct mutex access_mutex;
- struct i2c_client *i2c_client[AB3550_NUM_BANKS];
- char chip_name[32];
- u8 chip_id;
- struct work_struct mask_work;
- spinlock_t event_lock;
- u8 event_mask[AB3550_NUM_EVENT_REG];
- u8 startup_events[AB3550_NUM_EVENT_REG];
- bool startup_events_read;
-#ifdef CONFIG_DEBUG_FS
- unsigned int debug_bank;
- unsigned int debug_address;
-#endif
-};
-
-/**
- * struct ab3550_reg_range
- * @first: the first address of the range
- * @last: the last address of the range
- * @perm: access permissions for the range
- */
-struct ab3550_reg_range {
- u8 first;
- u8 last;
- u8 perm;
-};
-
-/**
- * struct ab3550_reg_ranges
- * @count: the number of ranges in the list
- * @range: the list of register ranges
- */
-struct ab3550_reg_ranges {
- u8 count;
- const struct ab3550_reg_range *range;
-};
-
-/*
- * Permissible register ranges for reading and writing per device and bank.
- *
- * The ranges must be listed in increasing address order, and no overlaps are
- * allowed. It is assumed that write permission implies read permission
- * (i.e. only RO and RW permissions should be used). Ranges with write
- * permission must not be split up.
- */
-
-#define NO_RANGE {.count = 0, .range = NULL,}
-
-static struct
-ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = {
- [AB3550_DEVID_DAC] = {
- NO_RANGE,
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0xb0,
- .last = 0xba,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0xbc,
- .last = 0xc3,
- .perm = AB3550_PERM_RW,
- },
- },
- },
- },
- [AB3550_DEVID_LEDS] = {
- NO_RANGE,
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x5a,
- .last = 0x88,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x8a,
- .last = 0xad,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_POWER] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_REGULATORS] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x69,
- .last = 0xa3,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x14,
- .last = 0x16,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_SIM] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x14,
- .last = 0x17,
- .perm = AB3550_PERM_RW,
- },
- }
-
- },
- },
- [AB3550_DEVID_UART] = {
- NO_RANGE,
- NO_RANGE,
- },
- [AB3550_DEVID_RTC] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0c,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_CHARGER] = {
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x10,
- .last = 0x1a,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- NO_RANGE,
- },
- [AB3550_DEVID_ADC] = {
- NO_RANGE,
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x20,
- .last = 0x56,
- .perm = AB3550_PERM_RW,
- },
-
- }
- },
- },
- [AB3550_DEVID_FUELGAUGE] = {
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x21,
- .last = 0x21,
- .perm = AB3550_PERM_RO,
- },
- }
- },
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- },
- [AB3550_DEVID_VIBRATOR] = {
- NO_RANGE,
- {
- .count = 1,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x10,
- .last = 0x13,
- .perm = AB3550_PERM_RW,
- },
-
- }
- },
- },
- [AB3550_DEVID_CODEC] = {
- {
- .count = 2,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x31,
- .last = 0x63,
- .perm = AB3550_PERM_RW,
- },
- {
- .first = 0x65,
- .last = 0x68,
- .perm = AB3550_PERM_RW,
- },
- }
- },
- NO_RANGE,
- },
-};
-
-static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = {
- [AB3550_DEVID_DAC] = {
- .name = "ab3550-dac",
- .id = AB3550_DEVID_DAC,
- .num_resources = 0,
- },
- [AB3550_DEVID_LEDS] = {
- .name = "ab3550-leds",
- .id = AB3550_DEVID_LEDS,
- },
- [AB3550_DEVID_POWER] = {
- .name = "ab3550-power",
- .id = AB3550_DEVID_POWER,
- },
- [AB3550_DEVID_REGULATORS] = {
- .name = "ab3550-regulators",
- .id = AB3550_DEVID_REGULATORS,
- },
- [AB3550_DEVID_SIM] = {
- .name = "ab3550-sim",
- .id = AB3550_DEVID_SIM,
- },
- [AB3550_DEVID_UART] = {
- .name = "ab3550-uart",
- .id = AB3550_DEVID_UART,
- },
- [AB3550_DEVID_RTC] = {
- .name = "ab3550-rtc",
- .id = AB3550_DEVID_RTC,
- },
- [AB3550_DEVID_CHARGER] = {
- .name = "ab3550-charger",
- .id = AB3550_DEVID_CHARGER,
- },
- [AB3550_DEVID_ADC] = {
- .name = "ab3550-adc",
- .id = AB3550_DEVID_ADC,
- .num_resources = 10,
- .resources = (struct resource[]) {
- {
- .name = "TRIGGER-0",
- .flags = IORESOURCE_IRQ,
- .start = 16,
- .end = 16,
- },
- {
- .name = "TRIGGER-1",
- .flags = IORESOURCE_IRQ,
- .start = 17,
- .end = 17,
- },
- {
- .name = "TRIGGER-2",
- .flags = IORESOURCE_IRQ,
- .start = 18,
- .end = 18,
- },
- {
- .name = "TRIGGER-3",
- .flags = IORESOURCE_IRQ,
- .start = 19,
- .end = 19,
- },
- {
- .name = "TRIGGER-4",
- .flags = IORESOURCE_IRQ,
- .start = 20,
- .end = 20,
- },
- {
- .name = "TRIGGER-5",
- .flags = IORESOURCE_IRQ,
- .start = 21,
- .end = 21,
- },
- {
- .name = "TRIGGER-6",
- .flags = IORESOURCE_IRQ,
- .start = 22,
- .end = 22,
- },
- {
- .name = "TRIGGER-7",
- .flags = IORESOURCE_IRQ,
- .start = 23,
- .end = 23,
- },
- {
- .name = "TRIGGER-VBAT-TXON",
- .flags = IORESOURCE_IRQ,
- .start = 13,
- .end = 13,
- },
- {
- .name = "TRIGGER-VBAT",
- .flags = IORESOURCE_IRQ,
- .start = 12,
- .end = 12,
- },
- },
- },
- [AB3550_DEVID_FUELGAUGE] = {
- .name = "ab3550-fuelgauge",
- .id = AB3550_DEVID_FUELGAUGE,
- },
- [AB3550_DEVID_VIBRATOR] = {
- .name = "ab3550-vibrator",
- .id = AB3550_DEVID_VIBRATOR,
- },
- [AB3550_DEVID_CODEC] = {
- .name = "ab3550-codec",
- .id = AB3550_DEVID_CODEC,
- },
-};
-
-/*
- * I2C transactions with error messages.
- */
-static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data,
- u8 count)
-{
- int err;
-
- err = i2c_master_send(ab->i2c_client[bank], data, count);
- if (err < 0) {
- dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err);
- return err;
- }
- return 0;
-}
-
-static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data,
- u8 count)
-{
- int err;
-
- err = i2c_master_recv(ab->i2c_client[bank], data, count);
- if (err < 0) {
- dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err);
- return err;
- }
- return 0;
-}
-
-/*
- * Functionality for getting/setting register values.
- */
-static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg,
- u8 *value)
-{
- int err;
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- err = ab3550_i2c_master_send(ab, bank, &reg, 1);
- if (!err)
- err = ab3550_i2c_master_recv(ab, bank, value, 1);
-
- mutex_unlock(&ab->access_mutex);
- return err;
-}
-
-static int get_register_page_interruptible(struct ab3550 *ab, u8 bank,
- u8 first_reg, u8 *regvals, u8 numregs)
-{
- int err;
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- err = ab3550_i2c_master_send(ab, bank, &first_reg, 1);
- if (!err)
- err = ab3550_i2c_master_recv(ab, bank, regvals, numregs);
-
- mutex_unlock(&ab->access_mutex);
- return err;
-}
-
-static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank,
- u8 reg, u8 bitmask, u8 bitvalues)
-{
- int err = 0;
-
- if (likely(bitmask)) {
- u8 reg_bits[2] = {reg, 0};
-
- err = mutex_lock_interruptible(&ab->access_mutex);
- if (err)
- return err;
-
- if (bitmask == 0xFF) /* No need to read in this case. */
- reg_bits[1] = bitvalues;
- else { /* Read and modify the register value. */
- u8 bits;
-
- err = ab3550_i2c_master_send(ab, bank, &reg, 1);
- if (err)
- goto unlock_and_return;
- err = ab3550_i2c_master_recv(ab, bank, &bits, 1);
- if (err)
- goto unlock_and_return;
- reg_bits[1] = ((~bitmask & bits) |
- (bitmask & bitvalues));
- }
- /* Write the new value. */
- err = ab3550_i2c_master_send(ab, bank, reg_bits, 2);
-unlock_and_return:
- mutex_unlock(&ab->access_mutex);
- }
- return err;
-}
-
-/*
- * Read/write permission checking functions.
- */
-static bool page_write_allowed(const struct ab3550_reg_ranges *ranges,
- u8 first_reg, u8 last_reg)
-{
- u8 i;
-
- if (last_reg < first_reg)
- return false;
-
- for (i = 0; i < ranges->count; i++) {
- if (first_reg < ranges->range[i].first)
- break;
- if ((last_reg <= ranges->range[i].last) &&
- (ranges->range[i].perm & AB3550_PERM_WR))
- return true;
- }
- return false;
-}
-
-static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
-{
- return page_write_allowed(ranges, reg, reg);
-}
-
-static bool page_read_allowed(const struct ab3550_reg_ranges *ranges,
- u8 first_reg, u8 last_reg)
-{
- u8 i;
-
- if (last_reg < first_reg)
- return false;
- /* Find the range (if it exists in the list) that includes first_reg. */
- for (i = 0; i < ranges->count; i++) {
- if (first_reg < ranges->range[i].first)
- return false;
- if (first_reg <= ranges->range[i].last)
- break;
- }
- /* Make sure that the entire range up to and including last_reg is
- * readable. This may span several of the ranges in the list.
- */
- while ((i < ranges->count) &&
- (ranges->range[i].perm & AB3550_PERM_RD)) {
- if (last_reg <= ranges->range[i].last)
- return true;
- if ((++i >= ranges->count) ||
- (ranges->range[i].first !=
- (ranges->range[i - 1].last + 1))) {
- break;
- }
- }
- return false;
-}
-
-static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
-{
- return page_read_allowed(ranges, reg, reg);
-}
-
-/*
- * The register access functionality.
- */
-static int ab3550_get_chip_id(struct device *dev)
-{
- struct ab3550 *ab = dev_get_drvdata(dev->parent);
- return (int)ab->chip_id;
-}
-
-static int ab3550_mask_and_set_register_interruptible(struct device *dev,
- u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return mask_and_set_register_interruptible(ab, bank, reg,
- bitmask, bitvalues);
-}
-
-static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 value)
-{
- return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
- value);
-}
-
-static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 *value)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return get_register_interruptible(ab, bank, reg, value);
-}
-
-static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
- u8 first_reg, u8 *regvals, u8 numregs)
-{
- struct ab3550 *ab;
- struct platform_device *pdev = to_platform_device(dev);
-
- if ((AB3550_NUM_BANKS <= bank) ||
- !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank],
- first_reg, (first_reg + numregs - 1)))
- return -EINVAL;
-
- ab = dev_get_drvdata(dev->parent);
- return get_register_page_interruptible(ab, bank, first_reg, regvals,
- numregs);
-}
-
-static int ab3550_event_registers_startup_state_get(struct device *dev,
- u8 *event)
-{
- struct ab3550 *ab;
-
- ab = dev_get_drvdata(dev->parent);
- if (!ab->startup_events_read)
- return -EAGAIN; /* Try again later */
-
- memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG);
- return 0;
-}
-
-static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
-{
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- bool val;
-
- ab = irq_get_chip_data(irq);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq -= plf_data->irq.base;
- val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0);
-
- return val;
-}
-
-static struct abx500_ops ab3550_ops = {
- .get_chip_id = ab3550_get_chip_id,
- .get_register = ab3550_get_register_interruptible,
- .set_register = ab3550_set_register_interruptible,
- .get_register_page = ab3550_get_register_page_interruptible,
- .set_register_page = NULL,
- .mask_and_set_register = ab3550_mask_and_set_register_interruptible,
- .event_registers_startup_state_get =
- ab3550_event_registers_startup_state_get,
- .startup_irq_enabled = ab3550_startup_irq_enabled,
-};
-
-static irqreturn_t ab3550_irq_handler(int irq, void *data)
-{
- struct ab3550 *ab = data;
- int err;
- unsigned int i;
- u8 e[AB3550_NUM_EVENT_REG];
- u8 *events;
- unsigned long flags;
-
- events = (ab->startup_events_read ? e : ab->startup_events);
-
- err = get_register_page_interruptible(ab, AB3550_EVENT_BANK,
- AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG);
- if (err)
- goto err_event_rd;
-
- if (!ab->startup_events_read) {
- dev_info(&ab->i2c_client[0]->dev,
- "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n",
- ab->startup_events[0], ab->startup_events[1],
- ab->startup_events[2], ab->startup_events[3],
- ab->startup_events[4]);
- ab->startup_events_read = true;
- goto out;
- }
-
- /* The two highest bits in event[4] are not used. */
- events[4] &= 0x3f;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
- events[i] &= ~ab->event_mask[i];
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
- u8 bit;
- u8 event_reg;
-
- dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n",
- i, events[i]);
-
- event_reg = events[i];
- for (bit = 0; event_reg; bit++, event_reg /= 2) {
- if (event_reg % 2) {
- unsigned int irq;
- struct ab3550_platform_data *plf_data;
-
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = plf_data->irq.base + (i * 8) + bit;
- handle_nested_irq(irq);
- }
- }
- }
-out:
- return IRQ_HANDLED;
-
-err_event_rd:
- dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n");
- return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = {
- {
- .count = 6,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- },
- {
- .first = 0x10,
- .last = 0x1a,
- },
- {
- .first = 0x1e,
- .last = 0x4f,
- },
- {
- .first = 0x51,
- .last = 0x63,
- },
- {
- .first = 0x65,
- .last = 0xa3,
- },
- {
- .first = 0xa5,
- .last = 0xa8,
- },
- }
- },
- {
- .count = 8,
- .range = (struct ab3550_reg_range[]) {
- {
- .first = 0x00,
- .last = 0x0e,
- },
- {
- .first = 0x10,
- .last = 0x17,
- },
- {
- .first = 0x1a,
- .last = 0x1c,
- },
- {
- .first = 0x20,
- .last = 0x56,
- },
- {
- .first = 0x5a,
- .last = 0x88,
- },
- {
- .first = 0x8a,
- .last = 0xad,
- },
- {
- .first = 0xb0,
- .last = 0xba,
- },
- {
- .first = 0xbc,
- .last = 0xc3,
- },
- }
- },
-};
-
-static int ab3550_registers_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
- int bank;
-
- seq_printf(s, AB3550_NAME_STRING " register values:\n");
-
- for (bank = 0; bank < AB3550_NUM_BANKS; bank++) {
- unsigned int i;
-
- seq_printf(s, " bank %d:\n", bank);
- for (i = 0; i < debug_ranges[bank].count; i++) {
- u8 reg;
-
- for (reg = debug_ranges[bank].range[i].first;
- reg <= debug_ranges[bank].range[i].last;
- reg++) {
- u8 value;
-
- get_register_interruptible(ab, bank, reg,
- &value);
- seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank,
- reg, value);
- }
- }
- }
- return 0;
-}
-
-static int ab3550_registers_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_registers_print, inode->i_private);
-}
-
-static const struct file_operations ab3550_registers_fops = {
- .open = ab3550_registers_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static int ab3550_bank_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
-
- seq_printf(s, "%d\n", ab->debug_bank);
- return 0;
-}
-
-static int ab3550_bank_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_bank_print, inode->i_private);
-}
-
-static ssize_t ab3550_bank_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- unsigned long user_bank;
- int err;
-
- /* Get userspace string and assure termination */
- err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
- if (err)
- return err;
-
- if (user_bank >= AB3550_NUM_BANKS) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > number of banks\n");
- return -EINVAL;
- }
-
- ab->debug_bank = user_bank;
-
- return count;
-}
-
-static int ab3550_address_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
-
- seq_printf(s, "0x%02X\n", ab->debug_address);
- return 0;
-}
-
-static int ab3550_address_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_address_print, inode->i_private);
-}
-
-static ssize_t ab3550_address_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- unsigned long user_address;
- int err;
-
- /* Get userspace string and assure termination */
- err = kstrtoul_from_user(user_buf, count, 0, &user_address);
- if (err)
- return err;
-
- if (user_address > 0xff) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > 0xff\n");
- return -EINVAL;
- }
- ab->debug_address = user_address;
- return count;
-}
-
-static int ab3550_val_print(struct seq_file *s, void *p)
-{
- struct ab3550 *ab = s->private;
- int err;
- u8 regvalue;
-
- err = get_register_interruptible(ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, &regvalue);
- if (err)
- return -EINVAL;
- seq_printf(s, "0x%02X\n", regvalue);
-
- return 0;
-}
-
-static int ab3550_val_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ab3550_val_print, inode->i_private);
-}
-
-static ssize_t ab3550_val_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- unsigned long user_val;
- int err;
- u8 regvalue;
-
- /* Get userspace string and assure termination */
- err = kstrtoul_from_user(user_buf, count, 0, &user_val);
- if (err)
- return err;
-
- if (user_val > 0xff) {
- dev_err(&ab->i2c_client[0]->dev,
- "debugfs error input > 0xff\n");
- return -EINVAL;
- }
- err = mask_and_set_register_interruptible(
- ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, 0xFF, (u8)user_val);
- if (err)
- return -EINVAL;
-
- get_register_interruptible(ab, (u8)ab->debug_bank,
- (u8)ab->debug_address, &regvalue);
- if (err)
- return -EINVAL;
-
- return count;
-}
-
-static const struct file_operations ab3550_bank_fops = {
- .open = ab3550_bank_open,
- .write = ab3550_bank_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations ab3550_address_fops = {
- .open = ab3550_address_open,
- .write = ab3550_address_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations ab3550_val_fops = {
- .open = ab3550_val_open,
- .write = ab3550_val_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static struct dentry *ab3550_dir;
-static struct dentry *ab3550_reg_file;
-static struct dentry *ab3550_bank_file;
-static struct dentry *ab3550_address_file;
-static struct dentry *ab3550_val_file;
-
-static inline void ab3550_setup_debugfs(struct ab3550 *ab)
-{
- ab->debug_bank = 0;
- ab->debug_address = 0x00;
-
- ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL);
- if (!ab3550_dir)
- goto exit_no_debugfs;
-
- ab3550_reg_file = debugfs_create_file("all-registers",
- S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops);
- if (!ab3550_reg_file)
- goto exit_destroy_dir;
-
- ab3550_bank_file = debugfs_create_file("register-bank",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops);
- if (!ab3550_bank_file)
- goto exit_destroy_reg;
-
- ab3550_address_file = debugfs_create_file("register-address",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops);
- if (!ab3550_address_file)
- goto exit_destroy_bank;
-
- ab3550_val_file = debugfs_create_file("register-value",
- (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops);
- if (!ab3550_val_file)
- goto exit_destroy_address;
-
- return;
-
-exit_destroy_address:
- debugfs_remove(ab3550_address_file);
-exit_destroy_bank:
- debugfs_remove(ab3550_bank_file);
-exit_destroy_reg:
- debugfs_remove(ab3550_reg_file);
-exit_destroy_dir:
- debugfs_remove(ab3550_dir);
-exit_no_debugfs:
- dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n");
- return;
-}
-
-static inline void ab3550_remove_debugfs(void)
-{
- debugfs_remove(ab3550_val_file);
- debugfs_remove(ab3550_address_file);
- debugfs_remove(ab3550_bank_file);
- debugfs_remove(ab3550_reg_file);
- debugfs_remove(ab3550_dir);
-}
-
-#else /* !CONFIG_DEBUG_FS */
-static inline void ab3550_setup_debugfs(struct ab3550 *ab)
-{
-}
-static inline void ab3550_remove_debugfs(void)
-{
-}
-#endif
-
-/*
- * Basic set-up, datastructure creation/destruction and I2C interface.
- * This sets up a default config in the AB3550 chip so that it
- * will work as expected.
- */
-static int __init ab3550_setup(struct ab3550 *ab)
-{
- int err = 0;
- int i;
- struct ab3550_platform_data *plf_data;
- struct abx500_init_settings *settings;
-
- plf_data = ab->i2c_client[0]->dev.platform_data;
- settings = plf_data->init_settings;
-
- for (i = 0; i < plf_data->init_settings_sz; i++) {
- err = mask_and_set_register_interruptible(ab,
- settings[i].bank,
- settings[i].reg,
- 0xFF, settings[i].setting);
- if (err)
- goto exit_no_setup;
-
- /* If event mask register update the event mask in ab3550 */
- if ((settings[i].bank == 0) &&
- (AB3550_IMR1 <= settings[i].reg) &&
- (settings[i].reg <= AB3550_IMR5)) {
- ab->event_mask[settings[i].reg - AB3550_IMR1] =
- settings[i].setting;
- }
- }
-exit_no_setup:
- return err;
-}
-
-static void ab3550_mask_work(struct work_struct *work)
-{
- struct ab3550 *ab = container_of(work, struct ab3550, mask_work);
- int i;
- unsigned long flags;
- u8 mask[AB3550_NUM_EVENT_REG];
-
- spin_lock_irqsave(&ab->event_lock, flags);
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
- mask[i] = ab->event_mask[i];
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
- int err;
-
- err = mask_and_set_register_interruptible(ab, 0,
- (AB3550_IMR1 + i), ~0, mask[i]);
- if (err)
- dev_err(&ab->i2c_client[0]->dev,
- "ab3550_mask_work failed 0x%x,0x%x\n",
- (AB3550_IMR1 + i), mask[i]);
- }
-}
-
-static void ab3550_mask(struct irq_data *data)
-{
- unsigned long flags;
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- int irq;
-
- ab = irq_data_get_irq_chip_data(data);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = data->irq - plf_data->irq.base;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- ab->event_mask[irq / 8] |= BIT(irq % 8);
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- schedule_work(&ab->mask_work);
-}
-
-static void ab3550_unmask(struct irq_data *data)
-{
- unsigned long flags;
- struct ab3550 *ab;
- struct ab3550_platform_data *plf_data;
- int irq;
-
- ab = irq_data_get_irq_chip_data(data);
- plf_data = ab->i2c_client[0]->dev.platform_data;
- irq = data->irq - plf_data->irq.base;
-
- spin_lock_irqsave(&ab->event_lock, flags);
- ab->event_mask[irq / 8] &= ~BIT(irq % 8);
- spin_unlock_irqrestore(&ab->event_lock, flags);
-
- schedule_work(&ab->mask_work);
-}
-
-static void noop(struct irq_data *data)
-{
-}
-
-static struct irq_chip ab3550_irq_chip = {
- .name = "ab3550-core", /* Keep the same name as the request */
- .irq_disable = ab3550_mask, /* No default to mask in chip.c */
- .irq_ack = noop,
- .irq_mask = ab3550_mask,
- .irq_unmask = ab3550_unmask,
-};
-
-struct ab_family_id {
- u8 id;
- char *name;
-};
-
-static const struct ab_family_id ids[] __initdata = {
- /* AB3550 */
- {
- .id = AB3550_P1A,
- .name = "P1A"
- },
- /* Terminator */
- {
- .id = 0x00,
- }
-};
-
-static int __init ab3550_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct ab3550 *ab;
- struct ab3550_platform_data *ab3550_plf_data =
- client->dev.platform_data;
- int err;
- int i;
- int num_i2c_clients = 0;
-
- ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL);
- if (!ab) {
- dev_err(&client->dev,
- "could not allocate " AB3550_NAME_STRING " device\n");
- return -ENOMEM;
- }
-
- /* Initialize data structure */
- mutex_init(&ab->access_mutex);
- spin_lock_init(&ab->event_lock);
- ab->i2c_client[0] = client;
-
- i2c_set_clientdata(client, ab);
-
- /* Read chip ID register */
- err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id);
- if (err) {
- dev_err(&client->dev, "could not communicate with the analog "
- "baseband chip\n");
- goto exit_no_detect;
- }
-
- for (i = 0; ids[i].id != 0x0; i++) {
- if (ids[i].id == ab->chip_id) {
- snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
- AB3550_ID_FORMAT_STRING, ids[i].name);
- break;
- }
- }
-
- if (ids[i].id == 0x0) {
- dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
- ab->chip_id);
- dev_err(&client->dev, "driver not started!\n");
- goto exit_no_detect;
- }
-
- dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
-
- /* Attach other dummy I2C clients. */
- while (++num_i2c_clients < AB3550_NUM_BANKS) {
- ab->i2c_client[num_i2c_clients] =
- i2c_new_dummy(client->adapter,
- (client->addr + num_i2c_clients));
- if (!ab->i2c_client[num_i2c_clients]) {
- err = -ENOMEM;
- goto exit_no_dummy_client;
- }
- strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name,
- sizeof(ab->i2c_client[num_i2c_clients]->name));
- }
-
- err = ab3550_setup(ab);
- if (err)
- goto exit_no_setup;
-
- INIT_WORK(&ab->mask_work, ab3550_mask_work);
-
- for (i = 0; i < ab3550_plf_data->irq.count; i++) {
- unsigned int irq;
-
- irq = ab3550_plf_data->irq.base + i;
- irq_set_chip_data(irq, ab);
- irq_set_chip_and_handler(irq, &ab3550_irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- irq_set_noprobe(irq);
-#endif
- }
-
- err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler,
- IRQF_ONESHOT, "ab3550-core", ab);
- /* This real unpredictable IRQ is of course sampled for entropy */
- rand_initialize_irq(client->irq);
-
- if (err)
- goto exit_no_irq;
-
- err = abx500_register_ops(&client->dev, &ab3550_ops);
- if (err)
- goto exit_no_ops;
-
- /* Set up and register the platform devices. */
- for (i = 0; i < AB3550_NUM_DEVICES; i++) {
- ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i];
- ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i];
- }
-
- err = mfd_add_devices(&client->dev, 0, ab3550_devs,
- ARRAY_SIZE(ab3550_devs), NULL,
- ab3550_plf_data->irq.base);
-
- ab3550_setup_debugfs(ab);
-
- return 0;
-
-exit_no_ops:
-exit_no_irq:
-exit_no_setup:
-exit_no_dummy_client:
- /* Unregister the dummy i2c clients. */
- while (--num_i2c_clients)
- i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
-exit_no_detect:
- kfree(ab);
- return err;
-}
-
-static int __exit ab3550_remove(struct i2c_client *client)
-{
- struct ab3550 *ab = i2c_get_clientdata(client);
- int num_i2c_clients = AB3550_NUM_BANKS;
-
- mfd_remove_devices(&client->dev);
- ab3550_remove_debugfs();
-
- while (--num_i2c_clients)
- i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
-
- /*
- * At this point, all subscribers should have unregistered
- * their notifiers so deactivate IRQ
- */
- free_irq(client->irq, ab);
- kfree(ab);
- return 0;
-}
-
-static const struct i2c_device_id ab3550_id[] = {
- {AB3550_NAME_STRING, 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, ab3550_id);
-
-static struct i2c_driver ab3550_driver = {
- .driver = {
- .name = AB3550_NAME_STRING,
- .owner = THIS_MODULE,
- },
- .id_table = ab3550_id,
- .probe = ab3550_probe,
- .remove = __exit_p(ab3550_remove),
-};
-
-static int __init ab3550_i2c_init(void)
-{
- return i2c_add_driver(&ab3550_driver);
-}
-
-static void __exit ab3550_i2c_exit(void)
-{
- i2c_del_driver(&ab3550_driver);
-}
-
-subsys_initcall(ab3550_i2c_init);
-module_exit(ab3550_i2c_exit);
-
-MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
-MODULE_DESCRIPTION("AB3550 core driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c
new file mode 100644
index 000000000000..4175544b491b
--- /dev/null
+++ b/drivers/mfd/ab5500-core.c
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) 2007-2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB5500 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ * Author: Bibek Basu <bibek.basu@stericsson.com>
+ *
+ * TODO: Event handling with irq_chip. Waiting for PRCMU fw support.
+ */
+
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/mfd/ab5500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/version.h>
+#include <linux/mfd/db5500-prcmu.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+#define AB5500_NUM_EVENT_REG 23
+#define AB5500_IT_LATCH0_REG 0x40
+#define AB5500_IT_MASK0_REG 0x60
+
+/*
+ * Permissible register ranges for reading and writing per device and bank.
+ *
+ * The ranges must be listed in increasing address order, and no overlaps are
+ * allowed. It is assumed that write permission implies read permission
+ * (i.e. only RO and RW permissions should be used). Ranges with write
+ * permission must not be split up.
+ */
+
+#define NO_RANGE {.count = 0, .range = NULL,}
+static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_USB] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_ADC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_LEDS] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .nbanks = 2,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_SIM] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CODEC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_POWER] = {
+ .nbanks = 2,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x30,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ {
+ .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+};
+
+#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit))
+
+/* I appologize for the resource names beeing a mix of upper case
+ * and lower case but I want them to be exact as the documentation */
+static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_LEDS] = {
+ .name = "ab5500-leds",
+ .id = AB5500_DEVID_LEDS,
+ },
+ [AB5500_DEVID_POWER] = {
+ .name = "ab5500-power",
+ .id = AB5500_DEVID_POWER,
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .name = "ab5500-regulator",
+ .id = AB5500_DEVID_REGULATORS,
+ },
+ [AB5500_DEVID_SIM] = {
+ .name = "ab5500-sim",
+ .id = AB5500_DEVID_SIM,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "SIMOFF",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 0), /*rising*/
+ .end = AB5500_IRQ(2, 1), /*falling*/
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .name = "ab5500-rtc",
+ .id = AB5500_DEVID_RTC,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "RTC_Alarm",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 7),
+ .end = AB5500_IRQ(1, 7),
+ }
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .name = "ab5500-charger",
+ .id = AB5500_DEVID_CHARGER,
+ },
+ [AB5500_DEVID_ADC] = {
+ .name = "ab5500-adc",
+ .id = AB5500_DEVID_ADC,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "TRIGGER-0",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 0),
+ .end = AB5500_IRQ(0, 0),
+ },
+ {
+ .name = "TRIGGER-1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 1),
+ .end = AB5500_IRQ(0, 1),
+ },
+ {
+ .name = "TRIGGER-2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 2),
+ .end = AB5500_IRQ(0, 2),
+ },
+ {
+ .name = "TRIGGER-3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 3),
+ .end = AB5500_IRQ(0, 3),
+ },
+ {
+ .name = "TRIGGER-4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 4),
+ .end = AB5500_IRQ(0, 4),
+ },
+ {
+ .name = "TRIGGER-5",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 5),
+ .end = AB5500_IRQ(0, 5),
+ },
+ {
+ .name = "TRIGGER-6",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 6),
+ .end = AB5500_IRQ(0, 6),
+ },
+ {
+ .name = "TRIGGER-7",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 7),
+ .end = AB5500_IRQ(0, 7),
+ },
+ {
+ .name = "TRIGGER-VBAT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 8),
+ .end = AB5500_IRQ(0, 8),
+ },
+ {
+ .name = "TRIGGER-VBAT-TXON",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 9),
+ .end = AB5500_IRQ(0, 9),
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .name = "ab5500-fuelgauge",
+ .id = AB5500_DEVID_FUELGAUGE,
+ .num_resources = 6,
+ .resources = (struct resource[]) {
+ {
+ .name = "Batt_attach",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 5),
+ .end = AB5500_IRQ(7, 5),
+ },
+ {
+ .name = "Batt_removal",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 6),
+ .end = AB5500_IRQ(7, 6),
+ },
+ {
+ .name = "UART_framing",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 7),
+ .end = AB5500_IRQ(7, 7),
+ },
+ {
+ .name = "UART_overrun",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 0),
+ .end = AB5500_IRQ(8, 0),
+ },
+ {
+ .name = "UART_Rdy_RX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 1),
+ .end = AB5500_IRQ(8, 1),
+ },
+ {
+ .name = "UART_Rdy_TX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 2),
+ .end = AB5500_IRQ(8, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .name = "ab5500-vibrator",
+ .id = AB5500_DEVID_VIBRATOR,
+ },
+ [AB5500_DEVID_CODEC] = {
+ .name = "ab5500-codec",
+ .id = AB5500_DEVID_CODEC,
+ .num_resources = 3,
+ .resources = (struct resource[]) {
+ {
+ .name = "audio_spkr1_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 5),
+ },
+ {
+ .name = "audio_plllocked",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 6),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "audio_spkr2_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 4),
+ .end = AB5500_IRQ(17, 4),
+ },
+ },
+ },
+ [AB5500_DEVID_USB] = {
+ .name = "ab5500-usb",
+ .id = AB5500_DEVID_USB,
+ .num_resources = 36,
+ .resources = (struct resource[]) {
+ {
+ .name = "Link_Update",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 1),
+ .end = AB5500_IRQ(22, 1),
+ },
+ {
+ .name = "DCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 3),
+ .end = AB5500_IRQ(8, 4),
+ },
+ {
+ .name = "VBUS_R",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 5),
+ .end = AB5500_IRQ(8, 5),
+ },
+ {
+ .name = "VBUS_F",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 6),
+ .end = AB5500_IRQ(8, 6),
+ },
+ {
+ .name = "CHGstate_10_PCVBUSchg",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 7),
+ .end = AB5500_IRQ(8, 7),
+ },
+ {
+ .name = "DCIOreverse_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 0),
+ .end = AB5500_IRQ(9, 0),
+ },
+ {
+ .name = "USBCharDetDone",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 1),
+ .end = AB5500_IRQ(9, 1),
+ },
+ {
+ .name = "DCIO_no_limit",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 2),
+ .end = AB5500_IRQ(9, 2),
+ },
+ {
+ .name = "USB_suspend",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 3),
+ .end = AB5500_IRQ(9, 3),
+ },
+ {
+ .name = "DCIOreverse_fwdcurrent",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 4),
+ .end = AB5500_IRQ(9, 4),
+ },
+ {
+ .name = "Vbus_Imeasmax_change",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "OVV",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 5),
+ .end = AB5500_IRQ(14, 5),
+ },
+ {
+ .name = "USBcharging_NOTok",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 3),
+ .end = AB5500_IRQ(15, 3),
+ },
+ {
+ .name = "usb_adp_sensoroff",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 6),
+ .end = AB5500_IRQ(15, 6),
+ },
+ {
+ .name = "usb_adp_probeplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 7),
+ .end = AB5500_IRQ(15, 7),
+ },
+ {
+ .name = "usb_adp_sinkerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 0),
+ .end = AB5500_IRQ(16, 6),
+ },
+ {
+ .name = "usb_adp_sourceerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 1),
+ .end = AB5500_IRQ(16, 1),
+ },
+ {
+ .name = "usb_idgnd_r",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 2),
+ .end = AB5500_IRQ(16, 2),
+ },
+ {
+ .name = "usb_idgnd_f",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 3),
+ .end = AB5500_IRQ(16, 3),
+ },
+ {
+ .name = "usb_iddetR1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 4),
+ .end = AB5500_IRQ(16, 5),
+ },
+ {
+ .name = "usb_iddetR2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 6),
+ .end = AB5500_IRQ(16, 7),
+ },
+ {
+ .name = "usb_iddetR3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 0),
+ .end = AB5500_IRQ(17, 1),
+ },
+ {
+ .name = "usb_iddetR4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 2),
+ .end = AB5500_IRQ(17, 3),
+ },
+ {
+ .name = "CharTempWindowOk",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 7),
+ .end = AB5500_IRQ(18, 0),
+ },
+ {
+ .name = "USB_SprDetect",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 1),
+ .end = AB5500_IRQ(18, 1),
+ },
+ {
+ .name = "usb_adp_probe_unplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 2),
+ .end = AB5500_IRQ(18, 2),
+ },
+ {
+ .name = "VBUSChDrop",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 3),
+ .end = AB5500_IRQ(18, 4),
+ },
+ {
+ .name = "dcio_char_rec_done",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 5),
+ .end = AB5500_IRQ(18, 5),
+ },
+ {
+ .name = "Charging_stopped_by_temp",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 6),
+ .end = AB5500_IRQ(18, 6),
+ },
+ {
+ .name = "CHGstate_11_SafeModeVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 1),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_12_comletedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 2),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_13_completedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 3),
+ .end = AB5500_IRQ(21, 3),
+ },
+ {
+ .name = "CHGstate_14_FullChgDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 4),
+ .end = AB5500_IRQ(21, 4),
+ },
+ {
+ .name = "CHGstate_15_SafeModeDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 5),
+ .end = AB5500_IRQ(21, 5),
+ },
+ {
+ .name = "CHGstate_16_OFFsuspendDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 6),
+ .end = AB5500_IRQ(21, 6),
+ },
+ {
+ .name = "CHGstate_17_completedDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 7),
+ .end = AB5500_IRQ(21, 7),
+ },
+ },
+ },
+ [AB5500_DEVID_OTP] = {
+ .name = "ab5500-otp",
+ .id = AB5500_DEVID_OTP,
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .name = "ab5500-video",
+ .id = AB5500_DEVID_VIDEO,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "plugTVdet",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 2),
+ .end = AB5500_IRQ(22, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_DBIECI] = {
+ .name = "ab5500-dbieci",
+ .id = AB5500_DEVID_DBIECI,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "COLL",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 0),
+ .end = AB5500_IRQ(14, 0),
+ },
+ {
+ .name = "RESERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 1),
+ .end = AB5500_IRQ(14, 1),
+ },
+ {
+ .name = "FRAERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 2),
+ .end = AB5500_IRQ(14, 2),
+ },
+ {
+ .name = "COMERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 3),
+ .end = AB5500_IRQ(14, 3),
+ },
+ {
+ .name = "BSI_indicator",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 4),
+ .end = AB5500_IRQ(14, 4),
+ },
+ {
+ .name = "SPDSET",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 6),
+ .end = AB5500_IRQ(14, 6),
+ },
+ {
+ .name = "DSENT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 7),
+ .end = AB5500_IRQ(14, 7),
+ },
+ {
+ .name = "DREC",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 0),
+ .end = AB5500_IRQ(15, 0),
+ },
+ {
+ .name = "ACCINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 1),
+ .end = AB5500_IRQ(15, 1),
+ },
+ {
+ .name = "NOPINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 2),
+ .end = AB5500_IRQ(15, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_ONSWA] = {
+ .name = "ab5500-onswa",
+ .id = AB5500_DEVID_ONSWA,
+ .num_resources = 2,
+ .resources = (struct resource[]) {
+ {
+ .name = "ONSWAn_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 3),
+ .end = AB5500_IRQ(1, 3),
+ },
+ {
+ .name = "ONSWAn_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 4),
+ .end = AB5500_IRQ(1, 4),
+ },
+ },
+ },
+};
+
+/*
+ * Functionality for getting/setting register values.
+ */
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab,
+ u8 bank, u8 reg,
+ u8 *value)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+static int get_register_page_interruptible(struct ab5500 *ab, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ while (numregs) {
+ /* The hardware limit for get page is 4 */
+ u8 curnum = min_t(u8, numregs, 4u);
+
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ first_reg, regvals, curnum);
+ if (err)
+ goto out;
+
+ numregs -= curnum;
+ first_reg += curnum;
+ regvals += curnum;
+ }
+
+out:
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
+{
+ int err = 0;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ if (bitmask) {
+ u8 buf;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ if (bitmask == 0xFF) /* No need to read in this case. */
+ buf = bitvalues;
+ else { /* Read and modify the register value. */
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ reg, &buf, 1);
+ if (err)
+ return err;
+
+ buf = ((~bitmask & buf) | (bitmask & bitvalues));
+ }
+ /* Write the new value. */
+ err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg,
+ &buf, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ }
+ return err;
+}
+
+static int
+set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value)
+{
+ return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+ 0xff, value);
+}
+
+/*
+ * Read/write permission checking functions.
+ */
+static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank)
+{
+ u8 i;
+
+ if (devid < AB5500_NUM_DEVICES) {
+ for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) {
+ if (ab5500_bank_ranges[devid].bank[i].bankid == bank)
+ return &ab5500_bank_ranges[devid].bank[i];
+ }
+ }
+ return NULL;
+}
+
+static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i; /* range loop index */
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ break;
+ if ((last_reg <= bankref->range[i].last) &&
+ (bankref->range[i].perm & AB5500_PERM_WR))
+ return true;
+ }
+ return false;
+}
+
+static bool reg_write_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_write_allowed(devid, bank, reg, reg);
+}
+
+static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i;
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+
+ /* Find the range (if it exists in the list) that includes first_reg. */
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ return false;
+ if (first_reg <= bankref->range[i].last)
+ break;
+ }
+ /* Make sure that the entire range up to and including last_reg is
+ * readable. This may span several of the ranges in the list.
+ */
+ while ((i < bankref->nranges) &&
+ (bankref->range[i].perm & AB5500_PERM_RD)) {
+ if (last_reg <= bankref->range[i].last)
+ return true;
+ if ((++i >= bankref->nranges) ||
+ (bankref->range[i].first !=
+ (bankref->range[i - 1].last + 1))) {
+ break;
+ }
+ }
+ return false;
+}
+
+static bool reg_read_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_read_allowed(devid, bank, reg, reg);
+}
+
+
+/*
+ * The exported register access functionality.
+ */
+static int ab5500_get_chip_id(struct device *dev)
+{
+ struct ab5500 *ab = dev_get_drvdata(dev->parent);
+
+ return (int)ab->chip_id;
+}
+
+static int ab5500_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_write_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+ bitmask, bitvalues);
+}
+
+static int ab5500_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
+{
+ return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
+ value);
+}
+
+static int ab5500_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_read_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return ab5500_get_register_interruptible_raw(ab, bank, reg, value);
+}
+
+static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !page_read_allowed(pdev->id, bank,
+ first_reg, (first_reg + numregs - 1)))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return get_register_page_interruptible(ab, bank, first_reg, regvals,
+ numregs);
+}
+
+static int
+ab5500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+ struct ab5500 *ab;
+
+ ab = dev_get_drvdata(dev->parent);
+ if (!ab->startup_events_read)
+ return -EAGAIN; /* Try again later */
+
+ memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG);
+ return 0;
+}
+
+static struct abx500_ops ab5500_ops = {
+ .get_chip_id = ab5500_get_chip_id,
+ .get_register = ab5500_get_register_interruptible,
+ .set_register = ab5500_set_register_interruptible,
+ .get_register_page = ab5500_get_register_page_interruptible,
+ .set_register_page = NULL,
+ .mask_and_set_register = ab5500_mask_and_set_register_interruptible,
+ .event_registers_startup_state_get =
+ ab5500_event_registers_startup_state_get,
+ .startup_irq_enabled = NULL,
+};
+
+/*
+ * ab5500_setup : Basic set-up, datastructure creation/destruction
+ * and I2C interface.This sets up a default config
+ * in the AB5500 chip so that it will work as expected.
+ * @ab : Pointer to ab5500 structure
+ * @settings : Pointer to struct abx500_init_settings
+ * @size : Size of init data
+ */
+static int __init ab5500_setup(struct ab5500 *ab,
+ struct abx500_init_settings *settings, unsigned int size)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ err = ab5500_mask_and_set_register_interruptible_raw(ab,
+ settings[i].bank,
+ settings[i].reg,
+ 0xFF, settings[i].setting);
+ if (err)
+ goto exit_no_setup;
+
+ /* If event mask register update the event mask in ab5500 */
+ if ((settings[i].bank == AB5500_BANK_IT) &&
+ (AB5500_MASK_BASE <= settings[i].reg) &&
+ (settings[i].reg <= AB5500_MASK_END)) {
+ ab->mask[settings[i].reg - AB5500_MASK_BASE] =
+ settings[i].setting;
+ }
+ }
+exit_no_setup:
+ return err;
+}
+
+struct ab_family_id {
+ u8 id;
+ char *name;
+};
+
+static const struct ab_family_id ids[] __initdata = {
+ /* AB5500 */
+ {
+ .id = AB5500_1_0,
+ .name = "1.0"
+ },
+ {
+ .id = AB5500_1_1,
+ .name = "1.1"
+ },
+ /* Terminator */
+ {
+ .id = 0x00,
+ }
+};
+
+static int __init ab5500_probe(struct platform_device *pdev)
+{
+ struct ab5500 *ab;
+ struct ab5500_platform_data *ab5500_plf_data =
+ pdev->dev.platform_data;
+ int err;
+ int i;
+
+ ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL);
+ if (!ab) {
+ dev_err(&pdev->dev,
+ "could not allocate ab5500 device\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize data structure */
+ mutex_init(&ab->access_mutex);
+ mutex_init(&ab->irq_lock);
+ ab->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, ab);
+
+ /* Read chip ID register */
+ err = ab5500_get_register_interruptible_raw(ab,
+ AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ AB5500_CHIP_ID, &ab->chip_id);
+ if (err) {
+ dev_err(&pdev->dev, "could not communicate with the analog "
+ "baseband chip\n");
+ goto exit_no_detect;
+ }
+
+ for (i = 0; ids[i].id != 0x0; i++) {
+ if (ids[i].id == ab->chip_id) {
+ snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
+ "AB5500 %s", ids[i].name);
+ break;
+ }
+ }
+ if (ids[i].id == 0x0) {
+ dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n",
+ ab->chip_id);
+ dev_err(&pdev->dev, "driver not started!\n");
+ goto exit_no_detect;
+ }
+
+ /* Clear and mask all interrupts */
+ for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) {
+ u8 latchreg = AB5500_IT_LATCH0_REG + i;
+ u8 maskreg = AB5500_IT_MASK0_REG + i;
+ u8 val;
+
+ ab5500_get_register_interruptible_raw(ab, AB5500_BANK_IT,
+ latchreg, &val);
+ set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff);
+ ab->mask[i] = ab->oldmask[i] = 0xff;
+ }
+
+ err = abx500_register_ops(&pdev->dev, &ab5500_ops);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_register ops error\n");
+ goto exit_no_detect;
+ }
+
+ /* Set up and register the platform devices. */
+ for (i = 0; i < AB5500_NUM_DEVICES; i++) {
+ ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i];
+ ab5500_devs[i].pdata_size =
+ sizeof(ab5500_plf_data->dev_data[i]);
+ }
+
+ err = mfd_add_devices(&pdev->dev, 0, ab5500_devs,
+ ARRAY_SIZE(ab5500_devs), NULL,
+ ab5500_plf_data->irq.base);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_mfd_add_device error\n");
+ goto exit_no_detect;
+ }
+
+ err = ab5500_setup(ab, ab5500_plf_data->init_settings,
+ ab5500_plf_data->init_settings_sz);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_setup error\n");
+ goto exit_no_detect;
+ }
+
+ ab5500_setup_debugfs(ab);
+
+ dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
+ return 0;
+
+exit_no_detect:
+ kfree(ab);
+ return err;
+}
+
+static int __exit ab5500_remove(struct platform_device *pdev)
+{
+ struct ab5500 *ab = platform_get_drvdata(pdev);
+
+ ab5500_remove_debugfs();
+ mfd_remove_devices(&pdev->dev);
+ kfree(ab);
+ return 0;
+}
+
+static struct platform_driver ab5500_driver = {
+ .driver = {
+ .name = "ab5500-core",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ab5500_remove),
+};
+
+static int __init ab5500_core_init(void)
+{
+ return platform_driver_probe(&ab5500_driver, ab5500_probe);
+}
+
+static void __exit ab5500_core_exit(void)
+{
+ platform_driver_unregister(&ab5500_driver);
+}
+
+subsys_initcall(ab5500_core_init);
+module_exit(ab5500_core_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab5500-core.h b/drivers/mfd/ab5500-core.h
new file mode 100644
index 000000000000..63b30b17e4f3
--- /dev/null
+++ b/drivers/mfd/ab5500-core.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Shared definitions and data structures for the AB5500 MFD driver
+ */
+
+/* Read/write operation values. */
+#define AB5500_PERM_RD (0x01)
+#define AB5500_PERM_WR (0x02)
+
+/* Read/write permissions. */
+#define AB5500_PERM_RO (AB5500_PERM_RD)
+#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR)
+
+#define AB5500_MASK_BASE (0x60)
+#define AB5500_MASK_END (0x79)
+#define AB5500_CHIP_ID (0x20)
+
+/**
+ * struct ab5500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab5500_reg_range {
+ u8 first;
+ u8 last;
+ u8 perm;
+};
+
+/**
+ * struct ab5500_i2c_ranges
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_ranges {
+ u8 nranges;
+ u8 bankid;
+ const struct ab5500_reg_range *range;
+};
+
+/**
+ * struct ab5500_i2c_banks
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_banks {
+ u8 nbanks;
+ const struct ab5500_i2c_ranges *bank;
+};
+
+/**
+ * struct ab5500_bank
+ * @slave_addr: I2C slave_addr found in AB5500 specification
+ * @name: Documentation name of the bank. For reference
+ */
+struct ab5500_bank {
+ u8 slave_addr;
+ const char *name;
+};
+
+static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"},
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"},
+ [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"},
+ [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"},
+ [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"},
+ [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"},
+ [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"},
+ [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"},
+ [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"},
+ [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"},
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"},
+ [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"},
+ [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"},
+ [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"},
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"},
+};
+
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg,
+ u8 *value);
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues);
diff --git a/drivers/mfd/ab5500-debugfs.c b/drivers/mfd/ab5500-debugfs.c
new file mode 100644
index 000000000000..6be1fe6b5f9a
--- /dev/null
+++ b/drivers/mfd/ab5500-debugfs.c
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs support for the AB5500 MFD driver
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mfd/ab5500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/uaccess.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_LED] = {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_ADC] = {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_RTC] = {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_STARTUP] = {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_DBI_ECI] = {
+ .bankid = AB5500_BANK_DBI_ECI,
+ .nranges = 3,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x07,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x10,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x13,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_CHG] = {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_USB] = {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_IT] = {
+ .bankid = AB5500_BANK_IT,
+ .nranges = 4,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x20,
+ .last = 0x36,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x40,
+ .last = 0x56,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x60,
+ .last = 0x76,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .nranges = 7,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x12,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x54,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x64,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x74,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .nranges = 13,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0D,
+ .last = 0x0F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1C,
+ .last = 0x1C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1E,
+ .last = 0x1E,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x20,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x25,
+ .last = 0x25,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x28,
+ .last = 0x2A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x33,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x43,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x53,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x63,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x73,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIBRA] = {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_SIM_USBSIM] = {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VDENC] = {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+};
+
+static int ab5500_registers_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ unsigned int i;
+ u8 bank = (u8)ab->debug_bank;
+
+ seq_printf(s, "ab5500 register values:\n");
+ for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
+ seq_printf(s, " bank %u, %s (0x%x):\n", bank,
+ bankinfo[bank].name,
+ bankinfo[bank].slave_addr);
+ for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
+ u8 reg;
+ int err;
+
+ for (reg = ab5500_reg_ranges[bank].range[i].first;
+ reg <= ab5500_reg_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+
+ err = ab5500_get_register_interruptible_raw(ab,
+ bank, reg,
+ &value);
+ if (err < 0) {
+ dev_err(ab->dev, "get_reg failed %d"
+ "bank 0x%x reg 0x%x\n",
+ err, bank, reg);
+ return err;
+ }
+
+ err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ dev_err(ab->dev,
+ "seq_printf overflow\n");
+ /*
+ * Error is not returned here since
+ * the output is wanted in any case
+ */
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ab5500_registers_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab5500_registers_fops = {
+ .open = ab5500_registers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab5500_bank_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "%d\n", ab->debug_bank);
+ return 0;
+}
+
+static int ab5500_bank_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_bank_print, inode->i_private);
+}
+
+static ssize_t ab5500_bank_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_bank;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_bank);
+ if (err)
+ return -EINVAL;
+
+ if (user_bank >= AB5500_NUM_BANKS) {
+ dev_err(ab->dev,
+ "debugfs error input > number of banks\n");
+ return -EINVAL;
+ }
+
+ ab->debug_bank = user_bank;
+
+ return buf_size;
+}
+
+static int ab5500_address_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "0x%02X\n", ab->debug_address);
+ return 0;
+}
+
+static int ab5500_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_address_print, inode->i_private);
+}
+
+static ssize_t ab5500_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_address;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_address);
+ if (err)
+ return -EINVAL;
+ if (user_address > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ ab->debug_address = user_address;
+ return buf_size;
+}
+
+static int ab5500_val_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ int err;
+ u8 regvalue;
+
+ err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err) {
+ dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
+ ", reg 0x%x\n", err, ab->debug_bank,
+ ab->debug_address);
+ return -EINVAL;
+ }
+ seq_printf(s, "0x%02X\n", regvalue);
+
+ return 0;
+}
+
+static int ab5500_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_val_print, inode->i_private);
+}
+
+static ssize_t ab5500_val_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ u8 regvalue;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ err = ab5500_mask_and_set_register_interruptible_raw(
+ ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, 0xFF, (u8)user_val);
+ if (err)
+ return -EINVAL;
+
+ ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err)
+ return -EINVAL;
+
+ return buf_size;
+}
+
+static const struct file_operations ab5500_bank_fops = {
+ .open = ab5500_bank_open,
+ .write = ab5500_bank_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_address_fops = {
+ .open = ab5500_address_open,
+ .write = ab5500_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_val_fops = {
+ .open = ab5500_val_open,
+ .write = ab5500_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab5500_dir;
+static struct dentry *ab5500_reg_file;
+static struct dentry *ab5500_bank_file;
+static struct dentry *ab5500_address_file;
+static struct dentry *ab5500_val_file;
+
+void __init ab5500_setup_debugfs(struct ab5500 *ab)
+{
+ ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
+ ab->debug_address = AB5500_CHIP_ID;
+
+ ab5500_dir = debugfs_create_dir("ab5500", NULL);
+ if (!ab5500_dir)
+ goto exit_no_debugfs;
+
+ ab5500_reg_file = debugfs_create_file("all-bank-registers",
+ S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
+ if (!ab5500_reg_file)
+ goto exit_destroy_dir;
+
+ ab5500_bank_file = debugfs_create_file("register-bank",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
+ if (!ab5500_bank_file)
+ goto exit_destroy_reg;
+
+ ab5500_address_file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
+ if (!ab5500_address_file)
+ goto exit_destroy_bank;
+
+ ab5500_val_file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
+ if (!ab5500_val_file)
+ goto exit_destroy_address;
+
+ return;
+
+exit_destroy_address:
+ debugfs_remove(ab5500_address_file);
+exit_destroy_bank:
+ debugfs_remove(ab5500_bank_file);
+exit_destroy_reg:
+ debugfs_remove(ab5500_reg_file);
+exit_destroy_dir:
+ debugfs_remove(ab5500_dir);
+exit_no_debugfs:
+ dev_err(ab->dev, "failed to create debugfs entries.\n");
+ return;
+}
+
+void __exit ab5500_remove_debugfs(void)
+{
+ debugfs_remove(ab5500_val_file);
+ debugfs_remove(ab5500_address_file);
+ debugfs_remove(ab5500_bank_file);
+ debugfs_remove(ab5500_reg_file);
+ debugfs_remove(ab5500_dir);
+}
diff --git a/drivers/mfd/ab5500-debugfs.h b/drivers/mfd/ab5500-debugfs.h
new file mode 100644
index 000000000000..7330a9b6afa6
--- /dev/null
+++ b/drivers/mfd/ab5500-debugfs.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs interface to the AB5500 core driver
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+void ab5500_setup_debugfs(struct ab5500 *ab);
+void ab5500_remove_debugfs(void);
+
+#else /* !CONFIG_DEBUG_FS */
+
+static inline void ab5500_setup_debugfs(struct ab5500 *ab)
+{
+}
+
+static inline void ab5500_remove_debugfs(void)
+{
+}
+
+#endif
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 387705e494b9..1e9173804ede 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -92,6 +92,8 @@
#define AB8500_REV_REG 0x80
#define AB8500_SWITCH_OFF_STATUS 0x00
+#define AB8500_TURN_ON_STATUS 0x00
+
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
* numbers are indexed into this array with (num / 8).
@@ -293,6 +295,7 @@ static struct irq_chip ab8500_irq_chip = {
.irq_bus_lock = ab8500_irq_lock,
.irq_bus_sync_unlock = ab8500_irq_sync_unlock,
.irq_mask = ab8500_irq_mask,
+ .irq_disable = ab8500_irq_mask,
.irq_unmask = ab8500_irq_unmask,
};
@@ -811,12 +814,40 @@ static ssize_t show_switch_off_status(struct device *dev,
return sprintf(buf, "%#x\n", value);
}
+/*
+ * ab8500 has turned on due to (TURN_ON_STATUS):
+ * 0x01 PORnVbat
+ * 0x02 PonKey1dbF
+ * 0x04 PonKey2dbF
+ * 0x08 RTCAlarm
+ * 0x10 MainChDet
+ * 0x20 VbusDet
+ * 0x40 UsbIDDetect
+ * 0x80 Reserved
+ */
+static ssize_t show_turn_on_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 value;
+ struct ab8500 *ab8500;
+
+ ab8500 = dev_get_drvdata(dev);
+ ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+ AB8500_TURN_ON_STATUS, &value);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%#x\n", value);
+}
+
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
+static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
static struct attribute *ab8500_sysfs_entries[] = {
&dev_attr_chip_id.attr,
&dev_attr_switch_off_status.attr,
+ &dev_attr_turn_on_status.attr,
NULL,
};
@@ -843,11 +874,11 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
return ret;
switch (value) {
- case AB8500_CUTEARLY:
case AB8500_CUT1P0:
case AB8500_CUT1P1:
case AB8500_CUT2P0:
case AB8500_CUT3P0:
+ case AB8500_CUT3P3:
dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
break;
default:
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index f16afb234ff9..e985d1701a83 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -143,12 +143,15 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name)
}
EXPORT_SYMBOL(ab8500_gpadc_get);
-static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
+/**
+ * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage
+ */
+int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
int ad_value)
{
int res;
- switch (input) {
+ switch (channel) {
case MAIN_CHARGER_V:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) {
@@ -232,18 +235,46 @@ static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
}
return res;
}
+EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/**
* ab8500_gpadc_convert() - gpadc conversion
- * @input: analog input to be converted to digital data
+ * @channel: analog channel to be converted to digital data
*
* This function converts the selected analog i/p to digital
* data.
*/
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
+int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+{
+ int ad_value;
+ int voltage;
+
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel);
+ if (ad_value < 0) {
+ dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+ return ad_value;
+ }
+
+ voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
+
+ if (voltage < 0)
+ dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
+ " %d AD: 0x%x\n", channel, ad_value);
+
+ return voltage;
+}
+EXPORT_SYMBOL(ab8500_gpadc_convert);
+
+/**
+ * ab8500_gpadc_read_raw() - gpadc read
+ * @channel: analog channel to be read
+ *
+ * This function obtains the raw ADC value, this then needs
+ * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ */
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
{
int ret;
- u16 data = 0;
int looplimit = 0;
u8 val, low_data, high_data;
@@ -278,9 +309,9 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
goto out;
}
- /* Select the input source and set average samples to 16 */
+ /* Select the channel source and set average samples to 16 */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16));
+ AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
@@ -292,7 +323,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
* charging current sense if it needed, ABB 3.0 needs some special
* treatment too.
*/
- switch (input) {
+ switch (channel) {
case MAIN_CHARGER_C:
case USB_CHARGER_C:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
@@ -359,7 +390,6 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
goto out;
}
- data = (high_data << 8) | low_data;
/* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@@ -370,8 +400,8 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
/* Disable VTVout LDO this is required for GPADC */
regulator_disable(gpadc->regu);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
- ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data);
- return ret;
+
+ return (high_data << 8) | low_data;
out:
/*
@@ -385,10 +415,10 @@ out:
regulator_disable(gpadc->regu);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
- "gpadc_conversion: Failed to AD convert channel %d\n", input);
+ "gpadc_conversion: Failed to AD convert channel %d\n", channel);
return ret;
}
-EXPORT_SYMBOL(ab8500_gpadc_convert);
+EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/**
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index c71ae09430c5..3bd85bddf6e3 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -584,7 +584,7 @@ static int asic3_gpio_remove(struct platform_device *pdev)
return gpiochip_remove(&asic->gpio);
}
-static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
+static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
{
unsigned long flags;
u32 cdex;
@@ -596,8 +596,6 @@ static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
}
spin_unlock_irqrestore(&asic->lock, flags);
-
- return 0;
}
static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
@@ -779,6 +777,8 @@ static struct mfd_cell asic3_cell_mmc = {
.name = "tmio-mmc",
.enable = asic3_mmc_enable,
.disable = asic3_mmc_disable,
+ .suspend = asic3_mmc_disable,
+ .resume = asic3_mmc_enable,
.platform_data = &asic3_mmc_data,
.pdata_size = sizeof(asic3_mmc_data),
.num_resources = ARRAY_SIZE(asic3_mmc_resources),
@@ -811,24 +811,43 @@ static int asic3_leds_disable(struct platform_device *pdev)
return 0;
}
+static int asic3_leds_suspend(struct platform_device *pdev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
+ msleep(1);
+
+ asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+ return 0;
+}
+
static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
[0] = {
.name = "leds-asic3",
.id = 0,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
[1] = {
.name = "leds-asic3",
.id = 1,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
[2] = {
.name = "leds-asic3",
.id = 2,
.enable = asic3_leds_enable,
.disable = asic3_leds_disable,
+ .suspend = asic3_leds_suspend,
+ .resume = asic3_leds_enable,
},
};
@@ -949,6 +968,7 @@ static int __init asic3_probe(struct platform_device *pdev)
goto out_unmap;
}
+ asic->gpio.label = "asic3";
asic->gpio.base = pdata->gpio_base;
asic->gpio.ngpio = ASIC3_NUM_GPIOS;
asic->gpio.get = asic3_gpio_get;
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index 2fadbaeb1cb1..1b79c37fd599 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -523,7 +523,7 @@ static int __devinit da903x_probe(struct i2c_client *client,
chip->ops->read_events(chip, &tmp);
ret = request_irq(client->irq, da903x_irq_handler,
- IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING,
"da903x", chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index 9dbb3cab4a6f..bb115b2f04e9 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -20,11 +20,11 @@
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
-#include <linux/mfd/db5500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db5500-regs.h>
-#include "db5500-prcmu-regs.h"
+#include "dbx500-prcmu-regs.h"
#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
@@ -109,15 +109,18 @@ enum mb5_header {
#define PRCMU_DSI_CLOCK_SETTING 0x00000128
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
-#define PRCMU_PLLDSI_FREQ_SETTING 0x0004013C
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
-#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
#define PRCMU_ENABLE_PLLDSI 0x00000001
#define PRCMU_DISABLE_PLLDSI 0x00000000
#define PRCMU_DSI_RESET_SW 0x00000003
+#define PRCMU_RESOUTN0_PIN 0x00000001
+#define PRCMU_RESOUTN1_PIN 0x00000002
+#define PRCMU_RESOUTN2_PIN 0x00000004
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
@@ -315,31 +318,31 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
return r;
}
static bool read_mailbox_1(void)
{
- writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_2(void)
{
- writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_4(void)
{
- writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
return false;
}
@@ -360,19 +363,19 @@ static bool read_mailbox_5(void)
print_unknown_header_warning(5, header);
break;
}
- writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR);
+ writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
return false;
}
@@ -434,7 +437,7 @@ int __init db5500_prcmu_init(void)
return -ENODEV;
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLEAR);
+ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
prcmu_irq_thread_fn, 0, "prcmu", NULL);
diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h
deleted file mode 100644
index 3bbf04d58043..000000000000
--- a/drivers/mfd/db8500-prcmu-regs.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics 2009
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
- *
- * License Terms: GNU General Public License v2
- *
- * PRCM Unit registers
- */
-#ifndef __DB8500_PRCMU_REGS_H
-#define __DB8500_PRCMU_REGS_H
-
-#include <linux/bitops.h>
-#include <mach/hardware.h>
-
-#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
-
-#define PRCM_ARM_PLLDIVPS 0x118
-#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE BITS(0, 5)
-#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xF
-
-#define PRCM_PLLARM_LOCKP 0x0A8
-#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 BIT(1)
-
-#define PRCM_ARM_CHGCLKREQ 0x114
-#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
-
-#define PRCM_PLLARM_ENABLE 0x98
-#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE BIT(0)
-#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON BIT(8)
-
-#define PRCM_ARMCLKFIX_MGT 0x0
-#define PRCM_A9_RESETN_CLR 0x1f4
-#define PRCM_A9_RESETN_SET 0x1f0
-#define PRCM_ARM_LS_CLAMP 0x30C
-#define PRCM_SRAM_A9 0x308
-
-/* ARM WFI Standby signal register */
-#define PRCM_ARM_WFI_STANDBY 0x130
-#define PRCM_IOCR 0x310
-#define PRCM_IOCR_IOFORCE BIT(0)
-
-/* CPU mailbox registers */
-#define PRCM_MBOX_CPU_VAL 0x0FC
-#define PRCM_MBOX_CPU_SET 0x100
-
-/* Dual A9 core interrupt management unit registers */
-#define PRCM_A9_MASK_REQ 0x328
-#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ BIT(0)
-
-#define PRCM_A9_MASK_ACK 0x32C
-#define PRCM_ARMITMSK31TO0 0x11C
-#define PRCM_ARMITMSK63TO32 0x120
-#define PRCM_ARMITMSK95TO64 0x124
-#define PRCM_ARMITMSK127TO96 0x128
-#define PRCM_POWER_STATE_VAL 0x25C
-#define PRCM_ARMITVAL31TO0 0x260
-#define PRCM_ARMITVAL63TO32 0x264
-#define PRCM_ARMITVAL95TO64 0x268
-#define PRCM_ARMITVAL127TO96 0x26C
-
-#define PRCM_HOSTACCESS_REQ 0x334
-#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ BIT(0)
-
-#define PRCM_ARM_IT1_CLR 0x48C
-#define PRCM_ARM_IT1_VAL 0x494
-
-#define PRCM_ITSTATUS0 0x148
-#define PRCM_ITSTATUS1 0x150
-#define PRCM_ITSTATUS2 0x158
-#define PRCM_ITSTATUS3 0x160
-#define PRCM_ITSTATUS4 0x168
-#define PRCM_ITSTATUS5 0x484
-#define PRCM_ITCLEAR5 0x488
-#define PRCM_ARMIT_MASKXP70_IT 0x1018
-
-/* System reset register */
-#define PRCM_APE_SOFTRST 0x228
-
-/* Level shifter and clamp control registers */
-#define PRCM_MMIP_LS_CLAMP_SET 0x420
-#define PRCM_MMIP_LS_CLAMP_CLR 0x424
-
-/* PRCMU HW semaphore */
-#define PRCM_SEM 0x400
-#define PRCM_SEM_PRCM_SEM BIT(0)
-
-/* PRCMU clock/PLL/reset registers */
-#define PRCM_PLLDSI_FREQ 0x500
-#define PRCM_PLLDSI_ENABLE 0x504
-#define PRCM_PLLDSI_LOCKP 0x508
-#define PRCM_DSI_PLLOUT_SEL 0x530
-#define PRCM_DSITVCLK_DIV 0x52C
-#define PRCM_APE_RESETN_SET 0x1E4
-#define PRCM_APE_RESETN_CLR 0x1E8
-
-#define PRCM_TCR 0x1C8
-#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
-#define PRCM_TCR_STOP_TIMERS BIT(16)
-#define PRCM_TCR_DOZE_MODE BIT(17)
-
-#define PRCM_CLKOCR 0x1CC
-#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
-#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
-#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
-#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
-#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
-#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
-#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
-#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
-#define PRCM_CLKOCR_CLK1TYPE BIT(28)
-
-#define PRCM_SGACLK_MGT 0x014
-#define PRCM_UARTCLK_MGT 0x018
-#define PRCM_MSP02CLK_MGT 0x01C
-#define PRCM_MSP1CLK_MGT 0x288
-#define PRCM_I2CCLK_MGT 0x020
-#define PRCM_SDMMCCLK_MGT 0x024
-#define PRCM_SLIMCLK_MGT 0x028
-#define PRCM_PER1CLK_MGT 0x02C
-#define PRCM_PER2CLK_MGT 0x030
-#define PRCM_PER3CLK_MGT 0x034
-#define PRCM_PER5CLK_MGT 0x038
-#define PRCM_PER6CLK_MGT 0x03C
-#define PRCM_PER7CLK_MGT 0x040
-#define PRCM_LCDCLK_MGT 0x044
-#define PRCM_BMLCLK_MGT 0x04C
-#define PRCM_HSITXCLK_MGT 0x050
-#define PRCM_HSIRXCLK_MGT 0x054
-#define PRCM_HDMICLK_MGT 0x058
-#define PRCM_APEATCLK_MGT 0x05C
-#define PRCM_APETRACECLK_MGT 0x060
-#define PRCM_MCDECLK_MGT 0x064
-#define PRCM_IPI2CCLK_MGT 0x068
-#define PRCM_DSIALTCLK_MGT 0x06C
-#define PRCM_DMACLK_MGT 0x074
-#define PRCM_B2R2CLK_MGT 0x078
-#define PRCM_TVCLK_MGT 0x07C
-#define PRCM_UNIPROCLK_MGT 0x278
-#define PRCM_SSPCLK_MGT 0x280
-#define PRCM_RNGCLK_MGT 0x284
-#define PRCM_UICCCLK_MGT 0x27C
-
-#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
-#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
-#define PRCM_CLK_MGT_CLKEN BIT(8)
-
-/* ePOD and memory power signal control registers */
-#define PRCM_EPOD_C_SET 0x410
-#define PRCM_SRAM_LS_SLEEP 0x304
-
-/* Debug power control unit registers */
-#define PRCM_POWER_STATE_SET 0x254
-
-/* Miscellaneous unit registers */
-#define PRCM_DSI_SW_RESET 0x324
-#define PRCM_GPIOCR 0x138
-
-/* GPIOCR register */
-#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
-
-#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
-
-#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 02a15d7cb3b0..a25ab9c6b5af 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -27,14 +27,14 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
#include <mach/id.h>
-#include "db8500-prcmu-regs.h"
+#include "dbx500-prcmu-regs.h"
/* Offset for the firmware version within the TCPM */
#define PRCMU_FW_VERSION_OFFSET 0xA4
@@ -131,12 +131,14 @@
#define MB1H_REQUEST_APE_OPP_100_VOLT 0x3
#define MB1H_RELEASE_APE_OPP_100_VOLT 0x4
#define MB1H_RELEASE_USB_WAKEUP 0x5
+#define MB1H_PLL_ON_OFF 0x6
/* Mailbox 1 Requests */
#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
-#define PRCM_REQ_MB1_APE_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x4)
-#define PRCM_REQ_MB1_ARM_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x8)
+#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4)
+#define PLL_SOC1_OFF 0x4
+#define PLL_SOC1_ON 0x8
/* Mailbox 1 ACKs */
#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
@@ -184,6 +186,11 @@
#define MB4H_HOTDOG 0x12
#define MB4H_HOTMON 0x13
#define MB4H_HOT_PERIOD 0x14
+#define MB4H_A9WDOG_CONF 0x16
+#define MB4H_A9WDOG_EN 0x17
+#define MB4H_A9WDOG_DIS 0x18
+#define MB4H_A9WDOG_LOAD 0x19
+#define MB4H_A9WDOG_KICK 0x20
/* Mailbox 4 Requests */
#define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE (PRCM_REQ_MB4 + 0x0)
@@ -196,6 +203,13 @@
#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 0x0)
#define HOTMON_CONFIG_LOW BIT(0)
#define HOTMON_CONFIG_HIGH BIT(1)
+#define PRCM_REQ_MB4_A9WDOG_0 (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_A9WDOG_1 (PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_A9WDOG_2 (PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_A9WDOG_3 (PRCM_REQ_MB4 + 0x3)
+#define A9WDOG_AUTO_OFF_EN BIT(7)
+#define A9WDOG_AUTO_OFF_DIS 0
+#define A9WDOG_ID_MASK 0xf
/* Mailbox 5 Requests */
#define PRCM_REQ_MB5_I2C_SLAVE_OP (PRCM_REQ_MB5 + 0x0)
@@ -412,7 +426,7 @@ struct clk_mgt {
static DEFINE_SPINLOCK(clk_mgt_lock);
-#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT), 0 }
+#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 }
struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
CLK_MGT_ENTRY(SGACLK),
CLK_MGT_ENTRY(UARTCLK),
@@ -445,6 +459,35 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
CLK_MGT_ENTRY(UICCCLK),
};
+static struct regulator *hwacc_regulator[NUM_HW_ACC];
+static struct regulator *hwacc_ret_regulator[NUM_HW_ACC];
+
+static bool hwacc_enabled[NUM_HW_ACC];
+static bool hwacc_ret_enabled[NUM_HW_ACC];
+
+static const char *hwacc_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp",
+ [HW_ACC_SVAPIPE] = "hwacc-sva-pipe",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp",
+ [HW_ACC_SIAPIPE] = "hwacc-sia-pipe",
+ [HW_ACC_SGA] = "hwacc-sga",
+ [HW_ACC_B2R2] = "hwacc-b2r2",
+ [HW_ACC_MCDE] = "hwacc-mcde",
+ [HW_ACC_ESRAM1] = "hwacc-esram1",
+ [HW_ACC_ESRAM2] = "hwacc-esram2",
+ [HW_ACC_ESRAM3] = "hwacc-esram3",
+ [HW_ACC_ESRAM4] = "hwacc-esram4",
+};
+
+static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp-ret",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp-ret",
+ [HW_ACC_ESRAM1] = "hwacc-esram1-ret",
+ [HW_ACC_ESRAM2] = "hwacc-esram2-ret",
+ [HW_ACC_ESRAM3] = "hwacc-esram3-ret",
+ [HW_ACC_ESRAM4] = "hwacc-esram4-ret",
+};
+
/*
* Used by MCDE to setup all necessary PRCMU registers
*/
@@ -493,55 +536,51 @@ static struct {
} prcmu_version;
-int prcmu_enable_dsipll(void)
+int db8500_prcmu_enable_dsipll(void)
{
int i;
unsigned int plldsifreq;
/* Clear DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_CLR));
+ writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
/* Unclamp DSIPLL in/out */
- writel(PRCMU_UNCLAMP_DSIPLL, (_PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR));
+ writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
if (prcmu_is_u8400())
plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400;
else
plldsifreq = PRCMU_PLLDSI_FREQ_SETTING;
/* Set DSI PLL FREQ */
- writel(plldsifreq, (_PRCMU_BASE + PRCM_PLLDSI_FREQ));
- writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
- (_PRCMU_BASE + PRCM_DSI_PLLOUT_SEL));
+ writel(plldsifreq, PRCM_PLLDSI_FREQ);
+ writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
/* Enable Escape clocks */
- writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV,
- (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
/* Start DSI PLL */
- writel(PRCMU_ENABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
/* Reset DSI PLL */
- writel(PRCMU_DSI_RESET_SW, (_PRCMU_BASE + PRCM_DSI_SW_RESET));
+ writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
for (i = 0; i < 10; i++) {
- if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
- PRCMU_PLLDSI_LOCKP_LOCKED)
+ if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED)
== PRCMU_PLLDSI_LOCKP_LOCKED)
break;
udelay(100);
}
/* Set DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_SET));
+ writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
return 0;
}
-int prcmu_disable_dsipll(void)
+int db8500_prcmu_disable_dsipll(void)
{
/* Disable dsi pll */
- writel(PRCMU_DISABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
/* Disable escapeclock */
- writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV,
- (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
return 0;
}
-int prcmu_set_display_clocks(void)
+int db8500_prcmu_set_display_clocks(void)
{
unsigned long flags;
unsigned int dsiclk;
@@ -554,15 +593,15 @@ int prcmu_set_display_clocks(void)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- writel(dsiclk, (_PRCMU_BASE + PRCM_HDMICLK_MGT));
- writel(PRCMU_DSI_LP_CLOCK_SETTING, (_PRCMU_BASE + PRCM_TVCLK_MGT));
- writel(PRCMU_DPI_CLOCK_SETTING, (_PRCMU_BASE + PRCM_LCDCLK_MGT));
+ writel(dsiclk, PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+ writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT);
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
@@ -578,8 +617,8 @@ void prcmu_enable_spi2(void)
unsigned long flags;
spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
- writel(reg | PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ reg = readl(PRCM_GPIOCR);
+ writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
spin_unlock_irqrestore(&gpiocr_lock, flags);
}
@@ -592,8 +631,8 @@ void prcmu_disable_spi2(void)
unsigned long flags;
spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
- writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ reg = readl(PRCM_GPIOCR);
+ writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
spin_unlock_irqrestore(&gpiocr_lock, flags);
}
@@ -701,7 +740,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
spin_lock_irqsave(&clkout_lock, flags);
- val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ val = readl(PRCM_CLKOCR);
if (val & div_mask) {
if (div) {
if ((val & mask) != bits) {
@@ -715,7 +754,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
}
}
}
- writel((bits | (val & ~mask)), (_PRCMU_BASE + PRCM_CLKOCR));
+ writel((bits | (val & ~mask)), PRCM_CLKOCR);
requests[clkout] += (div ? 1 : -1);
unlock_and_return:
@@ -724,7 +763,7 @@ unlock_and_return:
return r;
}
-int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
{
unsigned long flags;
@@ -732,7 +771,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
@@ -741,7 +780,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
writeb((keep_ulp_clk ? 1 : 0),
(tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE));
writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
@@ -770,18 +809,18 @@ static void config_wakeups(void)
return;
for (i = 0; i < 2; i++) {
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500));
writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500));
writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
}
last_dbb_events = dbb_events;
last_abb_events = abb_events;
}
-void prcmu_enable_wakeups(u32 wakeups)
+void db8500_prcmu_enable_wakeups(u32 wakeups)
{
unsigned long flags;
u32 bits;
@@ -802,7 +841,7 @@ void prcmu_enable_wakeups(u32 wakeups)
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
-void prcmu_config_abb_event_readout(u32 abb_events)
+void db8500_prcmu_config_abb_event_readout(u32 abb_events)
{
unsigned long flags;
@@ -814,7 +853,7 @@ void prcmu_config_abb_event_readout(u32 abb_events)
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
-void prcmu_get_abb_event_buffer(void __iomem **buf)
+void db8500_prcmu_get_abb_event_buffer(void __iomem **buf)
{
if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500);
@@ -823,13 +862,13 @@ void prcmu_get_abb_event_buffer(void __iomem **buf)
}
/**
- * prcmu_set_arm_opp - set the appropriate ARM OPP
+ * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP
* @opp: The new ARM operating point to which transition is to be made
* Returns: 0 on success, non-zero on failure
*
* This function sets the the operating point of the ARM.
*/
-int prcmu_set_arm_opp(u8 opp)
+int db8500_prcmu_set_arm_opp(u8 opp)
{
int r;
@@ -840,14 +879,14 @@ int prcmu_set_arm_opp(u8 opp)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
@@ -860,11 +899,11 @@ int prcmu_set_arm_opp(u8 opp)
}
/**
- * prcmu_get_arm_opp - get the current ARM OPP
+ * db8500_prcmu_get_arm_opp - get the current ARM OPP
*
* Returns: the current ARM OPP
*/
-int prcmu_get_arm_opp(void)
+int db8500_prcmu_get_arm_opp(void)
{
return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP);
}
@@ -876,7 +915,7 @@ int prcmu_get_arm_opp(void)
*/
int prcmu_get_ddr_opp(void)
{
- return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+ return readb(PRCM_DDR_SUBSYS_APE_MINBW);
}
/**
@@ -892,7 +931,7 @@ int prcmu_set_ddr_opp(u8 opp)
return -EINVAL;
/* Changing the DDR OPP can hang the hardware pre-v21 */
if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
- writeb(opp, (_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW));
+ writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
return 0;
}
@@ -909,14 +948,14 @@ int prcmu_set_ape_opp(u8 opp)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
@@ -966,12 +1005,12 @@ int prcmu_request_ape_opp_100_voltage(bool enable)
header = MB1H_RELEASE_APE_OPP_100_VOLT;
}
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != header) ||
@@ -995,13 +1034,13 @@ int prcmu_release_usb_wakeup_state(void)
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_RELEASE_USB_WAKEUP,
(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) ||
@@ -1013,15 +1052,169 @@ int prcmu_release_usb_wakeup_state(void)
return r;
}
+static int request_pll(u8 clock, bool enable)
+{
+ int r = 0;
+
+ if (clock == PRCMU_PLLSOC1)
+ clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
+ else
+ return -EINVAL;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF));
+
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb1_transfer.work);
+
+ if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF)
+ r = -EIO;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
/**
- * prcmu_set_epod - set the state of a EPOD (power domain)
+ * prcmu_set_hwacc - set the power state of a h/w accelerator
+ * @hwacc_dev: The hardware accelerator (enum hw_acc_dev).
+ * @state: The new power state (enum hw_acc_state).
+ *
+ * This function sets the power state of a hardware accelerator.
+ * This function should not be called from interrupt context.
+ *
+ * NOTE! Deprecated, to be removed when all users switched over to use the
+ * regulator framework API.
+ */
+int prcmu_set_hwacc(u16 hwacc_dev, u8 state)
+{
+ int r = 0;
+ bool ram_retention = false;
+ bool enable, enable_ret;
+
+ /* check argument */
+ BUG_ON(hwacc_dev >= NUM_HW_ACC);
+
+ /* get state of switches */
+ enable = hwacc_enabled[hwacc_dev];
+ enable_ret = hwacc_ret_enabled[hwacc_dev];
+
+ /* set flag if retention is possible */
+ switch (hwacc_dev) {
+ case HW_ACC_SVAMMDSP:
+ case HW_ACC_SIAMMDSP:
+ case HW_ACC_ESRAM1:
+ case HW_ACC_ESRAM2:
+ case HW_ACC_ESRAM3:
+ case HW_ACC_ESRAM4:
+ ram_retention = true;
+ break;
+ }
+
+ /* check argument */
+ BUG_ON(state > HW_ON);
+ BUG_ON(state == HW_OFF_RAMRET && !ram_retention);
+
+ /* modify enable flags */
+ switch (state) {
+ case HW_OFF:
+ enable_ret = false;
+ enable = false;
+ break;
+ case HW_ON:
+ enable = true;
+ break;
+ case HW_OFF_RAMRET:
+ enable_ret = true;
+ enable = false;
+ break;
+ }
+
+ /* get regulator (lazy) */
+ if (hwacc_regulator[hwacc_dev] == NULL) {
+ hwacc_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+
+ if (ram_retention) {
+ if (hwacc_ret_regulator[hwacc_dev] == NULL) {
+ hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_ret_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_ret_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+ }
+
+ /* set regulators */
+ if (ram_retention) {
+ if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret enable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = true;
+ }
+ }
+
+ if (enable && !hwacc_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: enable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = true;
+ }
+
+ if (!enable && hwacc_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: disable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = false;
+ }
+
+ if (ram_retention) {
+ if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret disable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = false;
+ }
+ }
+
+out:
+ return r;
+}
+EXPORT_SYMBOL(prcmu_set_hwacc);
+
+/**
+ * db8500_prcmu_set_epod - set the state of a EPOD (power domain)
* @epod_id: The EPOD to set
* @epod_state: The new EPOD state
*
* This function sets the state of a EPOD (power domain). It may not be called
* from interrupt context.
*/
-int prcmu_set_epod(u16 epod_id, u8 epod_state)
+int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
{
int r = 0;
bool ram_retention = false;
@@ -1048,7 +1241,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state)
mutex_lock(&mb2_transfer.lock);
/* wait for mailbox */
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
cpu_relax();
/* fill in mailbox */
@@ -1058,7 +1251,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state)
writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2));
- writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
/*
* The current firmware version does not handle errors correctly,
@@ -1145,13 +1338,13 @@ static int request_sysclk(bool enable)
spin_lock_irqsave(&mb3_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
cpu_relax();
writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT));
writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3));
- writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb3_transfer.lock, flags);
@@ -1177,7 +1370,7 @@ static int request_timclk(bool enable)
if (!enable)
val |= PRCM_TCR_STOP_TIMERS;
- writel(val, (_PRCMU_BASE + PRCM_TCR));
+ writel(val, PRCM_TCR);
return 0;
}
@@ -1190,7 +1383,7 @@ static int request_reg_clock(u8 clock, bool enable)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
@@ -1203,34 +1396,61 @@ static int request_reg_clock(u8 clock, bool enable)
writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
return 0;
}
+static int request_sga_clock(u8 clock, bool enable)
+{
+ u32 val;
+ int ret;
+
+ if (enable) {
+ val = readl(PRCM_CGATING_BYPASS);
+ writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+ }
+
+ ret = request_reg_clock(clock, enable);
+
+ if (!ret && !enable) {
+ val = readl(PRCM_CGATING_BYPASS);
+ writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+ }
+
+ return ret;
+}
+
/**
- * prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
* @clock: The clock for which the request is made.
* @enable: Whether the clock should be enabled (true) or disabled (false).
*
* This function should only be used by the clock implementation.
* Do not use it from any other place!
*/
-int prcmu_request_clock(u8 clock, bool enable)
+int db8500_prcmu_request_clock(u8 clock, bool enable)
{
- if (clock < PRCMU_NUM_REG_CLOCKS)
- return request_reg_clock(clock, enable);
- else if (clock == PRCMU_TIMCLK)
+ switch(clock) {
+ case PRCMU_SGACLK:
+ return request_sga_clock(clock, enable);
+ case PRCMU_TIMCLK:
return request_timclk(enable);
- else if (clock == PRCMU_SYSCLK)
+ case PRCMU_SYSCLK:
return request_sysclk(enable);
- else
- return -EINVAL;
+ case PRCMU_PLLSOC1:
+ return request_pll(clock, enable);
+ default:
+ break;
+ }
+ if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ return -EINVAL;
}
-int prcmu_config_esram0_deep_sleep(u8 state)
+int db8500_prcmu_config_esram0_deep_sleep(u8 state)
{
if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
(state < ESRAM0_DEEP_SLEEP_STATE_OFF))
@@ -1238,7 +1458,7 @@ int prcmu_config_esram0_deep_sleep(u8 state)
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
@@ -1248,7 +1468,7 @@ int prcmu_config_esram0_deep_sleep(u8 state)
(tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE));
writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1260,13 +1480,13 @@ int prcmu_config_hotdog(u8 threshold)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD));
writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1278,7 +1498,7 @@ int prcmu_config_hotmon(u8 low, u8 high)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW));
@@ -1287,7 +1507,7 @@ int prcmu_config_hotmon(u8 low, u8 high)
(tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG));
writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1299,13 +1519,13 @@ static int config_hot_period(u16 val)
{
mutex_lock(&mb4_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
cpu_relax();
writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD));
writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb4_transfer.work);
mutex_unlock(&mb4_transfer.lock);
@@ -1326,6 +1546,78 @@ int prcmu_stop_temp_sense(void)
return config_hot_period(0xFFFF);
}
+static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
+{
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
+ writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
+ writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
+ writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
+
+ writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+
+}
+
+int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ BUG_ON(num == 0 || num > 0xf);
+ return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
+ sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
+ A9WDOG_AUTO_OFF_DIS);
+}
+
+int prcmu_enable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
+}
+
+int prcmu_disable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
+}
+
+int prcmu_kick_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
+}
+
+/*
+ * timeout is 28 bit, in ms.
+ */
+#define MAX_WATCHDOG_TIMEOUT 131000
+int prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+ if (timeout > MAX_WATCHDOG_TIMEOUT)
+ /*
+ * Due to calculation bug in prcmu fw, timeouts
+ * can't be bigger than 131 seconds.
+ */
+ return -EINVAL;
+
+ return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
+ (id & A9WDOG_ID_MASK) |
+ /*
+ * Put the lowest 28 bits of timeout at
+ * offset 4. Four first bits are used for id.
+ */
+ (u8)((timeout << 4) & 0xf0),
+ (u8)((timeout >> 4) & 0xff),
+ (u8)((timeout >> 12) & 0xff),
+ (u8)((timeout >> 20) & 0xff));
+}
+
/**
* prcmu_set_clock_divider() - Configure the clock divider.
* @clock: The clock for which the request is made.
@@ -1345,7 +1637,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider)
spin_lock_irqsave(&clk_mgt_lock, flags);
/* Grab the HW semaphore. */
- while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
@@ -1354,7 +1646,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider)
writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
/* Release the HW semaphore. */
- writel(0, (_PRCMU_BASE + PRCM_SEM));
+ writel(0, PRCM_SEM);
spin_unlock_irqrestore(&clk_mgt_lock, flags);
@@ -1380,7 +1672,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
@@ -1388,7 +1680,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
if (!wait_for_completion_timeout(&mb5_transfer.work,
msecs_to_jiffies(20000))) {
@@ -1426,7 +1718,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
@@ -1434,7 +1726,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
if (!wait_for_completion_timeout(&mb5_transfer.work,
msecs_to_jiffies(20000))) {
@@ -1456,21 +1748,44 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
void prcmu_ac_wake_req(void)
{
u32 val;
+ u32 status;
mutex_lock(&mb0_transfer.ac_wake_lock);
- val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ val = readl(PRCM_HOSTACCESS_REQ);
if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)
goto unlock_and_return;
atomic_set(&ac_wake_req_state, 1);
- writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
- (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+retry:
+ writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
- msecs_to_jiffies(20000))) {
- pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ msecs_to_jiffies(5000))) {
+ 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__);
}
@@ -1487,16 +1802,16 @@ void prcmu_ac_sleep_req()
mutex_lock(&mb0_transfer.ac_wake_lock);
- val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ val = readl(PRCM_HOSTACCESS_REQ);
if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ))
goto unlock_and_return;
writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
- (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+ PRCM_HOSTACCESS_REQ);
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
- msecs_to_jiffies(20000))) {
- pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ msecs_to_jiffies(5000))) {
+ pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
}
@@ -1506,21 +1821,32 @@ unlock_and_return:
mutex_unlock(&mb0_transfer.ac_wake_lock);
}
-bool prcmu_is_ac_wake_requested(void)
+bool db8500_prcmu_is_ac_wake_requested(void)
{
return (atomic_read(&ac_wake_req_state) != 0);
}
/**
- * prcmu_system_reset - System reset
+ * db8500_prcmu_system_reset - System reset
*
- * Saves the reset reason code and then sets the APE_SOFRST register which
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
* fires interrupt to fw
*/
-void prcmu_system_reset(u16 reset_code)
+void db8500_prcmu_system_reset(u16 reset_code)
{
writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON));
- writel(1, (_PRCMU_BASE + PRCM_APE_SOFTRST));
+ writel(1, PRCM_APE_SOFTRST);
+}
+
+/**
+ * db8500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db8500_prcmu_get_reset_code(void)
+{
+ return readw(tcdm_base + PRCM_SW_RST_REASON);
}
/**
@@ -1530,11 +1856,11 @@ void prcmu_modem_reset(void)
{
mutex_lock(&mb1_transfer.lock);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
/*
@@ -1551,11 +1877,11 @@ static void ack_dbb_wakeup(void)
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
@@ -1600,7 +1926,7 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
return r;
}
@@ -1613,7 +1939,7 @@ static bool read_mailbox_1(void)
PRCM_ACK_MB1_CURRENT_APE_OPP);
mb1_transfer.ack.ape_voltage_status = readb(tcdm_base +
PRCM_ACK_MB1_APE_VOLTAGE_STATUS);
- writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
complete(&mb1_transfer.work);
return false;
}
@@ -1621,14 +1947,14 @@ static bool read_mailbox_1(void)
static bool read_mailbox_2(void)
{
mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS);
- writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
complete(&mb2_transfer.work);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
return false;
}
@@ -1643,6 +1969,11 @@ static bool read_mailbox_4(void)
case MB4H_HOTDOG:
case MB4H_HOTMON:
case MB4H_HOT_PERIOD:
+ case MB4H_A9WDOG_CONF:
+ case MB4H_A9WDOG_EN:
+ case MB4H_A9WDOG_DIS:
+ case MB4H_A9WDOG_LOAD:
+ case MB4H_A9WDOG_KICK:
break;
default:
print_unknown_header_warning(4, header);
@@ -1650,7 +1981,7 @@ static bool read_mailbox_4(void)
break;
}
- writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
if (do_complete)
complete(&mb4_transfer.work);
@@ -1662,20 +1993,20 @@ static bool read_mailbox_5(void)
{
mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS);
mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL);
- writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
complete(&mb5_transfer.work);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
return false;
}
@@ -1696,7 +2027,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
u8 n;
irqreturn_t r;
- bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
if (unlikely(!bits))
return IRQ_NONE;
@@ -1768,7 +2099,7 @@ static struct irq_chip prcmu_irq_chip = {
.irq_unmask = prcmu_irq_unmask,
};
-void __init prcmu_early_init(void)
+void __init db8500_prcmu_early_init(void)
{
unsigned int i;
@@ -1826,6 +2157,16 @@ void __init prcmu_early_init(void)
}
}
+static void __init db8500_prcmu_init_clkforce(void)
+{
+ u32 val;
+
+ val = readl(PRCM_A9PL_FORCE_CLKEN);
+ val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN |
+ PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN);
+ writel(val, (PRCM_A9PL_FORCE_CLKEN));
+}
+
/*
* Power domain switches (ePODs) modeled as regulators for the DB8500 SoC
*/
@@ -1861,7 +2202,42 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
REGULATOR_SUPPLY("vsupply", "b2r2.0"),
- REGULATOR_SUPPLY("vsupply", "mcde.0"),
+ REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+/* SVA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_svammdsp_consumers[] = {
+ REGULATOR_SUPPLY("sva-mmdsp", "cm_control"),
+};
+
+/* SVA pipe regulator switch */
+static struct regulator_consumer_supply db8500_svapipe_consumers[] = {
+ REGULATOR_SUPPLY("sva-pipe", "cm_control"),
+};
+
+/* SIA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_siammdsp_consumers[] = {
+ REGULATOR_SUPPLY("sia-mmdsp", "cm_control"),
+};
+
+/* SIA pipe regulator switch */
+static struct regulator_consumer_supply db8500_siapipe_consumers[] = {
+ REGULATOR_SUPPLY("sia-pipe", "cm_control"),
+};
+
+static struct regulator_consumer_supply db8500_sga_consumers[] = {
+ REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+/* ESRAM1 and 2 regulator switch */
+static struct regulator_consumer_supply db8500_esram12_consumers[] = {
+ REGULATOR_SUPPLY("esram12", "cm_control"),
+};
+
+/* ESRAM3 and 4 regulator switch */
+static struct regulator_consumer_supply db8500_esram34_consumers[] = {
+ REGULATOR_SUPPLY("v-esram34", "mcde"),
+ REGULATOR_SUPPLY("esram34", "cm_control"),
};
static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
@@ -1923,6 +2299,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sva-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_svammdsp_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers),
},
[DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
.constraints = {
@@ -1937,6 +2315,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sva-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_svapipe_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
},
[DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
.supply_regulator = "db8500-vape",
@@ -1944,6 +2324,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sia-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_siammdsp_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers),
},
[DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
.constraints = {
@@ -1957,6 +2339,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sia-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_siapipe_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers),
},
[DB8500_REGULATOR_SWITCH_SGA] = {
.supply_regulator = "db8500-vape",
@@ -1964,6 +2348,9 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-sga",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_sga_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers),
+
},
[DB8500_REGULATOR_SWITCH_B2R2_MCDE] = {
.supply_regulator = "db8500-vape",
@@ -1980,6 +2367,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-esram12",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_esram12_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers),
},
[DB8500_REGULATOR_SWITCH_ESRAM12RET] = {
.constraints = {
@@ -1993,6 +2382,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.name = "db8500-esram34",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
+ .consumer_supplies = db8500_esram34_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers),
},
[DB8500_REGULATOR_SWITCH_ESRAM34RET] = {
.constraints = {
@@ -2024,8 +2415,10 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
if (ux500_is_svp())
return -ENODEV;
+ db8500_prcmu_init_clkforce();
+
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 9a8e9e4ddd33..ec22e9f15d32 100644
--- a/drivers/mfd/db5500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -10,11 +10,49 @@
* PRCM Unit registers
*/
-#ifndef __MACH_PRCMU_REGS_H
-#define __MACH_PRCMU_REGS_H
+#ifndef __DB8500_PRCMU_REGS_H
+#define __DB8500_PRCMU_REGS_H
#include <mach/hardware.h>
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_SVACLK_MGT_OFF 0x008
+#define PRCM_SIACLK_MGT_OFF 0x00C
+#define PRCM_SGACLK_MGT_OFF 0x014
+#define PRCM_UARTCLK_MGT_OFF 0x018
+#define PRCM_MSP02CLK_MGT_OFF 0x01C
+#define PRCM_I2CCLK_MGT_OFF 0x020
+#define PRCM_SDMMCCLK_MGT_OFF 0x024
+#define PRCM_SLIMCLK_MGT_OFF 0x028
+#define PRCM_PER1CLK_MGT_OFF 0x02C
+#define PRCM_PER2CLK_MGT_OFF 0x030
+#define PRCM_PER3CLK_MGT_OFF 0x034
+#define PRCM_PER5CLK_MGT_OFF 0x038
+#define PRCM_PER6CLK_MGT_OFF 0x03C
+#define PRCM_PER7CLK_MGT_OFF 0x040
+#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */
+#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */
+#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */
+#define PRCM_LCDCLK_MGT_OFF 0x044
+#define PRCM_BMLCLK_MGT_OFF 0x04C
+#define PRCM_HSITXCLK_MGT_OFF 0x050
+#define PRCM_HSIRXCLK_MGT_OFF 0x054
+#define PRCM_HDMICLK_MGT_OFF 0x058
+#define PRCM_APEATCLK_MGT_OFF 0x05C
+#define PRCM_APETRACECLK_MGT_OFF 0x060
+#define PRCM_MCDECLK_MGT_OFF 0x064
+#define PRCM_IPI2CCLK_MGT_OFF 0x068
+#define PRCM_DSIALTCLK_MGT_OFF 0x06C
+#define PRCM_DMACLK_MGT_OFF 0x074
+#define PRCM_B2R2CLK_MGT_OFF 0x078
+#define PRCM_TVCLK_MGT_OFF 0x07C
+#define PRCM_UNIPROCLK_MGT_OFF 0x278
+#define PRCM_SSPCLK_MGT_OFF 0x280
+#define PRCM_RNGCLK_MGT_OFF 0x284
+#define PRCM_UICCCLK_MGT_OFF 0x27C
+#define PRCM_MSP1CLK_MGT_OFF 0x288
+
#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf
@@ -30,11 +68,15 @@
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100
#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0)
+#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C)
#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4)
#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0)
#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c)
#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
+
/* ARM WFI Standby signal register */
#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
#define PRCM_IOCR (_PRCMU_BASE + 0x310)
@@ -61,12 +103,18 @@
#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C)
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
+#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
#define ARM_WAKEUP_MODEM 0x1
-#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C)
+#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494)
#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174)
+#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
+
#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148)
#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150)
#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158)
@@ -87,16 +135,21 @@
#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044)
-#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064)
-#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058)
-#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c)
+#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF)
+#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF)
+#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF)
+#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)
#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
+
#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
+#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
+#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
+#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16)
+#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29)
/* ePOD and memory power signal control registers */
#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410)
@@ -111,5 +164,41 @@
#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
+/* PRCMU HW semaphore */
+#define PRCM_SEM (_PRCMU_BASE + 0x400)
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define PRCM_TCR (_PRCMU_BASE + 0x1C8)
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
+#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
+#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
+#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
+#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
+#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
+#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
+#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
+#define PRCM_CLKOCR_CLK1TYPE BIT(28)
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+/* GPIOCR register */
+#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
+
+#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438)
+#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134)
+#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
+
+/* Miscellaneous unit registers */
+#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214)
+#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218)
+
+/* System reset register */
+#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
-#endif /* __MACH_PRCMU__REGS_H */
+#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c
new file mode 100644
index 000000000000..97c27762174f
--- /dev/null
+++ b/drivers/mfd/intel_msic.c
@@ -0,0 +1,502 @@
+/*
+ * Driver for Intel MSIC
+ *
+ * Copyright (C) 2011, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.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/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_msic.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define MSIC_VENDOR(id) ((id >> 6) & 3)
+#define MSIC_VERSION(id) (id & 0x3f)
+#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7))
+#define MSIC_MINOR(id) (id & 7)
+
+/*
+ * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE.
+ * Since IRQ block starts from address 0x002 we need to substract that from
+ * the actual IRQ status register address.
+ */
+#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2))
+#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET)
+
+/*
+ * The SCU hardware has limitation of 16 bytes per read/write buffer on
+ * Medfield.
+ */
+#define SCU_IPC_RWBUF_LIMIT 16
+
+/**
+ * struct intel_msic - an MSIC MFD instance
+ * @pdev: pointer to the platform device
+ * @vendor: vendor ID
+ * @version: chip version
+ * @irq_base: base address of the mapped MSIC SRAM interrupt tree
+ */
+struct intel_msic {
+ struct platform_device *pdev;
+ unsigned vendor;
+ unsigned version;
+ void __iomem *irq_base;
+};
+
+static struct resource msic_touch_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_adc_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_battery_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_gpio_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_audio_resources[] = {
+ {
+ .name = "IRQ",
+ .flags = IORESOURCE_IRQ,
+ },
+ /*
+ * We will pass IRQ_BASE to the driver now but this can be removed
+ * when/if the driver starts to use intel_msic_irq_read().
+ */
+ {
+ .name = "IRQ_BASE",
+ .flags = IORESOURCE_MEM,
+ .start = MSIC_IRQ_STATUS_ACCDET,
+ .end = MSIC_IRQ_STATUS_ACCDET,
+ },
+};
+
+static struct resource msic_hdmi_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_thermal_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_power_btn_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource msic_ocd_resources[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*
+ * Devices that are part of the MSIC and are available via firmware
+ * populated SFI DEVS table.
+ */
+static struct mfd_cell msic_devs[] = {
+ [INTEL_MSIC_BLOCK_TOUCH] = {
+ .name = "msic_touch",
+ .num_resources = ARRAY_SIZE(msic_touch_resources),
+ .resources = msic_touch_resources,
+ },
+ [INTEL_MSIC_BLOCK_ADC] = {
+ .name = "msic_adc",
+ .num_resources = ARRAY_SIZE(msic_adc_resources),
+ .resources = msic_adc_resources,
+ },
+ [INTEL_MSIC_BLOCK_BATTERY] = {
+ .name = "msic_battery",
+ .num_resources = ARRAY_SIZE(msic_battery_resources),
+ .resources = msic_battery_resources,
+ },
+ [INTEL_MSIC_BLOCK_GPIO] = {
+ .name = "msic_gpio",
+ .num_resources = ARRAY_SIZE(msic_gpio_resources),
+ .resources = msic_gpio_resources,
+ },
+ [INTEL_MSIC_BLOCK_AUDIO] = {
+ .name = "msic_audio",
+ .num_resources = ARRAY_SIZE(msic_audio_resources),
+ .resources = msic_audio_resources,
+ },
+ [INTEL_MSIC_BLOCK_HDMI] = {
+ .name = "msic_hdmi",
+ .num_resources = ARRAY_SIZE(msic_hdmi_resources),
+ .resources = msic_hdmi_resources,
+ },
+ [INTEL_MSIC_BLOCK_THERMAL] = {
+ .name = "msic_thermal",
+ .num_resources = ARRAY_SIZE(msic_thermal_resources),
+ .resources = msic_thermal_resources,
+ },
+ [INTEL_MSIC_BLOCK_POWER_BTN] = {
+ .name = "msic_power_btn",
+ .num_resources = ARRAY_SIZE(msic_power_btn_resources),
+ .resources = msic_power_btn_resources,
+ },
+ [INTEL_MSIC_BLOCK_OCD] = {
+ .name = "msic_ocd",
+ .num_resources = ARRAY_SIZE(msic_ocd_resources),
+ .resources = msic_ocd_resources,
+ },
+};
+
+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table. These can be pseudo devices, regulators etc. which are needed for
+ * different purposes.
+ *
+ * These devices appear only after the MSIC driver itself is initialized so
+ * we can guarantee that the SCU IPC interface is ready.
+ */
+static struct mfd_cell msic_other_devs[] = {
+ /* Audio codec in the MSIC */
+ {
+ .id = -1,
+ .name = "sn95031",
+ },
+};
+
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+ return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+ return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);
+
+/**
+ * intel_msic_reg_update - update a single MSIC register
+ * @reg: register to update
+ * @val: value to write to the register
+ * @mask: specifies which of the bits are updated (%0 = don't update,
+ * %1 = update)
+ *
+ * Perform an update to a register @reg. @mask is used to specify which
+ * bits are updated. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask)
+{
+ return intel_scu_ipc_update_register(reg, val, mask);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_update);
+
+/**
+ * intel_msic_bulk_read - read an array of registers
+ * @reg: array of register addresses to read
+ * @buf: array where the read values are placed
+ * @count: number of registers to read
+ *
+ * Function reads @count registers from the MSIC using addresses passed in
+ * @reg. Read values are placed in @buf. Reads are performed atomically
+ * wrt. MSIC.
+ *
+ * Returns %0 in case of success and negative errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
+{
+ if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+ return -EINVAL;
+
+ return intel_scu_ipc_readv(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
+
+/**
+ * intel_msic_bulk_write - write an array of values to the MSIC registers
+ * @reg: array of registers to write
+ * @buf: values to write to each register
+ * @count: number of registers to write
+ *
+ * Function writes @count registers in @buf to MSIC. Writes are performed
+ * atomically wrt MSIC. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
+{
+ if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+ return -EINVAL;
+
+ return intel_scu_ipc_writev(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_write);
+
+/**
+ * intel_msic_irq_read - read a register from an MSIC interrupt tree
+ * @msic: MSIC instance
+ * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and
+ * %INTEL_MSIC_RESETIRQ2)
+ * @val: value of the register is placed here
+ *
+ * This function can be used by an MSIC subdevice interrupt handler to read
+ * a register value from the MSIC interrupt tree. In this way subdevice
+ * drivers don't have to map in the interrupt tree themselves but can just
+ * call this function instead.
+ *
+ * Function doesn't sleep and is callable from interrupt context.
+ *
+ * Returns %-EINVAL if @reg is outside of the allowed register region.
+ */
+int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
+{
+ if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2))
+ return -EINVAL;
+
+ *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(intel_msic_irq_read);
+
+static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+{
+ struct platform_device *pdev = msic->pdev;
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ int ret, i;
+
+ if (pdata->gpio) {
+ struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
+
+ cell->platform_data = pdata->gpio;
+ cell->pdata_size = sizeof(*pdata->gpio);
+ }
+
+ if (pdata->ocd) {
+ unsigned gpio = pdata->ocd->gpio;
+
+ ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register OCD GPIO\n");
+ return ret;
+ }
+
+ ret = gpio_to_irq(gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
+ gpio_free(gpio);
+ return ret;
+ }
+
+ /* Update the IRQ number for the OCD */
+ pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+ if (!pdata->irq[i])
+ continue;
+
+ ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
+ pdata->irq[i]);
+ if (ret)
+ goto fail;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
+ ARRAY_SIZE(msic_other_devs), NULL, 0);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ mfd_remove_devices(&pdev->dev);
+ if (pdata->ocd)
+ gpio_free(pdata->ocd->gpio);
+
+ return ret;
+}
+
+static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
+{
+ struct platform_device *pdev = msic->pdev;
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+
+ mfd_remove_devices(&pdev->dev);
+
+ if (pdata->ocd)
+ gpio_free(pdata->ocd->gpio);
+}
+
+static int __devinit intel_msic_probe(struct platform_device *pdev)
+{
+ struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ struct intel_msic *msic;
+ struct resource *res;
+ u8 id0, id1;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data passed\n");
+ return -EINVAL;
+ }
+
+ /* First validate that we have an MSIC in place */
+ ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n");
+ return -ENXIO;
+ }
+
+ ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n");
+ return -ENXIO;
+ }
+
+ if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) {
+ dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1);
+ return -ENXIO;
+ }
+
+ msic = kzalloc(sizeof(*msic), GFP_KERNEL);
+ if (!msic)
+ return -ENOMEM;
+
+ msic->vendor = MSIC_VENDOR(id0);
+ msic->version = MSIC_VERSION(id0);
+ msic->pdev = pdev;
+
+ /*
+ * Map in the MSIC interrupt tree area in SRAM. This is exposed to
+ * the clients via intel_msic_irq_read().
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
+ ret = -ENODEV;
+ goto fail_free_msic;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ ret = -EBUSY;
+ goto fail_free_msic;
+ }
+
+ msic->irq_base = ioremap_nocache(res->start, resource_size(res));
+ if (!msic->irq_base) {
+ dev_err(&pdev->dev, "failed to map SRAM memory\n");
+ ret = -ENOMEM;
+ goto fail_release_region;
+ }
+
+ platform_set_drvdata(pdev, msic);
+
+ ret = intel_msic_init_devices(msic);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
+ goto fail_unmap_mem;
+ }
+
+ dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
+ MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version),
+ msic->vendor);
+
+ return 0;
+
+fail_unmap_mem:
+ iounmap(msic->irq_base);
+fail_release_region:
+ release_mem_region(res->start, resource_size(res));
+fail_free_msic:
+ kfree(msic);
+
+ return ret;
+}
+
+static int __devexit intel_msic_remove(struct platform_device *pdev)
+{
+ struct intel_msic *msic = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ intel_msic_remove_devices(msic);
+ platform_set_drvdata(pdev, NULL);
+ iounmap(msic->irq_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(msic);
+
+ return 0;
+}
+
+static struct platform_driver intel_msic_driver = {
+ .probe = intel_msic_probe,
+ .remove = __devexit_p(intel_msic_remove),
+ .driver = {
+ .name = "intel_msic",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init intel_msic_init(void)
+{
+ return platform_driver_register(&intel_msic_driver);
+}
+module_init(intel_msic_init);
+
+static void __exit intel_msic_exit(void)
+{
+ platform_driver_unregister(&intel_msic_driver);
+}
+module_exit(intel_msic_exit);
+
+MODULE_DESCRIPTION("Driver for Intel MSIC");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index 563654c9b19e..1e9ee533eacb 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -328,7 +328,7 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
return 0;
}
-struct platform_driver jz4740_adc_driver = {
+static struct platform_driver jz4740_adc_driver = {
.probe = jz4740_adc_probe,
.remove = __devexit_p(jz4740_adc_remove),
.driver = {
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index f83103b8970d..dc58750bb71b 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -23,6 +23,7 @@
#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>
@@ -142,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
max8997->irq_base = pdata->irq_base;
max8997->ono = pdata->ono;
- max8997->wakeup = pdata->wakeup;
mutex_init(&max8997->iolock);
@@ -169,6 +169,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err_mfd;
+ /* MAX8997 has a power button input. */
+ device_init_wakeup(max8997->dev, pdata->wakeup);
+
return ret;
err_mfd:
@@ -398,7 +401,29 @@ static int max8997_restore(struct device *dev)
return 0;
}
+static int max8997_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ irq_set_irq_wake(max8997->irq, 1);
+ return 0;
+}
+
+static int max8997_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ irq_set_irq_wake(max8997->irq, 0);
+ return max8997_irq_resume(max8997);
+}
+
const struct dev_pm_ops max8997_pm = {
+ .suspend = max8997_suspend,
+ .resume = max8997_resume,
.freeze = max8997_freeze,
.restore = max8997_restore,
};
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 7e4d44bf92ab..e9619acc0237 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -26,20 +26,10 @@ struct mc13xxx {
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
void *irqdata[MC13XXX_NUM_IRQ];
-};
-
-struct mc13783 {
- struct mc13xxx mc13xxx;
int adcflags;
};
-struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783)
-{
- return &mc13783->mc13xxx;
-}
-EXPORT_SYMBOL(mc13783_to_mc13xxx);
-
#define MC13XXX_IRQSTAT0 0
#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1)
@@ -136,14 +126,14 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx);
#define MC13XXX_REVISION_FAB (0x03 << 11)
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
-#define MC13783_ADC1 44
-#define MC13783_ADC1_ADEN (1 << 0)
-#define MC13783_ADC1_RAND (1 << 1)
-#define MC13783_ADC1_ADSEL (1 << 3)
-#define MC13783_ADC1_ASC (1 << 20)
-#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+#define MC13XXX_ADC1 44
+#define MC13XXX_ADC1_ADEN (1 << 0)
+#define MC13XXX_ADC1_RAND (1 << 1)
+#define MC13XXX_ADC1_ADSEL (1 << 3)
+#define MC13XXX_ADC1_ASC (1 << 20)
+#define MC13XXX_ADC1_ADTRIGIGN (1 << 21)
-#define MC13783_ADC2 45
+#define MC13XXX_ADC2 45
#define MC13XXX_NUMREGS 0x3f
@@ -487,7 +477,7 @@ enum mc13xxx_id {
MC13XXX_ID_INVALID,
};
-const char *mc13xxx_chipname[] = {
+static const char *mc13xxx_chipname[] = {
[MC13XXX_ID_MC13783] = "mc13783",
[MC13XXX_ID_MC13892] = "mc13892",
};
@@ -558,8 +548,6 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
return mc13xxx_chipname[devid->driver_data];
}
-#include <linux/mfd/mc13783.h>
-
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
{
struct mc13xxx_platform_data *pdata =
@@ -569,15 +557,15 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
}
EXPORT_SYMBOL(mc13xxx_get_flags);
-#define MC13783_ADC1_CHAN0_SHIFT 5
-#define MC13783_ADC1_CHAN1_SHIFT 8
+#define MC13XXX_ADC1_CHAN0_SHIFT 5
+#define MC13XXX_ADC1_CHAN1_SHIFT 8
struct mc13xxx_adcdone_data {
struct mc13xxx *mc13xxx;
struct completion done;
};
-static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)
{
struct mc13xxx_adcdone_data *adcdone_data = data;
@@ -588,12 +576,11 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
return IRQ_HANDLED;
}
-#define MC13783_ADC_WORKING (1 << 0)
+#define MC13XXX_ADC_WORKING (1 << 0)
-int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
unsigned int channel, unsigned int *sample)
{
- struct mc13xxx *mc13xxx = &mc13783->mc13xxx;
u32 adc0, adc1, old_adc0;
int i, ret;
struct mc13xxx_adcdone_data adcdone_data = {
@@ -605,51 +592,51 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
mc13xxx_lock(mc13xxx);
- if (mc13783->adcflags & MC13783_ADC_WORKING) {
+ if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) {
ret = -EBUSY;
goto out;
}
- mc13783->adcflags |= MC13783_ADC_WORKING;
+ mc13xxx->adcflags |= MC13XXX_ADC_WORKING;
- mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0);
+ mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0);
- adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
- adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
+ adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2;
+ adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
if (channel > 7)
- adc1 |= MC13783_ADC1_ADSEL;
+ adc1 |= MC13XXX_ADC1_ADSEL;
switch (mode) {
- case MC13783_ADC_MODE_TS:
- adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
- MC13783_ADC0_TSMOD1;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ case MC13XXX_ADC_MODE_TS:
+ adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 |
+ MC13XXX_ADC0_TSMOD1;
+ adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
break;
- case MC13783_ADC_MODE_SINGLE_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
- adc1 |= MC13783_ADC1_RAND;
+ case MC13XXX_ADC_MODE_SINGLE_CHAN:
+ adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+ adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13XXX_ADC1_RAND;
break;
- case MC13783_ADC_MODE_MULT_CHAN:
- adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
- adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ case MC13XXX_ADC_MODE_MULT_CHAN:
+ adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+ adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
break;
default:
- mc13783_unlock(mc13783);
+ mc13xxx_unlock(mc13xxx);
return -EINVAL;
}
- dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
- mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
- mc13783_handler_adcdone, __func__, &adcdone_data);
- mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
+ dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
+ mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
+ mc13xxx_handler_adcdone, __func__, &adcdone_data);
+ mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
- mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0);
- mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1);
mc13xxx_unlock(mc13xxx);
@@ -660,27 +647,27 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
mc13xxx_lock(mc13xxx);
- mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data);
if (ret > 0)
for (i = 0; i < 4; ++i) {
ret = mc13xxx_reg_read(mc13xxx,
- MC13783_ADC2, &sample[i]);
+ MC13XXX_ADC2, &sample[i]);
if (ret)
break;
}
- if (mode == MC13783_ADC_MODE_TS)
+ if (mode == MC13XXX_ADC_MODE_TS)
/* restore TSMOD */
- mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0);
+ mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0);
- mc13783->adcflags &= ~MC13783_ADC_WORKING;
+ mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING;
out:
mc13xxx_unlock(mc13xxx);
return ret;
}
-EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
+EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion);
static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
const char *format, void *pdata, size_t pdata_size)
@@ -716,6 +703,11 @@ static int mc13xxx_probe(struct spi_device *spi)
enum mc13xxx_id id;
int ret;
+ if (!pdata) {
+ dev_err(&spi->dev, "invalid platform data\n");
+ return -EINVAL;
+ }
+
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
if (!mc13xxx)
return -ENOMEM;
@@ -763,10 +755,8 @@ err_revision:
if (pdata->flags & MC13XXX_USE_CODEC)
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
- if (pdata->flags & MC13XXX_USE_REGULATOR) {
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
- &pdata->regulators, sizeof(pdata->regulators));
- }
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
+ &pdata->regulators, sizeof(pdata->regulators));
if (pdata->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
@@ -774,10 +764,14 @@ err_revision:
if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
- if (pdata->flags & MC13XXX_USE_LED)
+ if (pdata->leds)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
pdata->leds, sizeof(*pdata->leds));
+ if (pdata->buttons)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
+ pdata->buttons, sizeof(*pdata->buttons));
+
return 0;
}
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index af5d9d061371..cb4910ac4d12 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -1226,7 +1226,7 @@ static int menelaus_probe(struct i2c_client *client,
menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
if (client->irq > 0) {
- err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
+ err = request_irq(client->irq, menelaus_irq, 0,
DRIVER_NAME, menelaus);
if (err) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 57868416c760..ff1a7e741ecd 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -23,45 +23,22 @@
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
#include <linux/mfd/pcf50633/core.h>
-static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
-{
- int ret;
-
- ret = i2c_smbus_read_i2c_block_data(pcf->i2c_client, reg,
- num, data);
- if (ret < 0)
- dev_err(pcf->dev, "Error reading %d regs at %d\n", num, reg);
-
- return ret;
-}
-
-static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
-{
- int ret;
-
- ret = i2c_smbus_write_i2c_block_data(pcf->i2c_client, reg,
- num, data);
- if (ret < 0)
- dev_err(pcf->dev, "Error writing %d regs at %d\n", num, reg);
-
- return ret;
-
-}
-
/* Read a block of up to 32 regs */
int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
int nr_regs, u8 *data)
{
int ret;
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, nr_regs, data);
- mutex_unlock(&pcf->lock);
+ ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
+ if (ret != 0)
+ return ret;
- return ret;
+ return nr_regs;
}
EXPORT_SYMBOL_GPL(pcf50633_read_block);
@@ -71,21 +48,22 @@ int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
{
int ret;
- mutex_lock(&pcf->lock);
- ret = __pcf50633_write(pcf, reg, nr_regs, data);
- mutex_unlock(&pcf->lock);
+ ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs);
+ if (ret != 0)
+ return ret;
- return ret;
+ return nr_regs;
}
EXPORT_SYMBOL_GPL(pcf50633_write_block);
u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
{
- u8 val;
+ unsigned int val;
+ int ret;
- mutex_lock(&pcf->lock);
- __pcf50633_read(pcf, reg, 1, &val);
- mutex_unlock(&pcf->lock);
+ ret = regmap_read(pcf->regmap, reg, &val);
+ if (ret < 0)
+ return -1;
return val;
}
@@ -93,56 +71,19 @@ EXPORT_SYMBOL_GPL(pcf50633_reg_read);
int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
{
- int ret;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_write(pcf, reg, 1, &val);
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_write(pcf->regmap, reg, val);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_write);
int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
{
- int ret;
- u8 tmp;
-
- val &= mask;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, 1, &tmp);
- if (ret < 0)
- goto out;
-
- tmp &= ~mask;
- tmp |= val;
- ret = __pcf50633_write(pcf, reg, 1, &tmp);
-
-out:
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_update_bits(pcf->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
{
- int ret;
- u8 tmp;
-
- mutex_lock(&pcf->lock);
- ret = __pcf50633_read(pcf, reg, 1, &tmp);
- if (ret < 0)
- goto out;
-
- tmp &= ~val;
- ret = __pcf50633_write(pcf, reg, 1, &tmp);
-
-out:
- mutex_unlock(&pcf->lock);
-
- return ret;
+ return regmap_update_bits(pcf->regmap, reg, val, 0);
}
EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
@@ -251,6 +192,11 @@ static int pcf50633_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
+static struct regmap_config pcf50633_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
static int __devinit pcf50633_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
@@ -272,16 +218,23 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
mutex_init(&pcf->lock);
+ pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
+ if (IS_ERR(pcf->regmap)) {
+ ret = PTR_ERR(pcf->regmap);
+ dev_err(pcf->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto err_free;
+ }
+
i2c_set_clientdata(client, pcf);
pcf->dev = &client->dev;
- pcf->i2c_client = client;
version = pcf50633_reg_read(pcf, 0);
variant = pcf50633_reg_read(pcf, 1);
if (version < 0 || variant < 0) {
dev_err(pcf->dev, "Unable to probe pcf50633\n");
ret = -ENODEV;
- goto err_free;
+ goto err_regmap;
}
dev_info(pcf->dev, "Probed device version %d variant %d\n",
@@ -328,6 +281,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
+err_regmap:
+ regmap_exit(pcf->regmap);
err_free:
kfree(pcf);
@@ -351,6 +306,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
platform_device_unregister(pcf->regulator_pdev[i]);
+ regmap_exit(pcf->regmap);
kfree(pcf);
return 0;
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index c27e515b0722..de979742c6fc 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -357,6 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
static int tc3589x_suspend(struct device *dev)
{
struct tc3589x *tc3589x = dev_get_drvdata(dev);
@@ -387,6 +388,7 @@ static int tc3589x_resume(struct device *dev)
static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
tc3589x_resume);
+#endif
static const struct i2c_device_id tc3589x_id[] = {
{ "tc3589x", 24 },
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 696879e2eef7..02d65692ceb4 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -697,7 +697,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
dev_err(&dev->dev, "The driver supports an older "
"version of the FPGA, please update the driver to "
"support %d.%d\n", priv->fw.major, priv->fw.minor);
- goto err_ioremap;
+ goto err_config;
}
if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
priv->fw.minor < TIMB_REQUIRED_MINOR) {
@@ -705,13 +705,13 @@ static int __devinit timb_probe(struct pci_dev *dev,
"please upgrade the FPGA to at least: %d.%d\n",
priv->fw.major, priv->fw.minor,
TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
- goto err_ioremap;
+ goto err_config;
}
msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
GFP_KERNEL);
if (!msix_entries)
- goto err_ioremap;
+ goto err_config;
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
msix_entries[i].entry = i;
@@ -825,6 +825,8 @@ err_mfd:
err_create_file:
pci_disable_msix(dev);
err_msix:
+ kfree(msix_entries);
+err_config:
iounmap(priv->ctl_membase);
err_ioremap:
release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
@@ -833,7 +835,6 @@ err_request:
err_start:
pci_disable_device(dev);
err_enable:
- kfree(msix_entries);
kfree(priv);
pci_set_drvdata(dev, NULL);
return -ENODEV;
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 955bc00e4b20..5fec23a9ac03 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -131,9 +131,6 @@ int tps65912_device_init(struct tps65912 *tps65912)
if (init_data == NULL)
return -ENOMEM;
- init_data->irq = pmic_plat_data->irq;
- init_data->irq_base = pmic_plat_data->irq;
-
mutex_init(&tps65912->io_mutex);
dev_set_drvdata(tps65912->dev, tps65912);
@@ -153,10 +150,13 @@ int tps65912_device_init(struct tps65912 *tps65912)
if (ret < 0)
goto err;
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
if (ret < 0)
goto err;
+ kfree(init_data);
return ret;
err:
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 01ecfeee6524..b8eef462737a 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -109,7 +109,7 @@
#define twl_has_watchdog() false
#endif
-#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
+#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\
defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
#define twl_has_codec() true
#else
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 8a7ee3139b86..f062c8cc6c38 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -30,7 +30,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/i2c/twl.h>
@@ -278,59 +277,6 @@ static const struct sih sih_modules_twl5031[8] = {
static unsigned twl4030_irq_base;
-static struct completion irq_event;
-
-/*
- * This thread processes interrupts reported by the Primary Interrupt Handler.
- */
-static int twl4030_irq_thread(void *data)
-{
- long irq = (long)data;
- static unsigned i2c_errors;
- static const unsigned max_i2c_errors = 100;
-
-
- current->flags |= PF_NOFREEZE;
-
- while (!kthread_should_stop()) {
- int ret;
- int module_irq;
- u8 pih_isr;
-
- /* Wait for IRQ, then read PIH irq status (also blocking) */
- wait_for_completion_interruptible(&irq_event);
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
- REG_PIH_ISR_P1);
- if (ret) {
- pr_warning("twl4030: I2C error %d reading PIH ISR\n",
- ret);
- if (++i2c_errors >= max_i2c_errors) {
- printk(KERN_ERR "Maximum I2C error count"
- " exceeded. Terminating %s.\n",
- __func__);
- break;
- }
- complete(&irq_event);
- continue;
- }
-
- /* these handlers deal with the relevant SIH irq status */
- local_irq_disable();
- for (module_irq = twl4030_irq_base;
- pih_isr;
- pih_isr >>= 1, module_irq++) {
- if (pih_isr & 0x1)
- generic_handle_irq(module_irq);
- }
- local_irq_enable();
-
- enable_irq(irq);
- }
-
- return 0;
-}
-
/*
* handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
* This is a chained interrupt, so there is no desc->action method for it.
@@ -342,9 +288,25 @@ static int twl4030_irq_thread(void *data)
*/
static irqreturn_t handle_twl4030_pih(int irq, void *devid)
{
- /* Acknowledge, clear *AND* mask the interrupt... */
- disable_irq_nosync(irq);
- complete(devid);
+ int module_irq;
+ irqreturn_t ret;
+ u8 pih_isr;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+ REG_PIH_ISR_P1);
+ if (ret) {
+ pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* these handlers deal with the relevant SIH irq status */
+ for (module_irq = twl4030_irq_base;
+ pih_isr;
+ pih_isr >>= 1, module_irq++) {
+ if (pih_isr & 0x1)
+ handle_nested_irq(module_irq);
+ }
+
return IRQ_HANDLED;
}
/*----------------------------------------------------------------------*/
@@ -460,113 +422,17 @@ static inline void activate_irq(int irq)
/*----------------------------------------------------------------------*/
-static DEFINE_SPINLOCK(sih_agent_lock);
-
-static struct workqueue_struct *wq;
-
struct sih_agent {
int irq_base;
const struct sih *sih;
u32 imr;
bool imr_change_pending;
- struct work_struct mask_work;
-
- u32 edge_change;
- struct work_struct edge_work;
-};
-
-static void twl4030_sih_do_mask(struct work_struct *work)
-{
- struct sih_agent *agent;
- const struct sih *sih;
- union {
- u8 bytes[4];
- u32 word;
- } imr;
- int status;
- agent = container_of(work, struct sih_agent, mask_work);
-
- /* see what work we have */
- spin_lock_irq(&sih_agent_lock);
- if (agent->imr_change_pending) {
- sih = agent->sih;
- /* byte[0] gets overwritten as we write ... */
- imr.word = cpu_to_le32(agent->imr << 8);
- agent->imr_change_pending = false;
- } else
- sih = NULL;
- spin_unlock_irq(&sih_agent_lock);
- if (!sih)
- return;
-
- /* write the whole mask ... simpler than subsetting it */
- status = twl_i2c_write(sih->module, imr.bytes,
- sih->mask[irq_line].imr_offset, sih->bytes_ixr);
- if (status)
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "write", status);
-}
-
-static void twl4030_sih_do_edge(struct work_struct *work)
-{
- struct sih_agent *agent;
- const struct sih *sih;
- u8 bytes[6];
u32 edge_change;
- int status;
-
- agent = container_of(work, struct sih_agent, edge_work);
-
- /* see what work we have */
- spin_lock_irq(&sih_agent_lock);
- edge_change = agent->edge_change;
- agent->edge_change = 0;
- sih = edge_change ? agent->sih : NULL;
- spin_unlock_irq(&sih_agent_lock);
- if (!sih)
- return;
-
- /* Read, reserving first byte for write scratch. Yes, this
- * could be cached for some speedup ... but be careful about
- * any processor on the other IRQ line, EDR registers are
- * shared.
- */
- status = twl_i2c_read(sih->module, bytes + 1,
- sih->edr_offset, sih->bytes_edr);
- if (status) {
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "read", status);
- return;
- }
-
- /* Modify only the bits we know must change */
- while (edge_change) {
- int i = fls(edge_change) - 1;
- struct irq_data *idata = irq_get_irq_data(i + agent->irq_base);
- int byte = 1 + (i >> 2);
- int off = (i & 0x3) * 2;
- unsigned int type;
-
- bytes[byte] &= ~(0x03 << off);
- type = irqd_get_trigger_type(idata);
- if (type & IRQ_TYPE_EDGE_RISING)
- bytes[byte] |= BIT(off + 1);
- if (type & IRQ_TYPE_EDGE_FALLING)
- bytes[byte] |= BIT(off + 0);
-
- edge_change &= ~BIT(i);
- }
-
- /* Write */
- status = twl_i2c_write(sih->module, bytes,
- sih->edr_offset, sih->bytes_edr);
- if (status)
- pr_err("twl4030: %s, %s --> %d\n", __func__,
- "write", status);
-}
+ struct mutex irq_lock;
+};
/*----------------------------------------------------------------------*/
@@ -579,50 +445,125 @@ static void twl4030_sih_do_edge(struct work_struct *work)
static void twl4030_sih_mask(struct irq_data *data)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
-
- spin_lock_irqsave(&sih_agent_lock, flags);
- sih->imr |= BIT(data->irq - sih->irq_base);
- sih->imr_change_pending = true;
- queue_work(wq, &sih->mask_work);
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ agent->imr |= BIT(data->irq - agent->irq_base);
+ agent->imr_change_pending = true;
}
static void twl4030_sih_unmask(struct irq_data *data)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
-
- spin_lock_irqsave(&sih_agent_lock, flags);
- sih->imr &= ~BIT(data->irq - sih->irq_base);
- sih->imr_change_pending = true;
- queue_work(wq, &sih->mask_work);
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ agent->imr &= ~BIT(data->irq - agent->irq_base);
+ agent->imr_change_pending = true;
}
static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger)
{
- struct sih_agent *sih = irq_data_get_irq_chip_data(data);
- unsigned long flags;
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
return -EINVAL;
- spin_lock_irqsave(&sih_agent_lock, flags);
- if (irqd_get_trigger_type(data) != trigger) {
- sih->edge_change |= BIT(data->irq - sih->irq_base);
- queue_work(wq, &sih->edge_work);
- }
- spin_unlock_irqrestore(&sih_agent_lock, flags);
+ if (irqd_get_trigger_type(data) != trigger)
+ agent->edge_change |= BIT(data->irq - agent->irq_base);
+
return 0;
}
+static void twl4030_sih_bus_lock(struct irq_data *data)
+{
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&agent->irq_lock);
+}
+
+static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
+{
+ struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+ const struct sih *sih = agent->sih;
+ int status;
+
+ if (agent->imr_change_pending) {
+ union {
+ u32 word;
+ u8 bytes[4];
+ } imr;
+
+ /* byte[0] gets overwriten as we write ... */
+ imr.word = cpu_to_le32(agent->imr << 8);
+ agent->imr_change_pending = false;
+
+ /* write the whole mask ... simpler than subsetting it */
+ status = twl_i2c_write(sih->module, imr.bytes,
+ sih->mask[irq_line].imr_offset,
+ sih->bytes_ixr);
+ if (status)
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "write", status);
+ }
+
+ if (agent->edge_change) {
+ u32 edge_change;
+ u8 bytes[6];
+
+ edge_change = agent->edge_change;
+ agent->edge_change = 0;
+
+ /*
+ * Read, reserving first byte for write scratch. Yes, this
+ * could be cached for some speedup ... but be careful about
+ * any processor on the other IRQ line, EDR registers are
+ * shared.
+ */
+ status = twl_i2c_read(sih->module, bytes + 1,
+ sih->edr_offset, sih->bytes_edr);
+ if (status) {
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "read", status);
+ return;
+ }
+
+ /* Modify only the bits we know must change */
+ while (edge_change) {
+ int i = fls(edge_change) - 1;
+ struct irq_data *idata;
+ int byte = 1 + (i >> 2);
+ int off = (i & 0x3) * 2;
+ unsigned int type;
+
+ idata = irq_get_irq_data(i + agent->irq_base);
+
+ bytes[byte] &= ~(0x03 << off);
+
+ type = irqd_get_trigger_type(idata);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ bytes[byte] |= BIT(off + 1);
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ bytes[byte] |= BIT(off + 0);
+
+ edge_change &= ~BIT(i);
+ }
+
+ /* Write */
+ status = twl_i2c_write(sih->module, bytes,
+ sih->edr_offset, sih->bytes_edr);
+ if (status)
+ pr_err("twl4030: %s, %s --> %d\n", __func__,
+ "write", status);
+ }
+
+ mutex_unlock(&agent->irq_lock);
+}
+
static struct irq_chip twl4030_sih_irq_chip = {
.name = "twl4030",
- .irq_mask = twl4030_sih_mask,
+ .irq_mask = twl4030_sih_mask,
.irq_unmask = twl4030_sih_unmask,
.irq_set_type = twl4030_sih_set_type,
+ .irq_bus_lock = twl4030_sih_bus_lock,
+ .irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock,
};
/*----------------------------------------------------------------------*/
@@ -655,9 +596,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
int isr;
/* reading ISR acks the IRQs, using clear-on-read mode */
- local_irq_enable();
isr = sih_read_isr(sih);
- local_irq_disable();
if (isr < 0) {
pr_err("twl4030: %s SIH, read ISR error %d\n",
@@ -672,7 +611,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
isr &= ~BIT(irq);
if (irq < sih->bits)
- generic_handle_irq(agent->irq_base + irq);
+ handle_nested_irq(agent->irq_base + irq);
else
pr_err("twl4030: %s SIH, invalid ISR bit %d\n",
sih->name, irq);
@@ -718,15 +657,14 @@ int twl4030_sih_setup(int module)
agent->irq_base = irq_base;
agent->sih = sih;
agent->imr = ~0;
- INIT_WORK(&agent->mask_work, twl4030_sih_do_mask);
- INIT_WORK(&agent->edge_work, twl4030_sih_do_edge);
+ mutex_init(&agent->irq_lock);
for (i = 0; i < sih->bits; i++) {
irq = irq_base + i;
+ irq_set_chip_data(irq, agent);
irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip,
handle_edge_irq);
- irq_set_chip_data(irq, agent);
activate_irq(irq);
}
@@ -758,7 +696,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
int status;
int i;
- struct task_struct *task;
/*
* Mask and clear all TWL4030 interrupts since initially we do
@@ -768,12 +705,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
if (status < 0)
return status;
- wq = create_singlethread_workqueue("twl4030-irqchip");
- if (!wq) {
- pr_err("twl4030: workqueue FAIL\n");
- return -ESRCH;
- }
-
twl4030_irq_base = irq_base;
/* install an irq handler for each of the SIH modules;
@@ -787,6 +718,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
for (i = irq_base; i < irq_end; i++) {
irq_set_chip_and_handler(i, &twl4030_irq_chip,
handle_simple_irq);
+ irq_set_nested_thread(i, 1);
activate_irq(i);
}
twl4030_irq_next = i;
@@ -801,34 +733,22 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
}
/* install an irq handler to demultiplex the TWL4030 interrupt */
-
-
- init_completion(&irq_event);
-
- status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
- "TWL4030-PIH", &irq_event);
+ status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, 0,
+ "TWL4030-PIH", NULL);
if (status < 0) {
pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
goto fail_rqirq;
}
- task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
- "twl4030-irq");
- if (IS_ERR(task)) {
- pr_err("twl4030: could not create irq %d thread!\n", irq_num);
- status = PTR_ERR(task);
- goto fail_kthread;
- }
return status;
-fail_kthread:
- free_irq(irq_num, &irq_event);
fail_rqirq:
/* clean up twl4030_sih_setup */
fail:
- for (i = irq_base; i < irq_end; i++)
+ for (i = irq_base; i < irq_end; i++) {
+ irq_set_nested_thread(i, 0);
irq_set_chip_and_handler(i, NULL, NULL);
- destroy_workqueue(wq);
- wq = NULL;
+ }
+
return status;
}
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 7cbf2aa9e64f..834f824d3c11 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -740,6 +740,28 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev)
TWL4030_BCI_BCICTL1);
goto err_i2c;
}
+
+ /* Check that MADC clock is on */
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+
+ /* If MADC clk is not on, turn it on */
+ if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
+ dev_info(&pdev->dev, "clk disabled, enabling\n");
+ regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
+ TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+ }
+
platform_set_drvdata(pdev, madc);
mutex_init(&madc->lock);
ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index eb3b5f88e566..deec3ec858bf 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -37,6 +37,7 @@
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
+#include <linux/suspend.h>
#include "twl-core.h"
@@ -83,8 +84,48 @@ static int twl6030_interrupt_mapping[24] = {
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_base;
+static int twl_irq;
+static bool twl_irq_wake_enabled;
static struct completion irq_event;
+static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+
+static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *unused)
+{
+ int chained_wakeups;
+
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ chained_wakeups = atomic_read(&twl6030_wakeirqs);
+
+ if (chained_wakeups && !twl_irq_wake_enabled) {
+ if (enable_irq_wake(twl_irq))
+ pr_err("twl6030 IRQ wake enable failed\n");
+ else
+ twl_irq_wake_enabled = true;
+ } else if (!chained_wakeups && twl_irq_wake_enabled) {
+ disable_irq_wake(twl_irq);
+ twl_irq_wake_enabled = false;
+ }
+
+ disable_irq(twl_irq);
+ break;
+
+ case PM_POST_SUSPEND:
+ enable_irq(twl_irq);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block twl6030_irq_pm_notifier_block = {
+ .notifier_call = twl6030_irq_pm_notifier,
+};
/*
* This thread processes interrupts reported by the Primary Interrupt Handler.
@@ -187,6 +228,16 @@ static inline void activate_irq(int irq)
#endif
}
+int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ if (on)
+ atomic_inc(&twl6030_wakeirqs);
+ else
+ atomic_dec(&twl6030_wakeirqs);
+
+ return 0;
+}
+
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_next;
@@ -318,10 +369,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
twl6030_irq_chip = dummy_irq_chip;
twl6030_irq_chip.name = "twl6030";
twl6030_irq_chip.irq_set_type = NULL;
+ twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
for (i = irq_base; i < irq_end; i++) {
irq_set_chip_and_handler(i, &twl6030_irq_chip,
handle_simple_irq);
+ irq_set_chip_data(i, (void *)irq_num);
activate_irq(i);
}
@@ -331,6 +384,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
/* install an irq handler to demultiplex the TWL6030 interrupt */
init_completion(&irq_event);
+
+ status = request_irq(irq_num, handle_twl6030_pih, 0,
+ "TWL6030-PIH", &irq_event);
+ if (status < 0) {
+ pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
+ goto fail_irq;
+ }
+
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
if (IS_ERR(task)) {
pr_err("twl6030: could not create irq %d thread!\n", irq_num);
@@ -338,17 +399,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
goto fail_kthread;
}
- status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
- "TWL6030-PIH", &irq_event);
- if (status < 0) {
- pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
- goto fail_irq;
- }
+ twl_irq = irq_num;
+ register_pm_notifier(&twl6030_irq_pm_notifier_block);
return status;
-fail_irq:
- free_irq(irq_num, &irq_event);
fail_kthread:
+ free_irq(irq_num, &irq_event);
+
+fail_irq:
for (i = irq_base; i < irq_end; i++)
irq_set_chip_and_handler(i, NULL, NULL);
return status;
@@ -356,6 +414,7 @@ fail_kthread:
int twl6030_exit_irq(void)
{
+ unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
if (twl6030_irq_base) {
pr_err("twl6030: can't yet clean up IRQs?\n");
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index ada1835a5455..f4747a4a9a93 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -420,12 +420,19 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_RISING:
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_FALLING:
wm831x->gpio_update[irq] = 0x10000;
+ wm831x->gpio_level[irq] = false;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = true;
break;
default:
return -EINVAL;
@@ -449,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
unsigned int i;
- int primary, status_addr;
+ int primary, status_addr, ret;
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
@@ -507,6 +514,19 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
if (*status & wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
+
+ /* Simulate an edge triggered IRQ by polling the input
+ * status. This is sucky but improves interoperability.
+ */
+ if (primary == WM831X_GP_INT &&
+ wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
+ ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+ while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
+ handle_nested_irq(wm831x->irq_base + i);
+ ret = wm831x_reg_read(wm831x,
+ WM831X_GPIO_LEVEL);
+ }
+ }
}
out:
@@ -596,8 +616,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
"No interrupt specified - functionality limited\n");
}
-
-
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index b03be1d4e0ca..5d6ba132837e 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -217,6 +217,47 @@ static int wm8994_suspend(struct device *dev)
return 0;
}
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
+ WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
+ WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
+ WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
+ WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ switch (wm8994->type) {
+ case WM8958:
+ ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & WM8958_MICD_ENA) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Disable LDO pulldowns while the device is suspended if we
+ * don't know that something will be driving them. */
+ if (!wm8994->ldo_ena_always_driven)
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
+
/* GPIO configuration state is saved here since we may be configuring
* the GPIO alternate functions even if we're not using the gpiolib
* driver for them.
@@ -286,6 +327,11 @@ static int wm8994_resume(struct device *dev)
if (ret < 0)
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+ /* Disable LDO pulldowns while the device is active */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ 0);
+
wm8994->suspended = false;
return 0;
@@ -467,8 +513,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
pdata->gpio_defaults[i]);
}
}
+
+ wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
}
+ /* Disable LDO pulldowns while the device is active */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ 0);
+
/* In some system designs where the regulators are not in use,
* we can achieve a small reduction in leakage currents by
* floating LDO outputs. This bit makes no difference if the
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2d6423c2d193..d593878d66d0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -63,7 +63,7 @@ config AD525X_DPOT_SPI
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
- depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
+ depends on HAVE_CLK
help
This option enables device driver support for the PWM channels
on certain Atmel processors. Pulse Width Modulation is used for
@@ -506,5 +506,6 @@ source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
+source "drivers/misc/altera-stapl/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 8f3efb68a141..b26495a02554 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
+obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 4ff73c215746..a39e0555df63 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -98,6 +98,7 @@ static const struct i2c_device_id ad_dpot_id[] = {
{"ad5282", AD5282_ID},
{"adn2860", ADN2860_ID},
{"ad5273", AD5273_ID},
+ {"ad5161", AD5161_ID},
{"ad5171", AD5171_ID},
{"ad5170", AD5170_ID},
{"ad5172", AD5172_ID},
diff --git a/drivers/staging/altera-stapl/Kconfig b/drivers/misc/altera-stapl/Kconfig
index b6537321ed72..7f01d8e93992 100644
--- a/drivers/staging/altera-stapl/Kconfig
+++ b/drivers/misc/altera-stapl/Kconfig
@@ -1,3 +1,5 @@
+comment "Altera FPGA firmware download module"
+
config ALTERA_STAPL
tristate "Altera FPGA firmware download module"
depends on I2C
diff --git a/drivers/misc/altera-stapl/Makefile b/drivers/misc/altera-stapl/Makefile
new file mode 100644
index 000000000000..055f61ee781a
--- /dev/null
+++ b/drivers/misc/altera-stapl/Makefile
@@ -0,0 +1,3 @@
+altera-stapl-objs = altera-lpt.o altera-jtag.o altera-comp.o altera.o
+
+obj-$(CONFIG_ALTERA_STAPL) += altera-stapl.o
diff --git a/drivers/staging/altera-stapl/altera-comp.c b/drivers/misc/altera-stapl/altera-comp.c
index 49b103bedaaf..49b103bedaaf 100644
--- a/drivers/staging/altera-stapl/altera-comp.c
+++ b/drivers/misc/altera-stapl/altera-comp.c
diff --git a/drivers/staging/altera-stapl/altera-exprt.h b/drivers/misc/altera-stapl/altera-exprt.h
index 39c38d84a670..39c38d84a670 100644
--- a/drivers/staging/altera-stapl/altera-exprt.h
+++ b/drivers/misc/altera-stapl/altera-exprt.h
diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/misc/altera-stapl/altera-jtag.c
index 8b1620b1b2d0..f4bf20096972 100644
--- a/drivers/staging/altera-stapl/altera-jtag.c
+++ b/drivers/misc/altera-stapl/altera-jtag.c
@@ -26,7 +26,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/slab.h>
-#include "altera.h"
+#include <misc/altera.h>
#include "altera-exprt.h"
#include "altera-jtag.h"
diff --git a/drivers/staging/altera-stapl/altera-jtag.h b/drivers/misc/altera-stapl/altera-jtag.h
index 2f97e36a2fbc..2f97e36a2fbc 100644
--- a/drivers/staging/altera-stapl/altera-jtag.h
+++ b/drivers/misc/altera-stapl/altera-jtag.h
diff --git a/drivers/staging/altera-stapl/altera-lpt.c b/drivers/misc/altera-stapl/altera-lpt.c
index 91456a03612d..91456a03612d 100644
--- a/drivers/staging/altera-stapl/altera-lpt.c
+++ b/drivers/misc/altera-stapl/altera-lpt.c
diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c
index c2eff6a82db1..24272e022bec 100644
--- a/drivers/staging/altera-stapl/altera.c
+++ b/drivers/misc/altera-stapl/altera.c
@@ -29,7 +29,7 @@
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include "altera.h"
+#include <misc/altera.h>
#include "altera-exprt.h"
#include "altera-jtag.h"
diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c
index 27dc0d21aafa..f6586d53e1a3 100644
--- a/drivers/misc/fsa9480.c
+++ b/drivers/misc/fsa9480.c
@@ -400,7 +400,8 @@ static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
return ret;
}
- device_init_wakeup(&client->dev, pdata->wakeup);
+ if (pdata)
+ device_init_wakeup(&client->dev, pdata->wakeup);
}
return 0;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index 8b51cd62d067..29d12a70eb1b 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -163,7 +163,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
int i;
if (lis3->blkread) {
- if (lis3_dev.whoami == WAI_12B) {
+ if (lis3->whoami == WAI_12B) {
u16 data[3];
lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);
for (i = 0; i < 3; i++)
@@ -195,18 +195,30 @@ static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
/* ODR is Output Data Rate */
-static int lis3lv02d_get_odr(void)
+static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
{
u8 ctrl;
int shift;
- lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
- ctrl &= lis3_dev.odr_mask;
- shift = ffs(lis3_dev.odr_mask) - 1;
- return lis3_dev.odrs[(ctrl >> shift)];
+ lis3->read(lis3, CTRL_REG1, &ctrl);
+ ctrl &= lis3->odr_mask;
+ shift = ffs(lis3->odr_mask) - 1;
+ return lis3->odrs[(ctrl >> shift)];
}
-static int lis3lv02d_set_odr(int rate)
+static int lis3lv02d_get_pwron_wait(struct lis3lv02d *lis3)
+{
+ int div = lis3lv02d_get_odr(lis3);
+
+ if (WARN_ONCE(div == 0, "device returned spurious data"))
+ return -ENXIO;
+
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / div);
+ return 0;
+}
+
+static int lis3lv02d_set_odr(struct lis3lv02d *lis3, int rate)
{
u8 ctrl;
int i, len, shift;
@@ -214,14 +226,14 @@ static int lis3lv02d_set_odr(int rate)
if (!rate)
return -EINVAL;
- lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
- ctrl &= ~lis3_dev.odr_mask;
- len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
- shift = ffs(lis3_dev.odr_mask) - 1;
+ lis3->read(lis3, CTRL_REG1, &ctrl);
+ ctrl &= ~lis3->odr_mask;
+ len = 1 << hweight_long(lis3->odr_mask); /* # of possible values */
+ shift = ffs(lis3->odr_mask) - 1;
for (i = 0; i < len; i++)
- if (lis3_dev.odrs[i] == rate) {
- lis3_dev.write(&lis3_dev, CTRL_REG1,
+ if (lis3->odrs[i] == rate) {
+ lis3->write(lis3, CTRL_REG1,
ctrl | (i << shift));
return 0;
}
@@ -240,12 +252,12 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
mutex_lock(&lis3->mutex);
irq_cfg = lis3->irq_cfg;
- if (lis3_dev.whoami == WAI_8B) {
+ if (lis3->whoami == WAI_8B) {
lis3->data_ready_count[IRQ_LINE0] = 0;
lis3->data_ready_count[IRQ_LINE1] = 0;
/* Change interrupt cfg to data ready for selftest */
- atomic_inc(&lis3_dev.wake_thread);
+ atomic_inc(&lis3->wake_thread);
lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;
lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);
lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &
@@ -253,12 +265,12 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
- if (lis3_dev.whoami == WAI_3DC) {
+ if (lis3->whoami == WAI_3DC) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
ctlreg = CTRL_REG1;
- if (lis3_dev.whoami == WAI_12B)
+ if (lis3->whoami == WAI_12B)
selftest = CTRL1_ST;
else
selftest = CTRL1_STP;
@@ -266,7 +278,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
lis3->read(lis3, ctlreg, &reg);
lis3->write(lis3, ctlreg, (reg | selftest));
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
/* Read directly to avoid axis remap */
x = lis3->read_data(lis3, OUTX);
@@ -275,7 +289,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
/* back to normal settings */
lis3->write(lis3, ctlreg, reg);
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
results[0] = x - lis3->read_data(lis3, OUTX);
results[1] = y - lis3->read_data(lis3, OUTY);
@@ -283,9 +299,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
ret = 0;
- if (lis3_dev.whoami == WAI_8B) {
+ if (lis3->whoami == WAI_8B) {
/* Restore original interrupt configuration */
- atomic_dec(&lis3_dev.wake_thread);
+ atomic_dec(&lis3->wake_thread);
lis3->write(lis3, CTRL_REG3, ctrl_reg_data);
lis3->irq_cfg = irq_cfg;
@@ -363,8 +379,9 @@ void lis3lv02d_poweroff(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
-void lis3lv02d_poweron(struct lis3lv02d *lis3)
+int lis3lv02d_poweron(struct lis3lv02d *lis3)
{
+ int err;
u8 reg;
lis3->init(lis3);
@@ -384,35 +401,41 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->write(lis3, CTRL_REG2, reg);
}
- /* LIS3 power on delay is quite long */
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ err = lis3lv02d_get_pwron_wait(lis3);
+ if (err)
+ return err;
if (lis3->reg_ctrl)
lis3_context_restore(lis3);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
{
+ struct lis3lv02d *lis3 = pidev->private;
int x, y, z;
- mutex_lock(&lis3_dev.mutex);
- lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
+ mutex_lock(&lis3->mutex);
+ lis3lv02d_get_xyz(lis3, &x, &y, &z);
input_report_abs(pidev->input, ABS_X, x);
input_report_abs(pidev->input, ABS_Y, y);
input_report_abs(pidev->input, ABS_Z, z);
input_sync(pidev->input);
- mutex_unlock(&lis3_dev.mutex);
+ mutex_unlock(&lis3->mutex);
}
static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
{
- if (lis3_dev.pm_dev)
- pm_runtime_get_sync(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = pidev->private;
- if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)
- atomic_set(&lis3_dev.wake_thread, 1);
+ if (lis3->pm_dev)
+ pm_runtime_get_sync(lis3->pm_dev);
+
+ if (lis3->pdata && lis3->whoami == WAI_8B && lis3->idev)
+ atomic_set(&lis3->wake_thread, 1);
/*
* Update coordinates for the case where poll interval is 0 and
* the chip in running purely under interrupt control
@@ -422,14 +445,18 @@ static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
{
- atomic_set(&lis3_dev.wake_thread, 0);
- if (lis3_dev.pm_dev)
- pm_runtime_put(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = pidev->private;
+
+ atomic_set(&lis3->wake_thread, 0);
+ if (lis3->pm_dev)
+ pm_runtime_put(lis3->pm_dev);
}
-static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
+static irqreturn_t lis302dl_interrupt(int irq, void *data)
{
- if (!test_bit(0, &lis3_dev.misc_opened))
+ struct lis3lv02d *lis3 = data;
+
+ if (!test_bit(0, &lis3->misc_opened))
goto out;
/*
@@ -437,12 +464,12 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
* the lid is closed. This leads to interrupts as soon as a little move
* is done.
*/
- atomic_inc(&lis3_dev.count);
+ atomic_inc(&lis3->count);
- wake_up_interruptible(&lis3_dev.misc_wait);
- kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
+ wake_up_interruptible(&lis3->misc_wait);
+ kill_fasync(&lis3->async_queue, SIGIO, POLL_IN);
out:
- if (atomic_read(&lis3_dev.wake_thread))
+ if (atomic_read(&lis3->wake_thread))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
@@ -514,28 +541,37 @@ static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
{
- if (test_and_set_bit(0, &lis3_dev.misc_opened))
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ if (test_and_set_bit(0, &lis3->misc_opened))
return -EBUSY; /* already open */
- if (lis3_dev.pm_dev)
- pm_runtime_get_sync(lis3_dev.pm_dev);
+ if (lis3->pm_dev)
+ pm_runtime_get_sync(lis3->pm_dev);
- atomic_set(&lis3_dev.count, 0);
+ atomic_set(&lis3->count, 0);
return 0;
}
static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
{
- fasync_helper(-1, file, 0, &lis3_dev.async_queue);
- clear_bit(0, &lis3_dev.misc_opened); /* release the device */
- if (lis3_dev.pm_dev)
- pm_runtime_put(lis3_dev.pm_dev);
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ fasync_helper(-1, file, 0, &lis3->async_queue);
+ clear_bit(0, &lis3->misc_opened); /* release the device */
+ if (lis3->pm_dev)
+ pm_runtime_put(lis3->pm_dev);
return 0;
}
static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
DECLARE_WAITQUEUE(wait, current);
u32 data;
unsigned char byte_data;
@@ -544,10 +580,10 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
if (count < 1)
return -EINVAL;
- add_wait_queue(&lis3_dev.misc_wait, &wait);
+ add_wait_queue(&lis3->misc_wait, &wait);
while (true) {
set_current_state(TASK_INTERRUPTIBLE);
- data = atomic_xchg(&lis3_dev.count, 0);
+ data = atomic_xchg(&lis3->count, 0);
if (data)
break;
@@ -577,22 +613,28 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
out:
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&lis3_dev.misc_wait, &wait);
+ remove_wait_queue(&lis3->misc_wait, &wait);
return retval;
}
static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait)
{
- poll_wait(file, &lis3_dev.misc_wait, wait);
- if (atomic_read(&lis3_dev.count))
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ poll_wait(file, &lis3->misc_wait, wait);
+ if (atomic_read(&lis3->count))
return POLLIN | POLLRDNORM;
return 0;
}
static int lis3lv02d_misc_fasync(int fd, struct file *file, int on)
{
- return fasync_helper(fd, file, on, &lis3_dev.async_queue);
+ struct lis3lv02d *lis3 = container_of(file->private_data,
+ struct lis3lv02d, miscdev);
+
+ return fasync_helper(fd, file, on, &lis3->async_queue);
}
static const struct file_operations lis3lv02d_misc_fops = {
@@ -605,85 +647,80 @@ static const struct file_operations lis3lv02d_misc_fops = {
.fasync = lis3lv02d_misc_fasync,
};
-static struct miscdevice lis3lv02d_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "freefall",
- .fops = &lis3lv02d_misc_fops,
-};
-
-int lis3lv02d_joystick_enable(void)
+int lis3lv02d_joystick_enable(struct lis3lv02d *lis3)
{
struct input_dev *input_dev;
int err;
int max_val, fuzz, flat;
int btns[] = {BTN_X, BTN_Y, BTN_Z};
- if (lis3_dev.idev)
+ if (lis3->idev)
return -EINVAL;
- lis3_dev.idev = input_allocate_polled_device();
- if (!lis3_dev.idev)
+ lis3->idev = input_allocate_polled_device();
+ if (!lis3->idev)
return -ENOMEM;
- lis3_dev.idev->poll = lis3lv02d_joystick_poll;
- lis3_dev.idev->open = lis3lv02d_joystick_open;
- lis3_dev.idev->close = lis3lv02d_joystick_close;
- lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
- lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
- lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
- input_dev = lis3_dev.idev->input;
+ lis3->idev->poll = lis3lv02d_joystick_poll;
+ lis3->idev->open = lis3lv02d_joystick_open;
+ lis3->idev->close = lis3lv02d_joystick_close;
+ lis3->idev->poll_interval = MDPS_POLL_INTERVAL;
+ lis3->idev->poll_interval_min = MDPS_POLL_MIN;
+ lis3->idev->poll_interval_max = MDPS_POLL_MAX;
+ lis3->idev->private = lis3;
+ input_dev = lis3->idev->input;
input_dev->name = "ST LIS3LV02DL Accelerometer";
input_dev->phys = DRIVER_NAME "/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0;
- input_dev->dev.parent = &lis3_dev.pdev->dev;
+ input_dev->dev.parent = &lis3->pdev->dev;
set_bit(EV_ABS, input_dev->evbit);
- max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
- if (lis3_dev.whoami == WAI_12B) {
+ max_val = (lis3->mdps_max_val * lis3->scale) / LIS3_ACCURACY;
+ if (lis3->whoami == WAI_12B) {
fuzz = LIS3_DEFAULT_FUZZ_12B;
flat = LIS3_DEFAULT_FLAT_12B;
} else {
fuzz = LIS3_DEFAULT_FUZZ_8B;
flat = LIS3_DEFAULT_FLAT_8B;
}
- fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;
- flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;
+ fuzz = (fuzz * lis3->scale) / LIS3_ACCURACY;
+ flat = (flat * lis3->scale) / LIS3_ACCURACY;
input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
- lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns);
- lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns);
- lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns);
+ lis3->mapped_btns[0] = lis3lv02d_get_axis(abs(lis3->ac.x), btns);
+ lis3->mapped_btns[1] = lis3lv02d_get_axis(abs(lis3->ac.y), btns);
+ lis3->mapped_btns[2] = lis3lv02d_get_axis(abs(lis3->ac.z), btns);
- err = input_register_polled_device(lis3_dev.idev);
+ err = input_register_polled_device(lis3->idev);
if (err) {
- input_free_polled_device(lis3_dev.idev);
- lis3_dev.idev = NULL;
+ input_free_polled_device(lis3->idev);
+ lis3->idev = NULL;
}
return err;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
-void lis3lv02d_joystick_disable(void)
+void lis3lv02d_joystick_disable(struct lis3lv02d *lis3)
{
- if (lis3_dev.irq)
- free_irq(lis3_dev.irq, &lis3_dev);
- if (lis3_dev.pdata && lis3_dev.pdata->irq2)
- free_irq(lis3_dev.pdata->irq2, &lis3_dev);
+ if (lis3->irq)
+ free_irq(lis3->irq, lis3);
+ if (lis3->pdata && lis3->pdata->irq2)
+ free_irq(lis3->pdata->irq2, lis3);
- if (!lis3_dev.idev)
+ if (!lis3->idev)
return;
- if (lis3_dev.irq)
- misc_deregister(&lis3lv02d_misc_device);
- input_unregister_polled_device(lis3_dev.idev);
- input_free_polled_device(lis3_dev.idev);
- lis3_dev.idev = NULL;
+ if (lis3->irq)
+ misc_deregister(&lis3->miscdev);
+ input_unregister_polled_device(lis3->idev);
+ input_free_polled_device(lis3->idev);
+ lis3->idev = NULL;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
@@ -708,6 +745,7 @@ static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)
static ssize_t lis3lv02d_selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
s16 values[3];
static const char ok[] = "OK";
@@ -715,8 +753,8 @@ static ssize_t lis3lv02d_selftest_show(struct device *dev,
static const char irq[] = "FAIL_IRQ";
const char *res;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- switch (lis3lv02d_selftest(&lis3_dev, values)) {
+ lis3lv02d_sysfs_poweron(lis3);
+ switch (lis3lv02d_selftest(lis3, values)) {
case SELFTEST_FAIL:
res = fail;
break;
@@ -735,33 +773,37 @@ static ssize_t lis3lv02d_selftest_show(struct device *dev,
static ssize_t lis3lv02d_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
int x, y, z;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- mutex_lock(&lis3_dev.mutex);
- lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
- mutex_unlock(&lis3_dev.mutex);
+ lis3lv02d_sysfs_poweron(lis3);
+ mutex_lock(&lis3->mutex);
+ lis3lv02d_get_xyz(lis3, &x, &y, &z);
+ mutex_unlock(&lis3->mutex);
return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
}
static ssize_t lis3lv02d_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- lis3lv02d_sysfs_poweron(&lis3_dev);
- return sprintf(buf, "%d\n", lis3lv02d_get_odr());
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
+
+ lis3lv02d_sysfs_poweron(lis3);
+ return sprintf(buf, "%d\n", lis3lv02d_get_odr(lis3));
}
static ssize_t lis3lv02d_rate_set(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
+ struct lis3lv02d *lis3 = dev_get_drvdata(dev);
unsigned long rate;
if (strict_strtoul(buf, 0, &rate))
return -EINVAL;
- lis3lv02d_sysfs_poweron(&lis3_dev);
- if (lis3lv02d_set_odr(rate))
+ lis3lv02d_sysfs_poweron(lis3);
+ if (lis3lv02d_set_odr(lis3, rate))
return -EINVAL;
return count;
@@ -790,6 +832,7 @@ static int lis3lv02d_add_fs(struct lis3lv02d *lis3)
if (IS_ERR(lis3->pdev))
return PTR_ERR(lis3->pdev);
+ platform_set_drvdata(lis3->pdev, lis3);
return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
}
@@ -803,7 +846,7 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
/* SYSFS may have left chip running. Turn off if necessary */
if (!pm_runtime_suspended(lis3->pm_dev))
- lis3lv02d_poweroff(&lis3_dev);
+ lis3lv02d_poweroff(lis3);
pm_runtime_disable(lis3->pm_dev);
pm_runtime_set_suspended(lis3->pm_dev);
@@ -813,24 +856,24 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
-static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
+static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
struct lis3lv02d_platform_data *p)
{
int err;
int ctrl2 = p->hipass_ctrl;
if (p->click_flags) {
- dev->write(dev, CLICK_CFG, p->click_flags);
- dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
- dev->write(dev, CLICK_LATENCY, p->click_latency);
- dev->write(dev, CLICK_WINDOW, p->click_window);
- dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);
- dev->write(dev, CLICK_THSY_X,
+ lis3->write(lis3, CLICK_CFG, p->click_flags);
+ lis3->write(lis3, CLICK_TIMELIMIT, p->click_time_limit);
+ lis3->write(lis3, CLICK_LATENCY, p->click_latency);
+ lis3->write(lis3, CLICK_WINDOW, p->click_window);
+ lis3->write(lis3, CLICK_THSZ, p->click_thresh_z & 0xf);
+ lis3->write(lis3, CLICK_THSY_X,
(p->click_thresh_x & 0xf) |
(p->click_thresh_y << 4));
- if (dev->idev) {
- struct input_dev *input_dev = lis3_dev.idev->input;
+ if (lis3->idev) {
+ struct input_dev *input_dev = lis3->idev->input;
input_set_capability(input_dev, EV_KEY, BTN_X);
input_set_capability(input_dev, EV_KEY, BTN_Y);
input_set_capability(input_dev, EV_KEY, BTN_Z);
@@ -838,22 +881,22 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
}
if (p->wakeup_flags) {
- dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
- dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
+ lis3->write(lis3, FF_WU_CFG_1, p->wakeup_flags);
+ lis3->write(lis3, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
/* pdata value + 1 to keep this backward compatible*/
- dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);
+ lis3->write(lis3, FF_WU_DURATION_1, p->duration1 + 1);
ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
}
if (p->wakeup_flags2) {
- dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
- dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
+ lis3->write(lis3, FF_WU_CFG_2, p->wakeup_flags2);
+ lis3->write(lis3, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
/* pdata value + 1 to keep this backward compatible*/
- dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);
+ lis3->write(lis3, FF_WU_DURATION_2, p->duration2 + 1);
ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
}
/* Configure hipass filters */
- dev->write(dev, CTRL_REG2, ctrl2);
+ lis3->write(lis3, CTRL_REG2, ctrl2);
if (p->irq2) {
err = request_threaded_irq(p->irq2,
@@ -861,7 +904,7 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
lis302dl_interrupt_thread2_8b,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
(p->irq_flags2 & IRQF_TRIGGER_MASK),
- DRIVER_NAME, &lis3_dev);
+ DRIVER_NAME, lis3);
if (err < 0)
pr_err("No second IRQ. Limited functionality\n");
}
@@ -871,93 +914,97 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
* Initialise the accelerometer and the various subsystems.
* Should be rather independent of the bus system.
*/
-int lis3lv02d_init_device(struct lis3lv02d *dev)
+int lis3lv02d_init_device(struct lis3lv02d *lis3)
{
int err;
irq_handler_t thread_fn;
int irq_flags = 0;
- dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
+ lis3->whoami = lis3lv02d_read_8(lis3, WHO_AM_I);
- switch (dev->whoami) {
+ switch (lis3->whoami) {
case WAI_12B:
pr_info("12 bits sensor found\n");
- dev->read_data = lis3lv02d_read_12;
- dev->mdps_max_val = 2048;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
- dev->odrs = lis3_12_rates;
- dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
- dev->scale = LIS3_SENSITIVITY_12B;
- dev->regs = lis3_wai12_regs;
- dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);
+ lis3->read_data = lis3lv02d_read_12;
+ lis3->mdps_max_val = 2048;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
+ lis3->odrs = lis3_12_rates;
+ lis3->odr_mask = CTRL1_DF0 | CTRL1_DF1;
+ lis3->scale = LIS3_SENSITIVITY_12B;
+ lis3->regs = lis3_wai12_regs;
+ lis3->regs_size = ARRAY_SIZE(lis3_wai12_regs);
break;
case WAI_8B:
pr_info("8 bits sensor found\n");
- dev->read_data = lis3lv02d_read_8;
- dev->mdps_max_val = 128;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
- dev->odrs = lis3_8_rates;
- dev->odr_mask = CTRL1_DR;
- dev->scale = LIS3_SENSITIVITY_8B;
- dev->regs = lis3_wai8_regs;
- dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);
+ lis3->read_data = lis3lv02d_read_8;
+ lis3->mdps_max_val = 128;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_8_rates;
+ lis3->odr_mask = CTRL1_DR;
+ lis3->scale = LIS3_SENSITIVITY_8B;
+ lis3->regs = lis3_wai8_regs;
+ lis3->regs_size = ARRAY_SIZE(lis3_wai8_regs);
break;
case WAI_3DC:
pr_info("8 bits 3DC sensor found\n");
- dev->read_data = lis3lv02d_read_8;
- dev->mdps_max_val = 128;
- dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
- dev->odrs = lis3_3dc_rates;
- dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
- dev->scale = LIS3_SENSITIVITY_8B;
+ lis3->read_data = lis3lv02d_read_8;
+ lis3->mdps_max_val = 128;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_3dc_rates;
+ lis3->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
+ lis3->scale = LIS3_SENSITIVITY_8B;
break;
default:
- pr_err("unknown sensor type 0x%X\n", dev->whoami);
+ pr_err("unknown sensor type 0x%X\n", lis3->whoami);
return -EINVAL;
}
- dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
+ lis3->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
sizeof(lis3_wai12_regs)), GFP_KERNEL);
- if (dev->reg_cache == NULL) {
+ if (lis3->reg_cache == NULL) {
printk(KERN_ERR DRIVER_NAME "out of memory\n");
return -ENOMEM;
}
- mutex_init(&dev->mutex);
- atomic_set(&dev->wake_thread, 0);
+ mutex_init(&lis3->mutex);
+ atomic_set(&lis3->wake_thread, 0);
- lis3lv02d_add_fs(dev);
- lis3lv02d_poweron(dev);
+ lis3lv02d_add_fs(lis3);
+ err = lis3lv02d_poweron(lis3);
+ if (err) {
+ lis3lv02d_remove_fs(lis3);
+ return err;
+ }
- if (dev->pm_dev) {
- pm_runtime_set_active(dev->pm_dev);
- pm_runtime_enable(dev->pm_dev);
+ if (lis3->pm_dev) {
+ pm_runtime_set_active(lis3->pm_dev);
+ pm_runtime_enable(lis3->pm_dev);
}
- if (lis3lv02d_joystick_enable())
+ if (lis3lv02d_joystick_enable(lis3))
pr_err("joystick initialization failed\n");
/* passing in platform specific data is purely optional and only
* used by the SPI transport layer at the moment */
- if (dev->pdata) {
- struct lis3lv02d_platform_data *p = dev->pdata;
+ if (lis3->pdata) {
+ struct lis3lv02d_platform_data *p = lis3->pdata;
- if (dev->whoami == WAI_8B)
- lis3lv02d_8b_configure(dev, p);
+ if (lis3->whoami == WAI_8B)
+ lis3lv02d_8b_configure(lis3, p);
irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;
- dev->irq_cfg = p->irq_cfg;
+ lis3->irq_cfg = p->irq_cfg;
if (p->irq_cfg)
- dev->write(dev, CTRL_REG3, p->irq_cfg);
+ lis3->write(lis3, CTRL_REG3, p->irq_cfg);
if (p->default_rate)
- lis3lv02d_set_odr(p->default_rate);
+ lis3lv02d_set_odr(lis3, p->default_rate);
}
/* bail if we did not get an IRQ from the bus layer */
- if (!dev->irq) {
+ if (!lis3->irq) {
pr_debug("No IRQ. Disabling /dev/freefall\n");
goto out;
}
@@ -973,23 +1020,27 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
* io-apic is not configurable (and generates a warning) but I keep it
* in case of support for other hardware.
*/
- if (dev->pdata && dev->whoami == WAI_8B)
+ if (lis3->pdata && lis3->whoami == WAI_8B)
thread_fn = lis302dl_interrupt_thread1_8b;
else
thread_fn = NULL;
- err = request_threaded_irq(dev->irq, lis302dl_interrupt,
+ err = request_threaded_irq(lis3->irq, lis302dl_interrupt,
thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
irq_flags,
- DRIVER_NAME, &lis3_dev);
+ DRIVER_NAME, lis3);
if (err < 0) {
pr_err("Cannot get IRQ\n");
goto out;
}
- if (misc_register(&lis3lv02d_misc_device))
+ lis3->miscdev.minor = MISC_DYNAMIC_MINOR;
+ lis3->miscdev.name = "freefall";
+ lis3->miscdev.fops = &lis3lv02d_misc_fops;
+
+ if (misc_register(&lis3->miscdev))
pr_err("misc_register failed\n");
out:
return 0;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index a1939589eb2c..2b1482ad3f16 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/regulator/consumer.h>
+#include <linux/miscdevice.h>
/*
* This driver tries to support the "digital" accelerometer chips from
@@ -273,6 +274,8 @@ struct lis3lv02d {
struct fasync_struct *async_queue; /* queue for the misc device */
wait_queue_head_t misc_wait; /* Wait queue for the misc device */
unsigned long misc_opened; /* bit0: whether the device is open */
+ struct miscdevice miscdev;
+
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
@@ -282,10 +285,10 @@ struct lis3lv02d {
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
-int lis3lv02d_joystick_enable(void);
-void lis3lv02d_joystick_disable(void);
+int lis3lv02d_joystick_enable(struct lis3lv02d *lis3);
+void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
-void lis3lv02d_poweron(struct lis3lv02d *lis3);
+int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index b20dfb4522d2..c02fea029dcf 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -79,8 +79,7 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
u8 reg;
int ret;
- if (lis3->reg_ctrl)
- lis3_reg_ctrl(lis3, LIS3_REG_ON);
+ lis3_reg_ctrl(lis3, LIS3_REG_ON);
lis3->read(lis3, WHO_AM_I, &reg);
if (reg != lis3->whoami)
@@ -106,10 +105,6 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
if (pdata) {
- /* Regulator control is optional */
- if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL)
- lis3_dev.reg_ctrl = lis3_reg_ctrl;
-
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK)))
@@ -131,15 +126,13 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
goto fail;
}
- if (lis3_dev.reg_ctrl) {
- lis3_dev.regulators[0].supply = reg_vdd;
- lis3_dev.regulators[1].supply = reg_vdd_io;
- ret = regulator_bulk_get(&client->dev,
- ARRAY_SIZE(lis3_dev.regulators),
- lis3_dev.regulators);
- if (ret < 0)
- goto fail;
- }
+ lis3_dev.regulators[0].supply = reg_vdd;
+ lis3_dev.regulators[1].supply = reg_vdd_io;
+ ret = regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(lis3_dev.regulators),
+ lis3_dev.regulators);
+ if (ret < 0)
+ goto fail;
lis3_dev.pdata = pdata;
lis3_dev.bus_priv = client;
@@ -153,16 +146,19 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, &lis3_dev);
/* Provide power over the init call */
- if (lis3_dev.reg_ctrl)
- lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
ret = lis3lv02d_init_device(&lis3_dev);
- if (lis3_dev.reg_ctrl)
- lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
- if (ret == 0)
- return 0;
+ if (ret)
+ goto fail2;
+ return 0;
+
+fail2:
+ regulator_bulk_free(ARRAY_SIZE(lis3_dev.regulators),
+ lis3_dev.regulators);
fail:
if (pdata && pdata->release_resources)
pdata->release_resources();
@@ -177,12 +173,11 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
if (pdata && pdata->release_resources)
pdata->release_resources();
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(lis3);
lis3lv02d_remove_fs(&lis3_dev);
- if (lis3_dev.reg_ctrl)
- regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
- lis3_dev.regulators);
+ regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
+ lis3_dev.regulators);
return 0;
}
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index c1f8a8fbf694..b2c1be12d16f 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -83,7 +83,7 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
static int __devexit lis302dl_spi_remove(struct spi_device *spi)
{
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(lis3);
lis3lv02d_poweroff(lis3);
return lis3lv02d_remove_fs(&lis3_dev);
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 053d36caf955..cd41d403c9df 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -151,7 +151,7 @@ MODULE_LICENSE("GPL");
struct vmballoon_stats {
unsigned int timer;
- /* allocation statustics */
+ /* allocation statistics */
unsigned int alloc;
unsigned int alloc_fail;
unsigned int sleep_alloc;
@@ -412,6 +412,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep)
gfp_t flags;
unsigned int hv_status;
bool locked = false;
+ flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP;
do {
if (!can_sleep)
@@ -419,7 +420,6 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep)
else
STATS_INC(b->stats.sleep_alloc);
- flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP;
page = alloc_page(flags);
if (!page) {
if (!can_sleep)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 87d5067ba629..cf444b0ca2cc 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -263,7 +263,7 @@ config MMC_WBSD
config MMC_AU1X
tristate "Alchemy AU1XX0 MMC Card Interface support"
- depends on SOC_AU1200
+ depends on MIPS_ALCHEMY
help
This selects the AMD Alchemy(R) Multimedia card interface.
If you have a Alchemy platform with a MMC slot, say Y or M here.
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index 707bc7dddd22..5d3b9ae64523 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -64,11 +64,8 @@
#define AU1XMMC_DESCRIPTOR_COUNT 1
/* max DMA seg size: 64KB on Au1100, 4MB on Au1200 */
-#ifdef CONFIG_SOC_AU1100
-#define AU1XMMC_DESCRIPTOR_SIZE 0x0000ffff
-#else /* Au1200 */
-#define AU1XMMC_DESCRIPTOR_SIZE 0x003fffff
-#endif
+#define AU1100_MMC_DESCRIPTOR_SIZE 0x0000ffff
+#define AU1200_MMC_DESCRIPTOR_SIZE 0x003fffff
#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
@@ -127,6 +124,7 @@ struct au1xmmc_host {
#define HOST_F_XMIT 0x0001
#define HOST_F_RECV 0x0002
#define HOST_F_DMA 0x0010
+#define HOST_F_DBDMA 0x0020
#define HOST_F_ACTIVE 0x0100
#define HOST_F_STOP 0x1000
@@ -151,6 +149,16 @@ struct au1xmmc_host {
#define DMA_CHANNEL(h) \
(((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
+static inline int has_dbdma(void)
+{
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1200:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
{
u32 val = au_readl(HOST_CONFIG(host));
@@ -353,14 +361,12 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
data->bytes_xfered = 0;
if (!data->error) {
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
u32 chan = DMA_CHANNEL(host);
chan_tab_t *c = *((chan_tab_t **)chan);
au1x_dma_chan_t *cp = c->chan_ptr;
data->bytes_xfered = cp->ddma_bytecnt;
-#endif
} else
data->bytes_xfered =
(data->blocks * data->blksz) - host->pio.len;
@@ -570,11 +576,10 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
host->status = HOST_S_DATA;
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if ((host->flags & (HOST_F_DMA | HOST_F_DBDMA))) {
u32 channel = DMA_CHANNEL(host);
- /* Start the DMA as soon as the buffer gets something in it */
+ /* Start the DBDMA as soon as the buffer gets something in it */
if (host->flags & HOST_F_RECV) {
u32 mask = SD_STATUS_DB | SD_STATUS_NE;
@@ -584,7 +589,6 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
}
au1xxx_dbdma_start(channel);
-#endif
}
}
@@ -633,8 +637,7 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host,
au_writel(data->blksz - 1, HOST_BLKSIZE(host));
- if (host->flags & HOST_F_DMA) {
-#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
int i;
u32 channel = DMA_CHANNEL(host);
@@ -663,7 +666,6 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host,
datalen -= len;
}
-#endif
} else {
host->pio.index = 0;
host->pio.offset = 0;
@@ -838,7 +840,6 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-#ifdef CONFIG_SOC_AU1200
/* 8bit memory DMA device */
static dbdev_tab_t au1xmmc_mem_dbdev = {
.dev_id = DSCR_CMD0_ALWAYS,
@@ -905,7 +906,7 @@ static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT);
/* DBDMA is good to go */
- host->flags |= HOST_F_DMA;
+ host->flags |= HOST_F_DMA | HOST_F_DBDMA;
return 0;
}
@@ -918,7 +919,6 @@ static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host)
au1xxx_dbdma_chan_free(host->rx_chan);
}
}
-#endif
static void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en)
{
@@ -997,8 +997,16 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
mmc->f_min = 450000;
mmc->f_max = 24000000;
- mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
- mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1100:
+ mmc->max_seg_size = AU1100_MMC_DESCRIPTOR_SIZE;
+ mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ break;
+ case ALCHEMY_CPU_AU1200:
+ mmc->max_seg_size = AU1200_MMC_DESCRIPTOR_SIZE;
+ mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ break;
+ }
mmc->max_blk_size = 2048;
mmc->max_blk_count = 512;
@@ -1028,11 +1036,11 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
(unsigned long)host);
-#ifdef CONFIG_SOC_AU1200
- ret = au1xmmc_dbdma_init(host);
- if (ret)
- pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
-#endif
+ if (has_dbdma()) {
+ ret = au1xmmc_dbdma_init(host);
+ if (ret)
+ pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
+ }
#ifdef CONFIG_LEDS_CLASS
if (host->platdata && host->platdata->led) {
@@ -1073,9 +1081,8 @@ out5:
au_writel(0, HOST_CONFIG2(host));
au_sync();
-#ifdef CONFIG_SOC_AU1200
- au1xmmc_dbdma_shutdown(host);
-#endif
+ if (host->flags & HOST_F_DBDMA)
+ au1xmmc_dbdma_shutdown(host);
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
@@ -1120,9 +1127,9 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
-#ifdef CONFIG_SOC_AU1200
- au1xmmc_dbdma_shutdown(host);
-#endif
+ if (host->flags & HOST_F_DBDMA)
+ au1xmmc_dbdma_shutdown(host);
+
au1xmmc_set_power(host, 0);
free_irq(host->irq, host);
@@ -1181,24 +1188,23 @@ static struct platform_driver au1xmmc_driver = {
static int __init au1xmmc_init(void)
{
-#ifdef CONFIG_SOC_AU1200
- /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
- * of 8 bits. And since devices are shared, we need to create
- * our own to avoid freaking out other devices.
- */
- memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
- if (!memid)
- pr_err("au1xmmc: cannot add memory dbdma dev\n");
-#endif
+ if (has_dbdma()) {
+ /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
+ * of 8 bits. And since devices are shared, we need to create
+ * our own to avoid freaking out other devices.
+ */
+ memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
+ if (!memid)
+ pr_err("au1xmmc: cannot add memory dbdma\n");
+ }
return platform_driver_register(&au1xmmc_driver);
}
static void __exit au1xmmc_exit(void)
{
-#ifdef CONFIG_SOC_AU1200
- if (memid)
+ if (has_dbdma() && memid)
au1xxx_ddma_del_device(memid);
-#endif
+
platform_driver_unregister(&au1xmmc_driver);
}
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 0076c7448fe6..64a8325a4a8a 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios)
static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmc_davinci_host *host = mmc_priv(mmc);
+ struct platform_device *pdev = to_platform_device(mmc->parent);
+ struct davinci_mmc_config *config = pdev->dev.platform_data;
dev_dbg(mmc_dev(host->mmc),
"clock %dHz busmode %d powermode %d Vdd %04x\n",
ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd);
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ if (config && config->set_power)
+ config->set_power(pdev->id, false);
+ break;
+ case MMC_POWER_UP:
+ if (config && config->set_power)
+ config->set_power(pdev->id, true);
+ break;
+ }
+
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n");
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index f48743de4673..325ea61e12d3 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -40,6 +40,7 @@
#include <mach/mmc.h>
#include <mach/dma.h>
+#include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc"
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e8ff12396680..101cd31c8220 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1270,7 +1270,7 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
}
} else {
if (!host->protect_card) {
- pr_info"%s: cover is open, "
+ pr_info("%s: cover is open, "
"card is now inaccessible\n",
mmc_hostname(host->mmc));
host->protect_card = 1;
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index d2856b6b2a62..720f99334a7f 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -913,9 +913,9 @@ request_done:
}
static void s3cmci_dma_setup(struct s3cmci_host *host,
- enum s3c2410_dmasrc source)
+ enum dma_data_direction source)
{
- static enum s3c2410_dmasrc last_source = -1;
+ static enum dma_data_direction last_source = -1;
static int setup_ok;
if (last_source == source)
@@ -1087,7 +1087,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
- s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
+ s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index a90cabd7b84d..7e508969239e 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -182,7 +182,7 @@ ltq_mtd_probe(struct platform_device *pdev)
parts = ltq_mtd_data->parts;
}
- err = add_mtd_partitions(ltq_mtd->mtd, parts, nr_parts);
+ err = mtd_device_register(ltq_mtd->mtd, parts, nr_parts);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
@@ -208,7 +208,7 @@ ltq_mtd_remove(struct platform_device *pdev)
if (ltq_mtd) {
if (ltq_mtd->mtd) {
- del_mtd_partitions(ltq_mtd->mtd);
+ mtd_device_unregister(ltq_mtd->mtd);
map_destroy(ltq_mtd->mtd);
}
if (ltq_mtd->map->virt)
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index f1af2228a1b1..61086ea3cc6b 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1144,7 +1144,7 @@ static void mtdchar_notify_remove(struct mtd_info *mtd)
if (mtd_ino) {
/* Destroy the inode if it exists */
- mtd_ino->i_nlink = 0;
+ clear_nlink(mtd_ino);
iput(mtd_ino);
}
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4c3425235adc..dbfa0f7fb464 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -138,7 +138,7 @@ config MTD_NAND_RICOH
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
- depends on SOC_AU1200 || SOC_AU1550
+ depends on MIPS_ALCHEMY
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index e7767eef4505..fa5736b9286c 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -19,7 +19,11 @@
#include <linux/mtd/partitions.h>
#include <asm/io.h>
-#include <asm/mach-au1x00/au1xxx.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/mach-pb1x00/pb1550.h>
+#elif defined(CONFIG_MIPS_DB1550)
+#include <asm/mach-db1x00/db1x00.h>
+#endif
#include <asm/mach-db1x00/bcsr.h>
/*
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index b33c099d65a4..0ae0d7c54ccf 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2110,9 +2110,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
//check if there are any slaves
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2161,9 +2158,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
}
re_arm:
- if (!bond->kill_timers)
- queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
-out:
+ queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
+
read_unlock(&bond->lock);
}
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index d4fbd2e62616..106b88a04738 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1343,10 +1343,6 @@ void bond_alb_monitor(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers) {
- goto out;
- }
-
if (bond->slave_cnt == 0) {
bond_info->tx_rebalance_counter = 0;
bond_info->lp_counter = 0;
@@ -1401,10 +1397,13 @@ void bond_alb_monitor(struct work_struct *work)
/*
* dev_set_promiscuity requires rtnl and
- * nothing else.
+ * nothing else. Avoid race with bond_close.
*/
read_unlock(&bond->lock);
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ goto re_arm;
+ }
bond_info->rlb_promisc_timeout_counter = 0;
@@ -1440,9 +1439,8 @@ void bond_alb_monitor(struct work_struct *work)
}
re_arm:
- if (!bond->kill_timers)
- queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
-out:
+ queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
+
read_unlock(&bond->lock);
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index c5944f1a4f9d..b2b9109b6712 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -550,7 +550,7 @@ down:
/*
* Get link speed and duplex from the slave's base driver
* using ethtool. If for some reason the call fails or the
- * values are invalid, fake speed and duplex to 100/Full
+ * values are invalid, set speed and duplex to -1,
* and return error.
*/
static int bond_update_speed_duplex(struct slave *slave)
@@ -560,9 +560,8 @@ static int bond_update_speed_duplex(struct slave *slave)
u32 slave_speed;
int res;
- /* Fake speed and duplex */
- slave->speed = SPEED_100;
- slave->duplex = DUPLEX_FULL;
+ slave->speed = -1;
+ slave->duplex = -1;
res = __ethtool_get_settings(slave_dev, &ecmd);
if (res < 0)
@@ -773,9 +772,6 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
/* rejoin all groups on bond device */
__bond_resend_igmp_join_requests(bond->dev);
@@ -789,9 +785,9 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
__bond_resend_igmp_join_requests(vlan_dev);
}
- if ((--bond->igmp_retrans > 0) && !bond->kill_timers)
+ if (--bond->igmp_retrans > 0)
queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5);
-out:
+
read_unlock(&bond->lock);
}
@@ -1754,16 +1750,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
new_slave->link = BOND_LINK_DOWN;
}
- if (bond_update_speed_duplex(new_slave) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- pr_warning("%s: Warning: failed to get speed and duplex from %s, assumed to be 100Mb/sec and Full.\n",
- bond_dev->name, new_slave->dev->name);
-
- if (bond->params.mode == BOND_MODE_8023AD) {
- pr_warning("%s: Warning: Operation of 802.3ad mode requires ETHTOOL support in base driver for proper aggregator selection.\n",
- bond_dev->name);
- }
- }
+ bond_update_speed_duplex(new_slave);
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
/* if there is a primary slave, remember it */
@@ -2517,10 +2504,11 @@ void bond_mii_monitor(struct work_struct *work)
struct bonding *bond = container_of(work, struct bonding,
mii_work.work);
bool should_notify_peers = false;
+ unsigned long delay;
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
+
+ delay = msecs_to_jiffies(bond->params.miimon);
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2529,7 +2517,15 @@ void bond_mii_monitor(struct work_struct *work)
if (bond_miimon_inspect(bond)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close cancel of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delay = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_miimon_commit(bond);
@@ -2540,14 +2536,18 @@ void bond_mii_monitor(struct work_struct *work)
}
re_arm:
- if (bond->params.miimon && !bond->kill_timers)
- queue_delayed_work(bond->wq, &bond->mii_work,
- msecs_to_jiffies(bond->params.miimon));
-out:
+ if (bond->params.miimon)
+ queue_delayed_work(bond->wq, &bond->mii_work, delay);
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
@@ -2789,9 +2789,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
- if (bond->kill_timers)
- goto out;
-
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2888,9 +2885,9 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
}
re_arm:
- if (bond->params.arp_interval && !bond->kill_timers)
+ if (bond->params.arp_interval)
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
@@ -3131,9 +3128,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
if (bond->slave_cnt == 0)
@@ -3143,7 +3137,15 @@ void bond_activebackup_arp_mon(struct work_struct *work)
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close flush of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delta_in_ticks = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_ab_arp_commit(bond, delta_in_ticks);
@@ -3156,13 +3158,18 @@ void bond_activebackup_arp_mon(struct work_struct *work)
bond_ab_arp_probe(bond);
re_arm:
- if (bond->params.arp_interval && !bond->kill_timers)
+ if (bond->params.arp_interval)
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
@@ -3203,6 +3210,7 @@ static int bond_slave_netdev_event(unsigned long event,
{
struct net_device *bond_dev = slave_dev->master;
struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave = NULL;
switch (event) {
case NETDEV_UNREGISTER:
@@ -3213,20 +3221,16 @@ static int bond_slave_netdev_event(unsigned long event,
bond_release(bond_dev, slave_dev);
}
break;
+ case NETDEV_UP:
case NETDEV_CHANGE:
- if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) {
- struct slave *slave;
+ slave = bond_get_slave_by_dev(bond, slave_dev);
+ if (slave) {
+ u32 old_speed = slave->speed;
+ u8 old_duplex = slave->duplex;
- slave = bond_get_slave_by_dev(bond, slave_dev);
- if (slave) {
- u32 old_speed = slave->speed;
- u8 old_duplex = slave->duplex;
-
- bond_update_speed_duplex(slave);
-
- if (bond_is_lb(bond))
- break;
+ bond_update_speed_duplex(slave);
+ if (bond->params.mode == BOND_MODE_8023AD) {
if (old_speed != slave->speed)
bond_3ad_adapter_speed_changed(slave);
if (old_duplex != slave->duplex)
@@ -3424,8 +3428,6 @@ static int bond_open(struct net_device *bond_dev)
struct slave *slave;
int i;
- bond->kill_timers = 0;
-
/* reset slave->backup and slave->inactive */
read_lock(&bond->lock);
if (bond->slave_cnt > 0) {
@@ -3494,33 +3496,30 @@ static int bond_close(struct net_device *bond_dev)
bond->send_peer_notif = 0;
- /* signal timers not to re-arm */
- bond->kill_timers = 1;
-
write_unlock_bh(&bond->lock);
if (bond->params.miimon) { /* link check interval, in milliseconds. */
- cancel_delayed_work(&bond->mii_work);
+ cancel_delayed_work_sync(&bond->mii_work);
}
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- cancel_delayed_work(&bond->arp_work);
+ cancel_delayed_work_sync(&bond->arp_work);
}
switch (bond->params.mode) {
case BOND_MODE_8023AD:
- cancel_delayed_work(&bond->ad_work);
+ cancel_delayed_work_sync(&bond->ad_work);
break;
case BOND_MODE_TLB:
case BOND_MODE_ALB:
- cancel_delayed_work(&bond->alb_work);
+ cancel_delayed_work_sync(&bond->alb_work);
break;
default:
break;
}
if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
+ cancel_delayed_work_sync(&bond->mcast_work);
if (bond_is_lb(bond)) {
/* Must be called only after all
@@ -4367,26 +4366,22 @@ static void bond_setup(struct net_device *bond_dev)
static void bond_work_cancel_all(struct bonding *bond)
{
- write_lock_bh(&bond->lock);
- bond->kill_timers = 1;
- write_unlock_bh(&bond->lock);
-
if (bond->params.miimon && delayed_work_pending(&bond->mii_work))
- cancel_delayed_work(&bond->mii_work);
+ cancel_delayed_work_sync(&bond->mii_work);
if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work))
- cancel_delayed_work(&bond->arp_work);
+ cancel_delayed_work_sync(&bond->arp_work);
if (bond->params.mode == BOND_MODE_ALB &&
delayed_work_pending(&bond->alb_work))
- cancel_delayed_work(&bond->alb_work);
+ cancel_delayed_work_sync(&bond->alb_work);
if (bond->params.mode == BOND_MODE_8023AD &&
delayed_work_pending(&bond->ad_work))
- cancel_delayed_work(&bond->ad_work);
+ cancel_delayed_work_sync(&bond->ad_work);
if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
+ cancel_delayed_work_sync(&bond->mcast_work);
}
/*
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index 95de93b90386..d2ff52e63cbb 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -157,8 +157,16 @@ static void bond_info_show_slave(struct seq_file *seq,
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
- seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+ if (slave->speed == -1)
+ seq_printf(seq, "Speed: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
+
+ if (slave->duplex == -1)
+ seq_printf(seq, "Duplex: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+
seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 82fec5fc75d7..1aecc37e5b4d 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -222,7 +222,6 @@ struct bonding {
struct slave *);
rwlock_t lock;
rwlock_t curr_slave_lock;
- s8 kill_timers;
u8 send_peer_notif;
s8 setup_by_slave;
s8 igmp_retrans;
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 6dff5a0e733f..597f4d45c632 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -159,6 +159,7 @@ config S6GMAC
will be called s6gmac.
source "drivers/net/ethernet/seeq/Kconfig"
+source "drivers/net/ethernet/silan/Kconfig"
source "drivers/net/ethernet/sis/Kconfig"
source "drivers/net/ethernet/sfc/Kconfig"
source "drivers/net/ethernet/sgi/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c53ad3afc991..be5dde040261 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_S6GMAC) += s6gmac.o
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
+obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
obj-$(CONFIG_NET_VENDOR_SIS) += sis/
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
index 82386677bb8c..4865ff14bebf 100644
--- a/drivers/net/ethernet/amd/au1000_eth.c
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -541,19 +541,17 @@ static void au1000_reset_mac(struct net_device *dev)
* these are not descriptors sitting in memory.
*/
static void
-au1000_setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
+au1000_setup_hw_rings(struct au1000_private *aup, void __iomem *tx_base)
{
int i;
for (i = 0; i < NUM_RX_DMA; i++) {
- aup->rx_dma_ring[i] =
- (struct rx_dma *)
- (rx_base + sizeof(struct rx_dma)*i);
+ aup->rx_dma_ring[i] = (struct rx_dma *)
+ (tx_base + 0x100 + sizeof(struct rx_dma) * i);
}
for (i = 0; i < NUM_TX_DMA; i++) {
- aup->tx_dma_ring[i] =
- (struct tx_dma *)
- (tx_base + sizeof(struct tx_dma)*i);
+ aup->tx_dma_ring[i] = (struct tx_dma *)
+ (tx_base + sizeof(struct tx_dma) * i);
}
}
@@ -1026,7 +1024,7 @@ static int __devinit au1000_probe(struct platform_device *pdev)
struct net_device *dev = NULL;
struct db_dest *pDB, *pDBfree;
int irq, i, err = 0;
- struct resource *base, *macen;
+ struct resource *base, *macen, *macdma;
base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!base) {
@@ -1049,6 +1047,13 @@ static int __devinit au1000_probe(struct platform_device *pdev)
goto out;
}
+ macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!macdma) {
+ dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
+ err = -ENODEV;
+ goto out;
+ }
+
if (!request_mem_region(base->start, resource_size(base),
pdev->name)) {
dev_err(&pdev->dev, "failed to request memory region for base registers\n");
@@ -1063,6 +1068,13 @@ static int __devinit au1000_probe(struct platform_device *pdev)
goto err_request;
}
+ if (!request_mem_region(macdma->start, resource_size(macdma),
+ pdev->name)) {
+ dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
+ err = -ENXIO;
+ goto err_macdma;
+ }
+
dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
dev_err(&pdev->dev, "alloc_etherdev failed\n");
@@ -1109,10 +1121,14 @@ static int __devinit au1000_probe(struct platform_device *pdev)
}
aup->mac_id = pdev->id;
- if (pdev->id == 0)
- au1000_setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
- else if (pdev->id == 1)
- au1000_setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
+ aup->macdma = ioremap_nocache(macdma->start, resource_size(macdma));
+ if (!aup->macdma) {
+ dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
+ err = -ENXIO;
+ goto err_remap3;
+ }
+
+ au1000_setup_hw_rings(aup, aup->macdma);
/* set a random MAC now in case platform_data doesn't provide one */
random_ether_addr(dev->dev_addr);
@@ -1252,6 +1268,8 @@ err_out:
err_mdiobus_reg:
mdiobus_free(aup->mii_bus);
err_mdiobus_alloc:
+ iounmap(aup->macdma);
+err_remap3:
iounmap(aup->enable);
err_remap2:
iounmap(aup->mac);
@@ -1261,6 +1279,8 @@ err_remap1:
err_vaddr:
free_netdev(dev);
err_alloc:
+ release_mem_region(macdma->start, resource_size(macdma));
+err_macdma:
release_mem_region(macen->start, resource_size(macen));
err_request:
release_mem_region(base->start, resource_size(base));
@@ -1293,9 +1313,13 @@ static int __devexit au1000_remove(struct platform_device *pdev)
(NUM_TX_BUFFS + NUM_RX_BUFFS),
(void *)aup->vaddr, aup->dma_addr);
+ iounmap(aup->macdma);
iounmap(aup->mac);
iounmap(aup->enable);
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ release_mem_region(base->start, resource_size(base));
+
base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(base->start, resource_size(base));
diff --git a/drivers/net/ethernet/amd/au1000_eth.h b/drivers/net/ethernet/amd/au1000_eth.h
index 6229c774552c..4b7f7ad62bb8 100644
--- a/drivers/net/ethernet/amd/au1000_eth.h
+++ b/drivers/net/ethernet/amd/au1000_eth.h
@@ -124,7 +124,7 @@ struct au1000_private {
*/
struct mac_reg *mac; /* mac registers */
u32 *enable; /* address of MAC Enable Register */
-
+ void __iomem *macdma; /* base of MAC DMA port */
u32 vaddr; /* virtual address of rx/tx buffers */
dma_addr_t dma_addr; /* dma address of rx/tx buffers */
diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig
index a759d5483ab9..1375e2dc9468 100644
--- a/drivers/net/ethernet/apple/Kconfig
+++ b/drivers/net/ethernet/apple/Kconfig
@@ -52,18 +52,6 @@ config BMAC
To compile this driver as a module, choose M here: the module
will be called bmac.
-config MAC89x0
- tristate "Macintosh CS89x0 based ethernet cards"
- depends on MAC
- ---help---
- Support for CS89x0 chipset based Ethernet cards. If you have a
- Nubus or LC-PDS network (Ethernet) card of this type, say Y and
- read the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here. This module will
- be called mac89x0.
-
config MACMACE
bool "Macintosh (AV) onboard MACE ethernet"
depends on MAC
diff --git a/drivers/net/ethernet/apple/Makefile b/drivers/net/ethernet/apple/Makefile
index 0d3a5919c95b..86eaa17af0f4 100644
--- a/drivers/net/ethernet/apple/Makefile
+++ b/drivers/net/ethernet/apple/Makefile
@@ -4,5 +4,4 @@
obj-$(CONFIG_MACE) += mace.o
obj-$(CONFIG_BMAC) += bmac.o
-obj-$(CONFIG_MAC89x0) += mac89x0.o
obj-$(CONFIG_MACMACE) += macmace.o
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 627a5807836d..aec7212ac983 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -23,8 +23,8 @@
* (you will need to reboot afterwards) */
/* #define BNX2X_STOP_ON_ERROR */
-#define DRV_MODULE_VERSION "1.70.00-0"
-#define DRV_MODULE_RELDATE "2011/06/13"
+#define DRV_MODULE_VERSION "1.70.30-0"
+#define DRV_MODULE_RELDATE "2011/10/25"
#define BNX2X_BC_VER 0x040200
#if defined(CONFIG_DCB)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 1a6e37ce730c..f0ca8b27a55e 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -329,6 +329,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
PORT_HW_CFG_PHY_SELECTION_FIRST_PHY;
break;
case PORT_FIBRE:
+ case PORT_DA:
if (bp->port.supported[cfg_idx] & SUPPORTED_FIBRE)
break; /* no port change */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
index e44b858ff12f..fc754cb6cc0f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
@@ -2550,7 +2550,7 @@ struct host_func_stats {
#define BCM_5710_FW_MAJOR_VERSION 7
#define BCM_5710_FW_MINOR_VERSION 0
-#define BCM_5710_FW_REVISION_VERSION 23
+#define BCM_5710_FW_REVISION_VERSION 29
#define BCM_5710_FW_ENGINEERING_VERSION 0
#define BCM_5710_FW_COMPILE_FLAGS 1
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
index 818723c9e678..bce203fa4b9e 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
@@ -45,6 +45,9 @@
#define MCPR_IMC_COMMAND_READ_OP 1
#define MCPR_IMC_COMMAND_WRITE_OP 2
+/* LED Blink rate that will achieve ~15.9Hz */
+#define LED_BLINK_RATE_VAL_E3 354
+#define LED_BLINK_RATE_VAL_E1X_E2 480
/***********************************************************/
/* Shortcut definitions */
/***********************************************************/
@@ -258,6 +261,7 @@
#define MAX_PACKET_SIZE (9700)
#define WC_UC_TIMEOUT 100
+#define MAX_KR_LINK_RETRY 4
/**********************************************************/
/* INTERFACE */
@@ -1490,6 +1494,18 @@ static void bnx2x_set_xumac_nig(struct link_params *params,
NIG_REG_P0_MAC_PAUSE_OUT_EN, tx_pause_en);
}
+static void bnx2x_umac_disable(struct link_params *params)
+{
+ u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+ struct bnx2x *bp = params->bp;
+ if (!(REG_RD(bp, MISC_REG_RESET_REG_2) &
+ (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)))
+ return;
+
+ /* Disable RX and TX */
+ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, 0);
+}
+
static void bnx2x_umac_enable(struct link_params *params,
struct link_vars *vars, u8 lb)
{
@@ -1599,8 +1615,9 @@ static u8 bnx2x_is_4_port_mode(struct bnx2x *bp)
}
/* Define the XMAC mode */
-static void bnx2x_xmac_init(struct bnx2x *bp, u32 max_speed)
+static void bnx2x_xmac_init(struct link_params *params, u32 max_speed)
{
+ struct bnx2x *bp = params->bp;
u32 is_port4mode = bnx2x_is_4_port_mode(bp);
/**
@@ -1610,7 +1627,8 @@ static void bnx2x_xmac_init(struct bnx2x *bp, u32 max_speed)
* ports of the path
**/
- if (is_port4mode && (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ if ((CHIP_NUM(bp) == CHIP_NUM_57840) &&
+ (REG_RD(bp, MISC_REG_RESET_REG_2) &
MISC_REGISTERS_RESET_REG_2_XMAC)) {
DP(NETIF_MSG_LINK,
"XMAC already out of reset in 4-port mode\n");
@@ -1677,10 +1695,6 @@ static void bnx2x_xmac_disable(struct link_params *params)
(pfc_ctrl | (1<<1)));
DP(NETIF_MSG_LINK, "Disable XMAC on port %x\n", port);
REG_WR(bp, xmac_base + XMAC_REG_CTRL, 0);
- usleep_range(1000, 1000);
- bnx2x_set_xumac_nig(params, 0, 0);
- REG_WR(bp, xmac_base + XMAC_REG_CTRL,
- XMAC_CTRL_REG_SOFT_RESET);
}
}
@@ -1693,7 +1707,7 @@ static int bnx2x_xmac_enable(struct link_params *params,
xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
- bnx2x_xmac_init(bp, vars->line_speed);
+ bnx2x_xmac_init(params, vars->line_speed);
/*
* This register determines on which events the MAC will assert
@@ -3575,6 +3589,11 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
u16 val16 = 0, lane, bam37 = 0;
struct bnx2x *bp = params->bp;
DP(NETIF_MSG_LINK, "Enable Auto Negotiation for KR\n");
+
+ /* Disable Autoneg: re-enable it after adv is done. */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0);
+
/* Check adding advertisement for 1G KX */
if (((vars->line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
@@ -3616,9 +3635,6 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
MDIO_WC_REG_CL72_USERB0_CL72_2P5_DEF_CTRL,
0x03f0);
- bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
- MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL,
- 0x383f);
/* Advertised speeds */
bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
@@ -3645,19 +3661,22 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
/* Advertise pause */
bnx2x_ext_phy_set_pause(params, phy, vars);
- /* Enable Autoneg */
- bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
- MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1000);
-
- /* Over 1G - AN local device user page 1 */
- bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
- MDIO_WC_REG_DIGITAL3_UP1, 0x1f);
+ vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY;
bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
MDIO_WC_REG_DIGITAL5_MISC7, &val16);
bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
MDIO_WC_REG_DIGITAL5_MISC7, val16 | 0x100);
+
+ /* Over 1G - AN local device user page 1 */
+ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+ MDIO_WC_REG_DIGITAL3_UP1, 0x1f);
+
+ /* Enable Autoneg */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1000);
+
}
static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy,
@@ -4126,6 +4145,85 @@ static int bnx2x_is_sfp_module_plugged(struct bnx2x_phy *phy,
else
return 0;
}
+static int bnx2x_warpcore_get_sigdet(struct bnx2x_phy *phy,
+ struct link_params *params)
+{
+ u16 gp2_status_reg0, lane;
+ struct bnx2x *bp = params->bp;
+
+ lane = bnx2x_get_warpcore_lane(phy, params);
+
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_GP2_STATUS_GP_2_0,
+ &gp2_status_reg0);
+
+ return (gp2_status_reg0 >> (8+lane)) & 0x1;
+}
+
+static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy,
+ struct link_params *params,
+ struct link_vars *vars)
+{
+ struct bnx2x *bp = params->bp;
+ u32 serdes_net_if;
+ u16 gp_status1 = 0, lnkup = 0, lnkup_kr = 0;
+ u16 lane = bnx2x_get_warpcore_lane(phy, params);
+
+ vars->turn_to_run_wc_rt = vars->turn_to_run_wc_rt ? 0 : 1;
+
+ if (!vars->turn_to_run_wc_rt)
+ return;
+
+ /* return if there is no link partner */
+ if (!(bnx2x_warpcore_get_sigdet(phy, params))) {
+ DP(NETIF_MSG_LINK, "bnx2x_warpcore_get_sigdet false\n");
+ return;
+ }
+
+ if (vars->rx_tx_asic_rst) {
+ serdes_net_if = (REG_RD(bp, params->shmem_base +
+ offsetof(struct shmem_region, dev_info.
+ port_hw_config[params->port].default_cfg)) &
+ PORT_HW_CFG_NET_SERDES_IF_MASK);
+
+ switch (serdes_net_if) {
+ case PORT_HW_CFG_NET_SERDES_IF_KR:
+ /* Do we get link yet? */
+ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, 0x81d1,
+ &gp_status1);
+ lnkup = (gp_status1 >> (8+lane)) & 0x1;/* 1G */
+ /*10G KR*/
+ lnkup_kr = (gp_status1 >> (12+lane)) & 0x1;
+
+ DP(NETIF_MSG_LINK,
+ "gp_status1 0x%x\n", gp_status1);
+
+ if (lnkup_kr || lnkup) {
+ vars->rx_tx_asic_rst = 0;
+ DP(NETIF_MSG_LINK,
+ "link up, rx_tx_asic_rst 0x%x\n",
+ vars->rx_tx_asic_rst);
+ } else {
+ /*reset the lane to see if link comes up.*/
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
+
+ /* restart Autoneg */
+ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD,
+ MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1200);
+
+ vars->rx_tx_asic_rst--;
+ DP(NETIF_MSG_LINK, "0x%x retry left\n",
+ vars->rx_tx_asic_rst);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } /*params->rx_tx_asic_rst*/
+
+}
static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
struct link_params *params,
@@ -5896,7 +5994,13 @@ int bnx2x_set_led(struct link_params *params,
SHARED_HW_CFG_LED_MAC1);
tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
- EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp | EMAC_LED_OVERRIDE));
+ if (params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE)
+ EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp & 0xfff1);
+ else {
+ EMAC_WR(bp, EMAC_REG_EMAC_LED,
+ (tmp | EMAC_LED_OVERRIDE));
+ }
break;
case LED_MODE_OPER:
@@ -5949,17 +6053,33 @@ int bnx2x_set_led(struct link_params *params,
else
REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
hw_led_mode);
+ } else if ((params->phy[EXT_PHY1].type ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) &&
+ (mode != LED_MODE_OPER)) {
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
+ tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
+ EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp | 0x3);
} else
- REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode);
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+ hw_led_mode);
REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
/* Set blinking rate to ~15.9Hz */
- REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
- LED_BLINK_RATE_VAL);
+ if (CHIP_IS_E3(bp))
+ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
+ LED_BLINK_RATE_VAL_E3);
+ else
+ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4,
+ LED_BLINK_RATE_VAL_E1X_E2);
REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 +
port*4, 1);
- tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
- EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE)));
+ if ((params->phy[EXT_PHY1].type !=
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) &&
+ (mode != LED_MODE_OPER)) {
+ tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
+ EMAC_WR(bp, EMAC_REG_EMAC_LED,
+ (tmp & (~EMAC_LED_OVERRIDE)));
+ }
if (CHIP_IS_E1(bp) &&
((speed == SPEED_2500) ||
@@ -6218,8 +6338,10 @@ static int bnx2x_update_link_down(struct link_params *params,
MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
}
- if (CHIP_IS_E3(bp))
+ if (CHIP_IS_E3(bp)) {
bnx2x_xmac_disable(params);
+ bnx2x_umac_disable(params);
+ }
return 0;
}
@@ -10205,22 +10327,6 @@ static int bnx2x_54618se_config_init(struct bnx2x_phy *phy,
return 0;
}
-static void bnx2x_54618se_set_link_led(struct bnx2x_phy *phy,
- struct link_params *params, u8 mode)
-{
- struct bnx2x *bp = params->bp;
- DP(NETIF_MSG_LINK, "54618SE set link led (mode=%x)\n", mode);
- switch (mode) {
- case LED_MODE_FRONT_PANEL_OFF:
- case LED_MODE_OFF:
- case LED_MODE_OPER:
- case LED_MODE_ON:
- default:
- break;
- }
- return;
-}
-
static void bnx2x_54618se_link_reset(struct bnx2x_phy *phy,
struct link_params *params)
{
@@ -10997,7 +11103,7 @@ static struct bnx2x_phy phy_54618se = {
.config_loopback = (config_loopback_t)bnx2x_54618se_config_loopback,
.format_fw_ver = (format_fw_ver_t)NULL,
.hw_reset = (hw_reset_t)NULL,
- .set_link_led = (set_link_led_t)bnx2x_54618se_set_link_led,
+ .set_link_led = (set_link_led_t)NULL,
.phy_specific_func = (phy_specific_func_t)NULL
};
/*****************************************************************/
@@ -11718,8 +11824,10 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
/* Stop BigMac rx */
if (!CHIP_IS_E3(bp))
bnx2x_bmac_rx_disable(bp, port);
- else
+ else {
bnx2x_xmac_disable(params);
+ bnx2x_umac_disable(params);
+ }
/* disable emac */
if (!CHIP_IS_E3(bp))
REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0);
@@ -11757,14 +11865,21 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
if (params->phy[INT_PHY].link_reset)
params->phy[INT_PHY].link_reset(
&params->phy[INT_PHY], params);
- /* reset BigMac */
- REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
- (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
/* disable nig ingress interface */
if (!CHIP_IS_E3(bp)) {
+ /* reset BigMac */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0);
REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0);
+ } else {
+ u32 xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+ bnx2x_set_xumac_nig(params, 0, 0);
+ if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ MISC_REGISTERS_RESET_REG_2_XMAC)
+ REG_WR(bp, xmac_base + XMAC_REG_CTRL,
+ XMAC_CTRL_REG_SOFT_RESET);
}
vars->link_up = 0;
vars->phy_flags = 0;
@@ -12332,11 +12447,6 @@ void bnx2x_period_func(struct link_params *params, struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u16 phy_idx;
- if (!params) {
- DP(NETIF_MSG_LINK, "Uninitialized params !\n");
- return;
- }
-
for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) {
bnx2x_set_aer_mmd(params, &params->phy[phy_idx]);
@@ -12345,8 +12455,13 @@ void bnx2x_period_func(struct link_params *params, struct link_vars *vars)
}
}
- if (CHIP_IS_E3(bp))
+ if (CHIP_IS_E3(bp)) {
+ struct bnx2x_phy *phy = &params->phy[INT_PHY];
+ bnx2x_set_aer_mmd(params, phy);
bnx2x_check_over_curr(params, vars);
+ bnx2x_warpcore_config_runtime(phy, params, vars);
+ }
+
}
u8 bnx2x_hw_lock_required(struct bnx2x *bp, u32 shmem_base, u32 shmem2_base)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
index c12db6da213e..2a46e633abe9 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
@@ -303,6 +303,9 @@ struct link_vars {
#define PERIODIC_FLAGS_LINK_EVENT 0x0001
u32 aeu_int_mask;
+ u8 rx_tx_asic_rst;
+ u8 turn_to_run_wc_rt;
+ u16 rsrv2;
};
/***********************************************************/
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
index 6cbb81ccc02e..1f8648f099c7 100644
--- a/drivers/net/ethernet/cirrus/Kconfig
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -6,7 +6,7 @@ config NET_VENDOR_CIRRUS
bool "Cirrus devices"
default y
depends on ISA || EISA || MACH_IXDP2351 || ARCH_IXDP2X01 \
- || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX)
+ || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) || MAC
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
@@ -47,4 +47,16 @@ config EP93XX_ETH
This is a driver for the ethernet hardware included in EP93xx CPUs.
Say Y if you are building a kernel for EP93xx based devices.
+config MAC89x0
+ tristate "Macintosh CS89x0 based ethernet cards"
+ depends on MAC
+ ---help---
+ Support for CS89x0 chipset based Ethernet cards. If you have a
+ Nubus or LC-PDS network (Ethernet) card of this type, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. This module will
+ be called mac89x0.
+
endif # NET_VENDOR_CIRRUS
diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
index 14bd77e0cb57..ca245e2b5d98 100644
--- a/drivers/net/ethernet/cirrus/Makefile
+++ b/drivers/net/ethernet/cirrus/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_CS89x0) += cs89x0.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
+obj-$(CONFIG_MAC89x0) += mac89x0.o
diff --git a/drivers/net/ethernet/apple/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
index 83781f316d1f..83781f316d1f 100644
--- a/drivers/net/ethernet/apple/mac89x0.c
+++ b/drivers/net/ethernet/cirrus/mac89x0.c
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index e0ff96193c49..2c7b36673dfc 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -318,8 +318,7 @@ static int be_mbox_db_ready_wait(struct be_adapter *adapter, void __iomem *db)
if (msecs > 4000) {
dev_err(&adapter->pdev->dev, "mbox poll timed out\n");
- if (!lancer_chip(adapter))
- be_detect_dump_ue(adapter);
+ be_detect_dump_ue(adapter);
return -1;
}
@@ -428,28 +427,33 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
return &wrb->payload.sgl[0];
}
-/* Don't touch the hdr after it's prepared */
-static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
- bool embedded, u8 sge_cnt, u32 opcode)
-{
- if (embedded)
- wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
- else
- wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
- MCC_WRB_SGE_CNT_SHIFT;
- wrb->payload_length = payload_len;
- wrb->tag0 = opcode;
- be_dws_cpu_to_le(wrb, 8);
-}
/* Don't touch the hdr after it's prepared */
-static void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
- u8 subsystem, u8 opcode, int cmd_len)
+/* mem will be NULL for embedded commands */
+static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
+ u8 subsystem, u8 opcode, int cmd_len,
+ struct be_mcc_wrb *wrb, struct be_dma_mem *mem)
{
+ struct be_sge *sge;
+
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
req_hdr->version = 0;
+
+ wrb->tag0 = opcode;
+ wrb->tag1 = subsystem;
+ wrb->payload_length = cmd_len;
+ if (mem) {
+ wrb->embedded |= (1 & MCC_WRB_SGE_CNT_MASK) <<
+ MCC_WRB_SGE_CNT_SHIFT;
+ sge = nonembedded_sgl(wrb);
+ sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma));
+ sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(mem->size);
+ } else
+ wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
+ be_dws_cpu_to_le(wrb, 8);
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
@@ -586,10 +590,8 @@ int be_cmd_eq_create(struct be_adapter *adapter,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_COMMON_EQ_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_EQ_CREATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
@@ -632,12 +634,8 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_MAC_QUERY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req));
-
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb, NULL);
req->type = type;
if (permanent) {
req->permanent = 1;
@@ -674,11 +672,8 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_PMAC_ADD);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->if_id = cpu_to_le32(if_id);
@@ -692,6 +687,10 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
err:
spin_unlock_bh(&adapter->mcc_lock);
+
+ if (status == MCC_STATUS_UNAUTHORIZED_REQUEST)
+ status = -EPERM;
+
return status;
}
@@ -711,11 +710,8 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id, u32 dom)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_PMAC_DEL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req), wrb, NULL);
req->hdr.domain = dom;
req->if_id = cpu_to_le32(if_id);
@@ -746,11 +742,8 @@ int be_cmd_cq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_CQ_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_CQ_CREATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
if (lancer_chip(adapter)) {
@@ -822,11 +815,8 @@ int be_cmd_mccq_ext_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MCC_CREATE_EXT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
if (lancer_chip(adapter)) {
@@ -882,11 +872,8 @@ int be_cmd_mccq_org_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MCC_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
@@ -943,11 +930,8 @@ int be_cmd_txq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_TX_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_TX_CREATE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_TX_CREATE, sizeof(*req), wrb, NULL);
if (lancer_chip(adapter)) {
req->hdr.version = 1;
@@ -999,11 +983,8 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_RX_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_RX_CREATE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
req->cq_id = cpu_to_le16(cq_id);
req->frag_size = fls(frag_size) - 1;
@@ -1071,9 +1052,8 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
BUG();
}
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, opcode);
-
- be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req), wrb,
+ NULL);
req->id = cpu_to_le16(q->id);
status = be_mbox_notify_wait(adapter);
@@ -1100,9 +1080,8 @@ int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_ETH_RX_DESTROY);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_RX_DESTROY,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
req->id = cpu_to_le16(q->id);
status = be_mcc_notify_wait(adapter);
@@ -1133,12 +1112,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_INTERFACE_CREATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req));
-
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->capability_flags = cpu_to_le32(cap_flags);
req->enable_flags = cpu_to_le32(en_flags);
@@ -1182,12 +1157,8 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_INTERFACE_DESTROY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req));
-
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->interface_id = cpu_to_le32(interface_id);
@@ -1205,7 +1176,6 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_hdr *hdr;
- struct be_sge *sge;
int status = 0;
if (MODULO(adapter->work_counter, be_get_temp_freq) == 0)
@@ -1219,22 +1189,13 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
goto err;
}
hdr = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1,
- OPCODE_ETH_GET_STATISTICS);
-
- be_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, nonemb_cmd);
if (adapter->generation == BE_GEN3)
hdr->version = 1;
- wrb->tag1 = CMD_SUBSYSTEM_ETH;
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
be_mcc_notify(adapter);
adapter->stats_cmd_sent = true;
@@ -1250,7 +1211,6 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
struct be_mcc_wrb *wrb;
struct lancer_cmd_req_pport_stats *req;
- struct be_sge *sge;
int status = 0;
spin_lock_bh(&adapter->mcc_lock);
@@ -1261,23 +1221,14 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
goto err;
}
req = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1,
- OPCODE_ETH_GET_PPORT_STATS);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size, wrb,
+ nonemb_cmd);
req->cmd_params.params.pport_num = cpu_to_le16(adapter->port_num);
req->cmd_params.params.reset_stats = 0;
- wrb->tag1 = CMD_SUBSYSTEM_ETH;
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
be_mcc_notify(adapter);
adapter->stats_cmd_sent = true;
@@ -1303,11 +1254,8 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u8 *mac_speed,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_LINK_STATUS_QUERY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req), wrb, NULL);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -1343,11 +1291,9 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req),
+ wrb, NULL);
wrb->tag1 = mccq_index;
@@ -1374,11 +1320,8 @@ int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MANAGE_FAT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb, NULL);
req->fat_operation = cpu_to_le32(QUERY_FAT);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -1397,7 +1340,6 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
struct be_dma_mem get_fat_cmd;
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_fat *req;
- struct be_sge *sge;
u32 offset = 0, total_size, buf_size,
log_offset = sizeof(u32), payload_len;
int status;
@@ -1430,18 +1372,11 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
goto err;
}
req = get_fat_cmd.va;
- sge = nonembedded_sgl(wrb);
payload_len = sizeof(struct be_cmd_req_get_fat) + buf_size;
- be_wrb_hdr_prepare(wrb, payload_len, false, 1,
- OPCODE_COMMON_MANAGE_FAT);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, payload_len);
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(get_fat_cmd.dma));
- sge->pa_lo = cpu_to_le32(get_fat_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(get_fat_cmd.size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MANAGE_FAT, payload_len, wrb,
+ &get_fat_cmd);
req->fat_operation = cpu_to_le32(RETRIEVE_FAT);
req->read_log_offset = cpu_to_le32(log_offset);
@@ -1485,11 +1420,9 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_FW_VERSION);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_FW_VERSION, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb, NULL);
status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_get_fw_version *resp = embedded_payload(wrb);
@@ -1520,11 +1453,8 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_MODIFY_EQ_DELAY);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL);
req->num_eq = cpu_to_le32(1);
req->delay[0].eq_id = cpu_to_le32(eq_id);
@@ -1555,11 +1485,8 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_NTWK_VLAN_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req), wrb, NULL);
req->interface_id = if_id;
req->promiscuous = promiscuous;
@@ -1582,7 +1509,6 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
struct be_mcc_wrb *wrb;
struct be_dma_mem *mem = &adapter->rx_filter;
struct be_cmd_req_rx_filter *req = mem->va;
- struct be_sge *sge;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -1592,16 +1518,10 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
status = -EBUSY;
goto err;
}
- sge = nonembedded_sgl(wrb);
- sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma));
- sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(mem->size);
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_NTWK_RX_FILTER);
-
memset(req, 0, sizeof(*req));
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
+ wrb, mem);
req->if_id = cpu_to_le32(adapter->if_handle);
if (flags & IFF_PROMISC) {
@@ -1619,7 +1539,14 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
req->if_flags_mask = req->if_flags =
cpu_to_le32(BE_IF_FLAGS_MULTICAST);
- req->mcast_num = cpu_to_le16(netdev_mc_count(adapter->netdev));
+
+ /* Reset mcast promisc mode if already set by setting mask
+ * and not setting flags field
+ */
+ req->if_flags_mask |=
+ cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
+
+ req->mcast_num = cpu_to_le32(netdev_mc_count(adapter->netdev));
netdev_for_each_mc_addr(ha, adapter->netdev)
memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN);
}
@@ -1646,11 +1573,8 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_FLOW_CONTROL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
req->tx_flow_control = cpu_to_le16((u16)tx_fc);
req->rx_flow_control = cpu_to_le16((u16)rx_fc);
@@ -1678,11 +1602,8 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_FLOW_CONTROL);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -1711,11 +1632,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_QUERY_FIRMWARE_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req), wrb, NULL);
status = be_mbox_notify_wait(adapter);
if (!status) {
@@ -1742,11 +1660,8 @@ int be_cmd_reset_function(struct be_adapter *adapter)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_FUNCTION_RESET);
-
- be_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_FUNCTION_RESET, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb, NULL);
status = be_mbox_notify_wait(adapter);
@@ -1768,11 +1683,8 @@ int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, u16 table_size)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_ETH_RSS_CONFIG);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_RSS_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
req->if_id = cpu_to_le32(adapter->if_handle);
req->enable_rss = cpu_to_le16(RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4);
@@ -1804,11 +1716,8 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_ENABLE_DISABLE_BEACON);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req), wrb, NULL);
req->port_num = port_num;
req->beacon_state = state;
@@ -1838,11 +1747,8 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_GET_BEACON_STATE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req), wrb, NULL);
req->port_num = port_num;
@@ -1879,13 +1785,10 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(struct lancer_cmd_req_write_object),
- true, 1, OPCODE_COMMON_WRITE_OBJECT);
- wrb->tag1 = CMD_SUBSYSTEM_COMMON;
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_WRITE_OBJECT,
- sizeof(struct lancer_cmd_req_write_object));
+ sizeof(struct lancer_cmd_req_write_object), wrb,
+ NULL);
ctxt = &req->context;
AMAP_SET_BITS(struct amap_lancer_write_obj_context,
@@ -1938,7 +1841,6 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
{
struct be_mcc_wrb *wrb;
struct be_cmd_write_flashrom *req;
- struct be_sge *sge;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -1950,17 +1852,9 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
goto err_unlock;
}
req = cmd->va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
- OPCODE_COMMON_WRITE_FLASHROM);
- wrb->tag1 = CMD_SUBSYSTEM_COMMON;
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_WRITE_FLASHROM, cmd->size);
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
- sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb, cmd);
req->params.op_type = cpu_to_le32(flash_type);
req->params.op_code = cpu_to_le32(flash_opcode);
@@ -1998,11 +1892,8 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
}
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req)+4, true, 0,
- OPCODE_COMMON_READ_FLASHROM);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4, wrb, NULL);
req->params.op_type = cpu_to_le32(IMG_TYPE_REDBOOT);
req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT);
@@ -2023,7 +1914,6 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_acpi_wol_magic_config *req;
- struct be_sge *sge;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -2034,19 +1924,12 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
goto err;
}
req = nonemb_cmd->va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
- OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req), wrb,
+ nonemb_cmd);
memcpy(req->magic_mac, mac, ETH_ALEN);
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
-
status = be_mcc_notify_wait(adapter);
err:
@@ -2071,12 +1954,9 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_LOWLEVEL_SET_LOOPBACK_MODE);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_SET_LOOPBACK_MODE,
- sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req), wrb,
+ NULL);
req->src_port = port_num;
req->dest_port = port_num;
@@ -2106,11 +1986,8 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_LOWLEVEL_LOOPBACK_TEST);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb, NULL);
req->hdr.timeout = cpu_to_le32(4);
req->pattern = cpu_to_le64(pattern);
@@ -2136,7 +2013,6 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_ddrdma_test *req;
- struct be_sge *sge;
int status;
int i, j = 0;
@@ -2148,15 +2024,8 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
goto err;
}
req = cmd->va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
- OPCODE_LOWLEVEL_HOST_DDR_DMA);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
- OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size);
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
- sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb, cmd);
req->pattern = cpu_to_le64(pattern);
req->byte_count = cpu_to_le32(byte_cnt);
@@ -2201,15 +2070,9 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
req = nonemb_cmd->va;
sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_SEEPROM_READ);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SEEPROM_READ, sizeof(*req));
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
+ nonemb_cmd);
status = be_mcc_notify_wait(adapter);
@@ -2223,7 +2086,6 @@ int be_cmd_get_phy_info(struct be_adapter *adapter,
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_phy_info *req;
- struct be_sge *sge;
struct be_dma_mem cmd;
int status;
@@ -2244,18 +2106,10 @@ int be_cmd_get_phy_info(struct be_adapter *adapter,
}
req = cmd.va;
- sge = nonembedded_sgl(wrb);
-
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
- OPCODE_COMMON_GET_PHY_DETAILS);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_PHY_DETAILS,
- sizeof(*req));
-
- sge->pa_hi = cpu_to_le32(upper_32_bits(cmd.dma));
- sge->pa_lo = cpu_to_le32(cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(cmd.size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
+ wrb, &cmd);
status = be_mcc_notify_wait(adapter);
if (!status) {
@@ -2288,11 +2142,8 @@ int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_QOS);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_QOS, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
req->hdr.domain = domain;
req->valid_bits = cpu_to_le32(BE_QOS_BITS_NIC);
@@ -2310,7 +2161,6 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
struct be_mcc_wrb *wrb;
struct be_cmd_req_cntl_attribs *req;
struct be_cmd_resp_cntl_attribs *resp;
- struct be_sge *sge;
int status;
int payload_len = max(sizeof(*req), sizeof(*resp));
struct mgmt_controller_attrib *attribs;
@@ -2335,15 +2185,10 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
goto err;
}
req = attribs_cmd.va;
- sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, payload_len, false, 1,
- OPCODE_COMMON_GET_CNTL_ATTRIBUTES);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len);
- sge->pa_hi = cpu_to_le32(upper_32_bits(attribs_cmd.dma));
- sge->pa_lo = cpu_to_le32(attribs_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(attribs_cmd.size);
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len, wrb,
+ &attribs_cmd);
status = be_mbox_notify_wait(adapter);
if (!status) {
@@ -2376,11 +2221,8 @@ int be_cmd_req_native_mode(struct be_adapter *adapter)
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
- OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP);
-
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req));
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req), wrb, NULL);
req->valid_cap_flags = cpu_to_le32(CAPABILITY_SW_TIMESTAMPS |
CAPABILITY_BE3_NATIVE_ERX_API);
diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h
index fbc8a915519e..f2c89e3ccabd 100644
--- a/drivers/net/ethernet/emulex/benet/be_hw.h
+++ b/drivers/net/ethernet/emulex/benet/be_hw.h
@@ -48,6 +48,8 @@
/* Lancer SLIPORT_CONTROL SLIPORT_STATUS registers */
#define SLIPORT_STATUS_OFFSET 0x404
#define SLIPORT_CONTROL_OFFSET 0x408
+#define SLIPORT_ERROR1_OFFSET 0x40C
+#define SLIPORT_ERROR2_OFFSET 0x410
#define SLIPORT_STATUS_ERR_MASK 0x80000000
#define SLIPORT_STATUS_RN_MASK 0x01000000
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index d6a232a300ad..e0aed188c57f 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -229,27 +229,29 @@ static int be_mac_addr_set(struct net_device *netdev, void *p)
struct be_adapter *adapter = netdev_priv(netdev);
struct sockaddr *addr = p;
int status = 0;
+ u8 current_mac[ETH_ALEN];
+ u32 pmac_id = adapter->pmac_id;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- /* MAC addr configuration will be done in hardware for VFs
- * by their corresponding PFs. Just copy to netdev addr here
- */
- if (!be_physfn(adapter))
- goto netdev_addr;
-
- status = be_cmd_pmac_del(adapter, adapter->if_handle,
- adapter->pmac_id, 0);
+ status = be_cmd_mac_addr_query(adapter, current_mac,
+ MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
if (status)
- return status;
+ goto err;
- status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
+ if (memcmp(addr->sa_data, current_mac, ETH_ALEN)) {
+ status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
adapter->if_handle, &adapter->pmac_id, 0);
-netdev_addr:
- if (!status)
- memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ if (status)
+ goto err;
+ be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0);
+ }
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ return 0;
+err:
+ dev_err(&adapter->pdev->dev, "MAC %pM set Failed\n", addr->sa_data);
return status;
}
@@ -1903,6 +1905,8 @@ loop_continue:
be_rx_stats_update(rxo, rxcp);
}
+ be_cq_notify(adapter, rx_cq->id, false, work_done);
+
/* Refill the queue */
if (work_done && atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM)
be_post_rx_frags(rxo, GFP_ATOMIC);
@@ -1910,10 +1914,8 @@ loop_continue:
/* All consumed */
if (work_done < budget) {
napi_complete(napi);
- be_cq_notify(adapter, rx_cq->id, true, work_done);
- } else {
- /* More to be consumed; continue with interrupts disabled */
- be_cq_notify(adapter, rx_cq->id, false, work_done);
+ /* Arm CQ */
+ be_cq_notify(adapter, rx_cq->id, true, 0);
}
return work_done;
}
@@ -1975,42 +1977,62 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
void be_detect_dump_ue(struct be_adapter *adapter)
{
- u32 ue_status_lo, ue_status_hi, ue_status_lo_mask, ue_status_hi_mask;
+ u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0;
+ u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
u32 i;
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW, &ue_status_lo);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HIGH, &ue_status_hi);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW_MASK, &ue_status_lo_mask);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HI_MASK, &ue_status_hi_mask);
+ if (lancer_chip(adapter)) {
+ sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
+ if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
+ sliport_err1 = ioread32(adapter->db +
+ SLIPORT_ERROR1_OFFSET);
+ sliport_err2 = ioread32(adapter->db +
+ SLIPORT_ERROR2_OFFSET);
+ }
+ } else {
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_LOW, &ue_lo);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_HIGH, &ue_hi);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
+ pci_read_config_dword(adapter->pdev,
+ PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
- ue_status_lo = (ue_status_lo & (~ue_status_lo_mask));
- ue_status_hi = (ue_status_hi & (~ue_status_hi_mask));
+ ue_lo = (ue_lo & (~ue_lo_mask));
+ ue_hi = (ue_hi & (~ue_hi_mask));
+ }
- if (ue_status_lo || ue_status_hi) {
+ if (ue_lo || ue_hi ||
+ sliport_status & SLIPORT_STATUS_ERR_MASK) {
adapter->ue_detected = true;
adapter->eeh_err = true;
dev_err(&adapter->pdev->dev, "UE Detected!!\n");
}
- if (ue_status_lo) {
- for (i = 0; ue_status_lo; ue_status_lo >>= 1, i++) {
- if (ue_status_lo & 1)
+ if (ue_lo) {
+ for (i = 0; ue_lo; ue_lo >>= 1, i++) {
+ if (ue_lo & 1)
dev_err(&adapter->pdev->dev,
"UE: %s bit set\n", ue_status_low_desc[i]);
}
}
- if (ue_status_hi) {
- for (i = 0; ue_status_hi; ue_status_hi >>= 1, i++) {
- if (ue_status_hi & 1)
+ if (ue_hi) {
+ for (i = 0; ue_hi; ue_hi >>= 1, i++) {
+ if (ue_hi & 1)
dev_err(&adapter->pdev->dev,
"UE: %s bit set\n", ue_status_hi_desc[i]);
}
}
+ if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
+ dev_err(&adapter->pdev->dev,
+ "sliport status 0x%x\n", sliport_status);
+ dev_err(&adapter->pdev->dev,
+ "sliport error1 0x%x\n", sliport_err1);
+ dev_err(&adapter->pdev->dev,
+ "sliport error2 0x%x\n", sliport_err2);
+ }
}
static void be_worker(struct work_struct *work)
@@ -2020,7 +2042,7 @@ static void be_worker(struct work_struct *work)
struct be_rx_obj *rxo;
int i;
- if (!adapter->ue_detected && !lancer_chip(adapter))
+ if (!adapter->ue_detected)
be_detect_dump_ue(adapter);
/* when interrupts are not yet enabled, just reap any pending
diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c
index 40e1a175fceb..ba82a266051d 100644
--- a/drivers/net/ethernet/i825xx/3c505.c
+++ b/drivers/net/ethernet/i825xx/3c505.c
@@ -126,15 +126,13 @@
*
*********************************************************/
-#define filename __FILE__
-
#define timeout_msg "*** timeout at %s:%s (line %d) ***\n"
#define TIMEOUT_MSG(lineno) \
- pr_notice(timeout_msg, filename, __func__, (lineno))
+ pr_notice(timeout_msg, __FILE__, __func__, (lineno))
#define invalid_pcb_msg "*** invalid pcb length %d at %s:%s (line %d) ***\n"
#define INVALID_PCB_MSG(len) \
- pr_notice(invalid_pcb_msg, (len), filename, __func__, __LINE__)
+ pr_notice(invalid_pcb_msg, (len), __FILE__, __func__, __LINE__)
#define search_msg "%s: Looking for 3c505 adapter at address %#x..."
diff --git a/drivers/net/ethernet/i825xx/Kconfig b/drivers/net/ethernet/i825xx/Kconfig
index 2be46986cbe2..ca1ae985c6df 100644
--- a/drivers/net/ethernet/i825xx/Kconfig
+++ b/drivers/net/ethernet/i825xx/Kconfig
@@ -85,7 +85,7 @@ config APRICOT
config BVME6000_NET
tristate "BVME6000 Ethernet support"
- depends on BVME6000MVME16x
+ depends on BVME6000
---help---
This is the driver for the Ethernet interface on BVME4000 and
BVME6000 VME boards. Say Y here to include the driver for this chip
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index ae17cd1a907f..5a2fdf7a00c8 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -2810,6 +2810,10 @@ static int __devinit e100_probe(struct pci_dev *pdev,
e100_get_defaults(nic);
+ /* D100 MAC doesn't allow rx of vlan packets with normal MTU */
+ if (nic->mac < mac_82558_D101_A4)
+ netdev->features |= NETIF_F_VLAN_CHALLENGED;
+
/* locks must be initialized before calling hw_reset */
spin_lock_init(&nic->cb_lock);
spin_lock_init(&nic->cmd_lock);
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 6a17c62cb86f..e2a80a283fd3 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -866,8 +866,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
if (test_and_set_bit(__E1000_ACCESS_SHARED_RESOURCE,
&hw->adapter->state)) {
- WARN(1, "e1000e: %s: contention for Phy access\n",
- hw->adapter->netdev->name);
+ e_dbg("contention for Phy access\n");
return -E1000_ERR_PHY;
}
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index 7edf31efe756..b17d7c20f817 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1687,7 +1687,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
if (ret_val)
goto out;
- is_cm = !(phy_data & I347AT4_PCDC_CABLE_LENGTH_UNIT);
+ is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
/* Populate the phy structure with cable length in meters */
phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index 834f044be4c3..f1365fef4ed2 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -3344,7 +3344,7 @@ static u8 ixgbe_calculate_checksum(u8 *buffer, u32 length)
static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer,
u32 length)
{
- u32 hicr, i;
+ u32 hicr, i, bi;
u32 hdr_size = sizeof(struct ixgbe_hic_hdr);
u8 buf_len, dword_len;
@@ -3398,9 +3398,9 @@ static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer,
dword_len = hdr_size >> 2;
/* first pull in the header so we know the buffer length */
- for (i = 0; i < dword_len; i++) {
- buffer[i] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, i);
- le32_to_cpus(&buffer[i]);
+ for (bi = 0; bi < dword_len; bi++) {
+ buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi);
+ le32_to_cpus(&buffer[bi]);
}
/* If there is any thing in data position pull it in */
@@ -3414,12 +3414,14 @@ static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer,
goto out;
}
- /* Calculate length in DWORDs, add one for odd lengths */
- dword_len = (buf_len + 1) >> 2;
+ /* Calculate length in DWORDs, add 3 for odd lengths */
+ dword_len = (buf_len + 3) >> 2;
- /* Pull in the rest of the buffer (i is where we left off)*/
- for (; i < buf_len; i++)
- buffer[i] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, i);
+ /* Pull in the rest of the buffer (bi is where we left off)*/
+ for (; bi <= dword_len; bi++) {
+ buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi);
+ le32_to_cpus(&buffer[bi]);
+ }
out:
return ret_val;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
index 3631d639d86a..33b93ffb87cb 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
@@ -561,11 +561,12 @@ static int ixgbe_dcbnl_ieee_getets(struct net_device *dev,
struct ixgbe_adapter *adapter = netdev_priv(dev);
struct ieee_ets *my_ets = adapter->ixgbe_ieee_ets;
+ ets->ets_cap = adapter->dcb_cfg.num_tcs.pg_tcs;
+
/* No IEEE PFC settings available */
if (!my_ets)
- return -EINVAL;
+ return 0;
- ets->ets_cap = adapter->dcb_cfg.num_tcs.pg_tcs;
ets->cbs = my_ets->cbs;
memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
@@ -621,11 +622,12 @@ static int ixgbe_dcbnl_ieee_getpfc(struct net_device *dev,
struct ieee_pfc *my_pfc = adapter->ixgbe_ieee_pfc;
int i;
+ pfc->pfc_cap = adapter->dcb_cfg.num_tcs.pfc_tcs;
+
/* No IEEE PFC settings available */
if (!my_pfc)
- return -EINVAL;
+ return 0;
- pfc->pfc_cap = adapter->dcb_cfg.num_tcs.pfc_tcs;
pfc->pfc_en = my_pfc->pfc_en;
pfc->mbc = my_pfc->mbc;
pfc->delay = my_pfc->delay;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 09b8e88b2999..8ef92d1a6aa1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -3345,34 +3345,25 @@ static void ixgbe_configure_dcb(struct ixgbe_adapter *adapter)
hw->mac.ops.set_vfta(&adapter->hw, 0, 0, true);
- /* reconfigure the hardware */
- if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE) {
#ifdef IXGBE_FCOE
- if (adapter->netdev->features & NETIF_F_FCOE_MTU)
- max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE);
+ if (adapter->netdev->features & NETIF_F_FCOE_MTU)
+ max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE);
#endif
+
+ /* reconfigure the hardware */
+ if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE) {
ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame,
DCB_TX_CONFIG);
ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame,
DCB_RX_CONFIG);
ixgbe_dcb_hw_config(hw, &adapter->dcb_cfg);
- } else {
- struct net_device *dev = adapter->netdev;
-
- if (adapter->ixgbe_ieee_ets) {
- struct ieee_ets *ets = adapter->ixgbe_ieee_ets;
- int max_frame = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
-
- ixgbe_dcb_hw_ets(&adapter->hw, ets, max_frame);
- }
-
- if (adapter->ixgbe_ieee_pfc) {
- struct ieee_pfc *pfc = adapter->ixgbe_ieee_pfc;
- u8 *prio_tc = adapter->ixgbe_ieee_ets->prio_tc;
-
- ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc->pfc_en,
- prio_tc);
- }
+ } else if (adapter->ixgbe_ieee_ets && adapter->ixgbe_ieee_pfc) {
+ ixgbe_dcb_hw_ets(&adapter->hw,
+ adapter->ixgbe_ieee_ets,
+ max_frame);
+ ixgbe_dcb_hw_pfc_config(&adapter->hw,
+ adapter->ixgbe_ieee_pfc->pfc_en,
+ adapter->ixgbe_ieee_ets->prio_tc);
}
/* Enable RSS Hash per TC */
@@ -6125,7 +6116,6 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter)
autoneg = hw->phy.autoneg_advertised;
if ((!autoneg) && (hw->mac.ops.get_link_capabilities))
hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiation);
- hw->mac.autotry_restart = false;
if (hw->mac.ops.setup_link)
hw->mac.ops.setup_link(hw, autoneg, negotiation, true);
@@ -7589,13 +7579,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
goto err_eeprom;
}
- /* power down the optics for multispeed fiber and 82599 SFP+ fiber */
- if (hw->mac.ops.disable_tx_laser &&
- ((hw->phy.multispeed_fiber) ||
- ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
- (hw->mac.type == ixgbe_mac_82599EB))))
- hw->mac.ops.disable_tx_laser(hw);
-
setup_timer(&adapter->service_timer, &ixgbe_service_timer,
(unsigned long) adapter);
@@ -7693,6 +7676,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
if (err)
goto err_register;
+ /* power down the optics for multispeed fiber and 82599 SFP+ fiber */
+ if (hw->mac.ops.disable_tx_laser &&
+ ((hw->phy.multispeed_fiber) ||
+ ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
+ (hw->mac.type == ixgbe_mac_82599EB))))
+ hw->mac.ops.disable_tx_laser(hw);
+
/* carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
index 5a7e1eb33599..4a5d8897faab 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
@@ -42,10 +42,12 @@ int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
int ixgbe_ndo_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivi);
void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter);
+#ifdef CONFIG_PCI_IOV
void ixgbe_disable_sriov(struct ixgbe_adapter *adapter);
void ixgbe_enable_sriov(struct ixgbe_adapter *adapter,
const struct ixgbe_info *ii);
int ixgbe_check_vf_assignment(struct ixgbe_adapter *adapter);
+#endif
#endif /* _IXGBE_SRIOV_H_ */
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 5e92cc2079bd..4c8e19951d57 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -54,7 +54,7 @@ char ixgbevf_driver_name[] = "ixgbevf";
static const char ixgbevf_driver_string[] =
"Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver";
-#define DRV_VERSION "2.1.0-k"
+#define DRV_VERSION "2.2.0-k"
const char ixgbevf_driver_version[] = DRV_VERSION;
static char ixgbevf_copyright[] =
"Copyright (c) 2009 - 2010 Intel Corporation.";
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 1ad1f6029af8..869a2c220a7b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -484,7 +484,7 @@ static void mlx4_free_eq(struct mlx4_dev *dev,
mlx4_mtt_cleanup(dev, &eq->mtt);
for (i = 0; i < npages; ++i)
- pci_free_consistent(dev->pdev, PAGE_SIZE,
+ dma_free_coherent(&dev->pdev->dev, PAGE_SIZE,
eq->page_list[i].buf,
eq->page_list[i].map);
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index ed452ddfe342..abdfbacab4a6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -205,6 +205,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
#define QUERY_DEV_CAP_MAX_MCG_OFFSET 0x63
#define QUERY_DEV_CAP_RSVD_PD_OFFSET 0x64
#define QUERY_DEV_CAP_MAX_PD_OFFSET 0x65
+#define QUERY_DEV_CAP_RSVD_XRC_OFFSET 0x66
+#define QUERY_DEV_CAP_MAX_XRC_OFFSET 0x67
#define QUERY_DEV_CAP_MAX_COUNTERS_OFFSET 0x68
#define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80
#define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82
@@ -319,6 +321,10 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev_cap->reserved_pds = field >> 4;
MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PD_OFFSET);
dev_cap->max_pds = 1 << (field & 0x3f);
+ MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_XRC_OFFSET);
+ dev_cap->reserved_xrcds = field >> 4;
+ MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PD_OFFSET);
+ dev_cap->max_xrcds = 1 << (field & 0x1f);
MLX4_GET(size, outbox, QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET);
dev_cap->rdmarc_entry_sz = size;
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h
index 1e8ecc3708e2..bf5ec2286528 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.h
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.h
@@ -93,6 +93,8 @@ struct mlx4_dev_cap {
int max_mcgs;
int reserved_pds;
int max_pds;
+ int reserved_xrcds;
+ int max_xrcds;
int qpc_entry_sz;
int rdmarc_entry_sz;
int altc_entry_sz;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index f0ee35df4dd7..94bbc85a532d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -96,6 +96,8 @@ MODULE_PARM_DESC(log_num_mac, "Log2 max number of MACs per ETH port (1-7)");
static int log_num_vlan;
module_param_named(log_num_vlan, log_num_vlan, int, 0444);
MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)");
+/* Log2 max number of VLANs per ETH port (0-7) */
+#define MLX4_LOG_NUM_VLANS 7
static int use_prio;
module_param_named(use_prio, use_prio, bool, 0444);
@@ -220,6 +222,10 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.reserved_mrws = dev_cap->reserved_mrws;
dev->caps.reserved_uars = dev_cap->reserved_uars;
dev->caps.reserved_pds = dev_cap->reserved_pds;
+ dev->caps.reserved_xrcds = (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) ?
+ dev_cap->reserved_xrcds : 0;
+ dev->caps.max_xrcds = (dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC) ?
+ dev_cap->max_xrcds : 0;
dev->caps.mtt_entry_sz = dev->caps.mtts_per_seg * dev_cap->mtt_entry_sz;
dev->caps.max_msg_sz = dev_cap->max_msg_sz;
dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1);
@@ -230,7 +236,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.max_gso_sz = dev_cap->max_gso_sz;
dev->caps.log_num_macs = log_num_mac;
- dev->caps.log_num_vlans = log_num_vlan;
+ dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS;
dev->caps.log_num_prios = use_prio ? 3 : 0;
for (i = 1; i <= dev->caps.num_ports; ++i) {
@@ -912,11 +918,18 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
goto err_kar_unmap;
}
+ err = mlx4_init_xrcd_table(dev);
+ if (err) {
+ mlx4_err(dev, "Failed to initialize "
+ "reliable connection domain table, aborting.\n");
+ goto err_pd_table_free;
+ }
+
err = mlx4_init_mr_table(dev);
if (err) {
mlx4_err(dev, "Failed to initialize "
"memory region table, aborting.\n");
- goto err_pd_table_free;
+ goto err_xrcd_table_free;
}
err = mlx4_init_eq_table(dev);
@@ -998,6 +1011,13 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
"ib capabilities (%d). Continuing with "
"caps = 0\n", port, err);
dev->caps.ib_port_def_cap[port] = ib_port_default_caps;
+
+ err = mlx4_check_ext_port_caps(dev, port);
+ if (err)
+ mlx4_warn(dev, "failed to get port %d extended "
+ "port capabilities support info (%d)."
+ " Assuming not supported\n", port, err);
+
err = mlx4_SET_PORT(dev, port);
if (err) {
mlx4_err(dev, "Failed to set port %d, aborting\n",
@@ -1033,6 +1053,9 @@ err_eq_table_free:
err_mr_table_free:
mlx4_cleanup_mr_table(dev);
+err_xrcd_table_free:
+ mlx4_cleanup_xrcd_table(dev);
+
err_pd_table_free:
mlx4_cleanup_pd_table(dev);
@@ -1355,6 +1378,7 @@ err_port:
mlx4_cmd_use_polling(dev);
mlx4_cleanup_eq_table(dev);
mlx4_cleanup_mr_table(dev);
+ mlx4_cleanup_xrcd_table(dev);
mlx4_cleanup_pd_table(dev);
mlx4_cleanup_uar_table(dev);
@@ -1416,6 +1440,7 @@ static void mlx4_remove_one(struct pci_dev *pdev)
mlx4_cmd_use_polling(dev);
mlx4_cleanup_eq_table(dev);
mlx4_cleanup_mr_table(dev);
+ mlx4_cleanup_xrcd_table(dev);
mlx4_cleanup_pd_table(dev);
iounmap(priv->kar);
@@ -1489,10 +1514,9 @@ static int __init mlx4_verify_params(void)
return -1;
}
- if ((log_num_vlan < 0) || (log_num_vlan > 7)) {
- pr_warning("mlx4_core: bad num_vlan: %d\n", log_num_vlan);
- return -1;
- }
+ if (log_num_vlan != 0)
+ pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
+ MLX4_LOG_NUM_VLANS);
if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) {
pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index a2fcd8402d37..5dfa68ffc11c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -335,6 +335,7 @@ struct mlx4_priv {
struct mlx4_cmd cmd;
struct mlx4_bitmap pd_bitmap;
+ struct mlx4_bitmap xrcd_bitmap;
struct mlx4_uar_table uar_table;
struct mlx4_mr_table mr_table;
struct mlx4_cq_table cq_table;
@@ -384,6 +385,7 @@ int mlx4_alloc_eq_table(struct mlx4_dev *dev);
void mlx4_free_eq_table(struct mlx4_dev *dev);
int mlx4_init_pd_table(struct mlx4_dev *dev);
+int mlx4_init_xrcd_table(struct mlx4_dev *dev);
int mlx4_init_uar_table(struct mlx4_dev *dev);
int mlx4_init_mr_table(struct mlx4_dev *dev);
int mlx4_init_eq_table(struct mlx4_dev *dev);
@@ -393,6 +395,7 @@ int mlx4_init_srq_table(struct mlx4_dev *dev);
int mlx4_init_mcg_table(struct mlx4_dev *dev);
void mlx4_cleanup_pd_table(struct mlx4_dev *dev);
+void mlx4_cleanup_xrcd_table(struct mlx4_dev *dev);
void mlx4_cleanup_uar_table(struct mlx4_dev *dev);
void mlx4_cleanup_mr_table(struct mlx4_dev *dev);
void mlx4_cleanup_eq_table(struct mlx4_dev *dev);
@@ -450,6 +453,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table);
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port);
int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps);
+int mlx4_check_ext_port_caps(struct mlx4_dev *dev, u8 port);
int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
enum mlx4_protocol prot, enum mlx4_steer_type steer);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index fca66165110e..8fda331c65df 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -581,8 +581,9 @@ extern const struct ethtool_ops mlx4_en_ethtool_ops;
* printk / logging functions
*/
+__printf(3, 4)
int en_print(const char *level, const struct mlx4_en_priv *priv,
- const char *format, ...) __attribute__ ((format (printf, 3, 4)));
+ const char *format, ...);
#define en_dbg(mlevel, priv, format, arg...) \
do { \
diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c
index 9c188bdd7f4f..ab639cfef78e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mr.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mr.c
@@ -139,7 +139,7 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
if (!buddy->bits || !buddy->num_free)
goto err_out;
diff --git a/drivers/net/ethernet/mellanox/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c
index 1286b886dcea..3736163e30e9 100644
--- a/drivers/net/ethernet/mellanox/mlx4/pd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/pd.c
@@ -61,6 +61,24 @@ void mlx4_pd_free(struct mlx4_dev *dev, u32 pdn)
}
EXPORT_SYMBOL_GPL(mlx4_pd_free);
+int mlx4_xrcd_alloc(struct mlx4_dev *dev, u32 *xrcdn)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ *xrcdn = mlx4_bitmap_alloc(&priv->xrcd_bitmap);
+ if (*xrcdn == -1)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_xrcd_alloc);
+
+void mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn)
+{
+ mlx4_bitmap_free(&mlx4_priv(dev)->xrcd_bitmap, xrcdn);
+}
+EXPORT_SYMBOL_GPL(mlx4_xrcd_free);
+
int mlx4_init_pd_table(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -74,6 +92,18 @@ void mlx4_cleanup_pd_table(struct mlx4_dev *dev)
mlx4_bitmap_cleanup(&mlx4_priv(dev)->pd_bitmap);
}
+int mlx4_init_xrcd_table(struct mlx4_dev *dev)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ return mlx4_bitmap_init(&priv->xrcd_bitmap, (1 << 16),
+ (1 << 16) - 1, dev->caps.reserved_xrcds + 1, 0);
+}
+
+void mlx4_cleanup_xrcd_table(struct mlx4_dev *dev)
+{
+ mlx4_bitmap_cleanup(&mlx4_priv(dev)->xrcd_bitmap);
+}
int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar)
{
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index 163a314c148f..a44f080fdfe5 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -148,22 +148,26 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn, u8 wrap)
if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) {
err = mlx4_uc_steer_add(dev, port, mac, qpn, 1);
- if (!err) {
- entry = kmalloc(sizeof *entry, GFP_KERNEL);
- if (!entry) {
- mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
- return -ENOMEM;
- }
- entry->mac = mac;
- err = radix_tree_insert(&info->mac_tree, *qpn, entry);
- if (err) {
- mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
- return err;
- }
- } else
+ if (err)
return err;
+
+ entry = kmalloc(sizeof *entry, GFP_KERNEL);
+ if (!entry) {
+ mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
+ return -ENOMEM;
+ }
+
+ entry->mac = mac;
+ err = radix_tree_insert(&info->mac_tree, *qpn, entry);
+ if (err) {
+ kfree(entry);
+ mlx4_uc_steer_release(dev, port, mac, *qpn, 1);
+ return err;
+ }
}
+
mlx4_dbg(dev, "Registering MAC: 0x%llx\n", (unsigned long long) mac);
+
mutex_lock(&table->mutex);
for (i = 0; i < MLX4_MAX_MAC_NUM - 1; i++) {
if (free < 0 && !table->refs[i]) {
@@ -465,6 +469,48 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
return err;
}
+int mlx4_check_ext_port_caps(struct mlx4_dev *dev, u8 port)
+{
+ struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
+ u8 *inbuf, *outbuf;
+ int err, packet_error;
+
+ inmailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(inmailbox))
+ return PTR_ERR(inmailbox);
+
+ outmailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(outmailbox)) {
+ mlx4_free_cmd_mailbox(dev, inmailbox);
+ return PTR_ERR(outmailbox);
+ }
+
+ inbuf = inmailbox->buf;
+ outbuf = outmailbox->buf;
+ memset(inbuf, 0, 256);
+ memset(outbuf, 0, 256);
+ inbuf[0] = 1;
+ inbuf[1] = 1;
+ inbuf[2] = 1;
+ inbuf[3] = 1;
+
+ *(__be16 *) (&inbuf[16]) = MLX4_ATTR_EXTENDED_PORT_INFO;
+ *(__be32 *) (&inbuf[20]) = cpu_to_be32(port);
+
+ err = mlx4_cmd_box(dev, inmailbox->dma, outmailbox->dma, port, 3,
+ MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C);
+
+ packet_error = be16_to_cpu(*(__be16 *) (outbuf + 4));
+
+ dev->caps.ext_port_cap[port] = (!err && !packet_error) ?
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO
+ : 0;
+
+ mlx4_free_cmd_mailbox(dev, inmailbox);
+ mlx4_free_cmd_mailbox(dev, outmailbox);
+ return err;
+}
+
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port)
{
struct mlx4_cmd_mailbox *mailbox;
diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c
index ec9350e5f21a..51c53898c35f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx4/qp.c
@@ -280,6 +280,9 @@ int mlx4_init_qp_table(struct mlx4_dev *dev)
* We reserve 2 extra QPs per port for the special QPs. The
* block of special QPs must be aligned to a multiple of 8, so
* round up.
+ *
+ * We also reserve the MSB of the 24-bit QP number to indicate
+ * that a QP is an XRC QP.
*/
dev->caps.sqp_start =
ALIGN(dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW], 8);
diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c
index 3b07b80a0456..a20b141dbb5c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/srq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/srq.c
@@ -40,20 +40,20 @@
struct mlx4_srq_context {
__be32 state_logsize_srqn;
u8 logstride;
- u8 reserved1[3];
- u8 pg_offset;
- u8 reserved2[3];
- u32 reserved3;
+ u8 reserved1;
+ __be16 xrcd;
+ __be32 pg_offset_cqn;
+ u32 reserved2;
u8 log_page_size;
- u8 reserved4[2];
+ u8 reserved3[2];
u8 mtt_base_addr_h;
__be32 mtt_base_addr_l;
__be32 pd;
__be16 limit_watermark;
__be16 wqe_cnt;
- u16 reserved5;
+ u16 reserved4;
__be16 wqe_counter;
- u32 reserved6;
+ u32 reserved5;
__be64 db_rec_addr;
};
@@ -109,8 +109,8 @@ static int mlx4_QUERY_SRQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox
MLX4_CMD_TIME_CLASS_A);
}
-int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt,
- u64 db_rec, struct mlx4_srq *srq)
+int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
+ struct mlx4_mtt *mtt, u64 db_rec, struct mlx4_srq *srq)
{
struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
struct mlx4_cmd_mailbox *mailbox;
@@ -148,6 +148,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt,
srq_context->state_logsize_srqn = cpu_to_be32((ilog2(srq->max) << 24) |
srq->srqn);
srq_context->logstride = srq->wqe_shift - 4;
+ srq_context->xrcd = cpu_to_be16(xrcd);
+ srq_context->pg_offset_cqn = cpu_to_be32(cqn & 0xffffff);
srq_context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT;
mtt_addr = mlx4_mtt_addr(dev, mtt);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 2fd1ba8fee4a..7ed53dbb8646 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -36,8 +36,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 24
-#define QLCNIC_LINUX_VERSIONID "5.0.24"
+#define _QLCNIC_LINUX_SUBVERSION 25
+#define QLCNIC_LINUX_VERSIONID "5.0.25"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 5d8bec283267..8aa1c6e8667b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -935,31 +935,49 @@ static int qlcnic_set_led(struct net_device *dev,
{
struct qlcnic_adapter *adapter = netdev_priv(dev);
int max_sds_rings = adapter->max_sds_rings;
+ int err = -EIO, active = 1;
+
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ netdev_warn(dev, "LED test not supported for non "
+ "privilege function\n");
+ return -EOPNOTSUPP;
+ }
switch (state) {
case ETHTOOL_ID_ACTIVE:
if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state))
return -EBUSY;
- if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
- if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
- return -EIO;
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+ break;
- if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) {
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- return -EIO;
- }
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST))
+ break;
set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
}
- if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0)
- return 0;
+ if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) {
+ err = 0;
+ break;
+ }
dev_err(&adapter->pdev->dev,
"Failed to set LED blink state.\n");
break;
case ETHTOOL_ID_INACTIVE:
+ active = 0;
+
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+ break;
+
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST))
+ break;
+ set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
+ }
+
if (adapter->nic_ops->config_led(adapter, 0, 0xf))
dev_err(&adapter->pdev->dev,
"Failed to reset LED blink state.\n");
@@ -970,14 +988,13 @@ static int qlcnic_set_led(struct net_device *dev,
return -EINVAL;
}
- if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) {
+ if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
qlcnic_diag_free_res(dev, max_sds_rings);
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- }
- clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+ if (!active || err)
+ clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
- return -EIO;
+ return err;
}
static void
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
index 92bc8ce9b287..a52819303d1b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
@@ -407,7 +407,9 @@ enum {
#define QLCNIC_CRB_SRE QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SRE)
#define QLCNIC_CRB_ROMUSB \
QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_ROMUSB)
+#define QLCNIC_CRB_EPG QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_EG)
#define QLCNIC_CRB_I2Q QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2Q)
+#define QLCNIC_CRB_TIMER QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_TIMR)
#define QLCNIC_CRB_I2C0 QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2C0)
#define QLCNIC_CRB_SMB QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SMB)
#define QLCNIC_CRB_MAX QLCNIC_PCI_CRB_WINDOW(64)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index 74e9d7b94965..bcb81e47543a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -566,7 +566,7 @@ int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
return -EIO;
if (qlcnic_nic_set_promisc(adapter, VPORT_MISS_MODE_ACCEPT_ALL)) {
- qlcnic_set_fw_loopback(adapter, mode);
+ qlcnic_set_fw_loopback(adapter, 0);
return -EIO;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
index 312c1c37889d..38669583840c 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
@@ -422,9 +422,53 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter)
QLCWR32(adapter, CRB_CMDPEG_STATE, 0);
QLCWR32(adapter, CRB_RCVPEG_STATE, 0);
- qlcnic_rom_lock(adapter);
- QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff);
+ /* Halt all the indiviual PEGs and other blocks */
+ /* disable all I2Q */
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x10, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x14, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x18, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x1c, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x20, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x24, 0x0);
+
+ /* disable all niu interrupts */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x40, 0xff);
+ /* disable xge rx/tx */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x70000, 0x00);
+ /* disable xg1 rx/tx */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x80000, 0x00);
+ /* disable sideband mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0x90000, 0x00);
+ /* disable ap0 mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0xa0000, 0x00);
+ /* disable ap1 mac */
+ QLCWR32(adapter, QLCNIC_CRB_NIU + 0xb0000, 0x00);
+
+ /* halt sre */
+ val = QLCRD32(adapter, QLCNIC_CRB_SRE + 0x1000);
+ QLCWR32(adapter, QLCNIC_CRB_SRE + 0x1000, val & (~(0x1)));
+
+ /* halt epg */
+ QLCWR32(adapter, QLCNIC_CRB_EPG + 0x1300, 0x1);
+
+ /* halt timers */
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x0, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x8, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x10, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x18, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x100, 0x0);
+ QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x200, 0x0);
+ /* halt pegs */
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c, 1);
+ QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c, 1);
+ msleep(20);
+
qlcnic_rom_unlock(adapter);
+ /* big hammer don't reset CAM block on reset */
+ QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff);
/* Init HW CRB block */
if (qlcnic_rom_fast_read(adapter, 0, &n) != 0 || (n != 0xcafecafe) ||
@@ -522,8 +566,10 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter)
QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x8, 0);
QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0xc, 0);
msleep(1);
+
QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS1, 0);
QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS2, 0);
+
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 106503f118f6..0bd163828e33 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2840,8 +2840,15 @@ qlcnic_fwinit_work(struct work_struct *work)
goto wait_npar;
}
+ if (dev_state == QLCNIC_DEV_INITIALIZING ||
+ dev_state == QLCNIC_DEV_READY) {
+ dev_info(&adapter->pdev->dev, "Detected state change from "
+ "DEV_NEED_RESET, skipping ack check\n");
+ goto skip_ack_check;
+ }
+
if (adapter->fw_wait_cnt++ > adapter->reset_ack_timeo) {
- dev_err(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
+ dev_info(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
adapter->reset_ack_timeo);
goto skip_ack_check;
}
@@ -3497,11 +3504,16 @@ qlcnic_store_beacon(struct device *dev,
{
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
int max_sds_rings = adapter->max_sds_rings;
- int dev_down = 0;
u16 beacon;
u8 b_state, b_rate;
int err;
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ dev_warn(dev, "LED test not supported for non "
+ "privilege function\n");
+ return -EOPNOTSUPP;
+ }
+
if (len != sizeof(u16))
return QL_STATUS_INVALID_PARAM;
@@ -3513,36 +3525,40 @@ qlcnic_store_beacon(struct device *dev,
if (adapter->ahw->beacon_state == b_state)
return len;
+ rtnl_lock();
+
if (!adapter->ahw->beacon_state)
- if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state))
+ if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) {
+ rtnl_unlock();
return -EBUSY;
+ }
+
+ if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+ err = -EIO;
+ goto out;
+ }
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
- if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
- return -EIO;
err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST);
- if (err) {
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
- return err;
- }
- dev_down = 1;
+ if (err)
+ goto out;
+ set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state);
}
err = qlcnic_config_led(adapter, b_state, b_rate);
if (!err) {
- adapter->ahw->beacon_state = b_state;
err = len;
+ adapter->ahw->beacon_state = b_state;
}
- if (dev_down) {
+ if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
qlcnic_diag_free_res(adapter->netdev, max_sds_rings);
- clear_bit(__QLCNIC_RESETTING, &adapter->state);
- }
- if (!b_state)
+ out:
+ if (!adapter->ahw->beacon_state)
clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+ rtnl_unlock();
return err;
}
diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 84083ec6e612..0578859a3c73 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -115,16 +115,4 @@ config R8169
To compile this driver as a module, choose M here: the module
will be called r8169. This is recommended.
-config SC92031
- tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)"
- depends on PCI && EXPERIMENTAL
- select CRC32
- ---help---
- This is a driver for the Fast Ethernet PCI network cards based on
- the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you
- have one of these, say Y here.
-
- To compile this driver as a module, choose M here: the module
- will be called sc92031. This is recommended.
-
endif # NET_VENDOR_REALTEK
diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile
index e48cfb6ac42d..71b1da30ecb5 100644
--- a/drivers/net/ethernet/realtek/Makefile
+++ b/drivers/net/ethernet/realtek/Makefile
@@ -6,4 +6,3 @@ obj-$(CONFIG_8139CP) += 8139cp.o
obj-$(CONFIG_8139TOO) += 8139too.o
obj-$(CONFIG_ATP) += atp.o
obj-$(CONFIG_R8169) += r8169.o
-obj-$(CONFIG_SC92031) += sc92031.o
diff --git a/drivers/net/ethernet/silan/Kconfig b/drivers/net/ethernet/silan/Kconfig
new file mode 100644
index 000000000000..ae1ce170864d
--- /dev/null
+++ b/drivers/net/ethernet/silan/Kconfig
@@ -0,0 +1,33 @@
+#
+# Silan device configuration
+#
+
+config NET_VENDOR_SILAN
+ bool "Silan devices"
+ default y
+ depends on PCI && EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Silan devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_SILAN
+
+config SC92031
+ tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ ---help---
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you
+ have one of these, say Y here.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sc92031. This is recommended.
+
+endif # NET_VENDOR_SILAN
diff --git a/drivers/net/ethernet/silan/Makefile b/drivers/net/ethernet/silan/Makefile
new file mode 100644
index 000000000000..4ad3523dcb92
--- /dev/null
+++ b/drivers/net/ethernet/silan/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Silan network device drivers.
+#
+
+obj-$(CONFIG_SC92031) += sc92031.o
diff --git a/drivers/net/ethernet/realtek/sc92031.c b/drivers/net/ethernet/silan/sc92031.c
index a284d6440538..a284d6440538 100644
--- a/drivers/net/ethernet/realtek/sc92031.c
+++ b/drivers/net/ethernet/silan/sc92031.c
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 9100c100d295..2cc119295821 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -49,7 +49,7 @@ struct stmmac_extra_stats {
unsigned long tx_underflow ____cacheline_aligned;
unsigned long tx_carrier;
unsigned long tx_losscarrier;
- unsigned long tx_heartbeat;
+ unsigned long vlan_tag;
unsigned long tx_deferred;
unsigned long tx_vlan;
unsigned long tx_jabber;
@@ -58,9 +58,9 @@ struct stmmac_extra_stats {
unsigned long tx_ip_header_error;
/* Receive errors */
unsigned long rx_desc;
- unsigned long rx_partial;
- unsigned long rx_runt;
- unsigned long rx_toolong;
+ unsigned long sa_filter_fail;
+ unsigned long overflow_error;
+ unsigned long ipc_csum_error;
unsigned long rx_collision;
unsigned long rx_crc;
unsigned long rx_length;
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index 63a03e264694..9820ec842cc0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -25,33 +25,34 @@ struct dma_desc {
union {
struct {
/* RDES0 */
- u32 reserved1:1;
+ u32 payload_csum_error:1;
u32 crc_error:1;
u32 dribbling:1;
u32 mii_error:1;
u32 receive_watchdog:1;
u32 frame_type:1;
u32 collision:1;
- u32 frame_too_long:1;
+ u32 ipc_csum_error:1;
u32 last_descriptor:1;
u32 first_descriptor:1;
- u32 multicast_frame:1;
- u32 run_frame:1;
+ u32 vlan_tag:1;
+ u32 overflow_error:1;
u32 length_error:1;
- u32 partial_frame_error:1;
+ u32 sa_filter_fail:1;
u32 descriptor_error:1;
u32 error_summary:1;
u32 frame_length:14;
- u32 filtering_fail:1;
+ u32 da_filter_fail:1;
u32 own:1;
/* RDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
- u32 reserved2:2;
+ u32 reserved1:2;
u32 second_address_chained:1;
u32 end_ring:1;
- u32 reserved3:5;
+ u32 reserved2:5;
u32 disable_ic:1;
+
} rx;
struct {
/* RDES0 */
@@ -91,24 +92,28 @@ struct dma_desc {
u32 underflow_error:1;
u32 excessive_deferral:1;
u32 collision_count:4;
- u32 heartbeat_fail:1;
+ u32 vlan_frame:1;
u32 excessive_collisions:1;
u32 late_collision:1;
u32 no_carrier:1;
u32 loss_carrier:1;
- u32 reserved1:3;
+ u32 payload_error:1;
+ u32 frame_flushed:1;
+ u32 jabber_timeout:1;
u32 error_summary:1;
- u32 reserved2:15;
+ u32 ip_header_error:1;
+ u32 time_stamp_status:1;
+ u32 reserved1:13;
u32 own:1;
/* TDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
- u32 reserved3:1;
+ u32 time_stamp_enable:1;
u32 disable_padding:1;
u32 second_address_chained:1;
u32 end_ring:1;
u32 crc_disable:1;
- u32 reserved4:2;
+ u32 checksum_insertion:2;
u32 first_segment:1;
u32 last_segment:1;
u32 interrupt:1;
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index f7e8ba7f501a..fda5d2b31d3a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -50,11 +50,12 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
stats->collisions += p->des01.tx.collision_count;
ret = -1;
}
- if (unlikely(p->des01.tx.heartbeat_fail)) {
- x->tx_heartbeat++;
- stats->tx_heartbeat_errors++;
- ret = -1;
+
+ if (p->des01.etx.vlan_frame) {
+ CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
+ x->tx_vlan++;
}
+
if (unlikely(p->des01.tx.deferred))
x->tx_deferred++;
@@ -68,12 +69,12 @@ static int ndesc_get_tx_len(struct dma_desc *p)
/* This function verifies if each incoming frame has some errors
* and, if required, updates the multicast statistics.
- * In case of success, it returns csum_none because the device
- * is not able to compute the csum in HW. */
+ * In case of success, it returns good_frame because the GMAC device
+ * is supposed to be able to compute the csum in HW. */
static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
- int ret = csum_none;
+ int ret = good_frame;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.rx.last_descriptor == 0)) {
@@ -86,12 +87,12 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
if (unlikely(p->des01.rx.error_summary)) {
if (unlikely(p->des01.rx.descriptor_error))
x->rx_desc++;
- if (unlikely(p->des01.rx.partial_frame_error))
- x->rx_partial++;
- if (unlikely(p->des01.rx.run_frame))
- x->rx_runt++;
- if (unlikely(p->des01.rx.frame_too_long))
- x->rx_toolong++;
+ if (unlikely(p->des01.rx.sa_filter_fail))
+ x->sa_filter_fail++;
+ if (unlikely(p->des01.rx.overflow_error))
+ x->overflow_error++;
+ if (unlikely(p->des01.rx.ipc_csum_error))
+ x->ipc_csum_error++;
if (unlikely(p->des01.rx.collision)) {
x->rx_collision++;
stats->collisions++;
@@ -113,10 +114,10 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
x->rx_mii++;
ret = discard_frame;
}
- if (p->des01.rx.multicast_frame) {
- x->rx_multicast++;
- stats->multicast++;
- }
+#ifdef STMMAC_VLAN_TAG_USED
+ if (p->des01.rx.vlan_tag)
+ x->vlan_tag++;
+#endif
return ret;
}
@@ -184,6 +185,9 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
{
p->des01.tx.first_segment = is_fs;
norm_set_tx_desc_len(p, len);
+
+ if (likely(csum_flag))
+ p->des01.tx.checksum_insertion = cic_full;
}
static void ndesc_clear_tx_ic(struct dma_desc *p)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 406404f6e321..e8eff09bbbd7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -50,7 +50,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(tx_underflow),
STMMAC_STAT(tx_carrier),
STMMAC_STAT(tx_losscarrier),
- STMMAC_STAT(tx_heartbeat),
+ STMMAC_STAT(vlan_tag),
STMMAC_STAT(tx_deferred),
STMMAC_STAT(tx_vlan),
STMMAC_STAT(rx_vlan),
@@ -59,9 +59,9 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(tx_payload_error),
STMMAC_STAT(tx_ip_header_error),
STMMAC_STAT(rx_desc),
- STMMAC_STAT(rx_partial),
- STMMAC_STAT(rx_runt),
- STMMAC_STAT(rx_toolong),
+ STMMAC_STAT(sa_filter_fail),
+ STMMAC_STAT(overflow_error),
+ STMMAC_STAT(ipc_csum_error),
STMMAC_STAT(rx_collision),
STMMAC_STAT(rx_crc),
STMMAC_STAT(rx_length),
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index aeaa15b451de..20546bbbb8db 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -325,7 +325,7 @@ static int stmmac_init_phy(struct net_device *dev)
(interface == PHY_INTERFACE_MODE_RMII))) {
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause);
- priv->phydev->advertising = priv->phydev->supported;
+ phydev->advertising = phydev->supported;
}
/*
@@ -812,9 +812,11 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
*/
static int stmmac_get_hw_features(struct stmmac_priv *priv)
{
- u32 hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
+ u32 hw_cap = 0;
+
+ if (priv->hw->dma->get_hw_feature) {
+ hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
- if (likely(hw_cap)) {
priv->dma_cap.mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL);
priv->dma_cap.mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1;
priv->dma_cap.half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2;
@@ -937,6 +939,7 @@ static int stmmac_open(struct net_device *dev)
stmmac_get_hw_features(priv);
+ priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr);
if (priv->rx_coe)
pr_info("stmmac: Rx Checksum Offload Engine supported\n");
if (priv->plat->tx_coe)
@@ -1274,8 +1277,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
#endif
skb->protocol = eth_type_trans(skb, priv->dev);
- if (unlikely(status == csum_none)) {
- /* always for the old mac 10/100 */
+ if (unlikely(!priv->rx_coe)) {
+ /* No RX COE for old mac10/100 devices */
skb_checksum_none_assert(skb);
netif_receive_skb(skb);
} else {
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index 4d1658e78dee..caf3659e173c 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -716,8 +716,8 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
cur_p->phys = dma_map_single(ndev->dev.parent,
skb_frag_address(frag),
- frag_size(frag), DMA_TO_DEVICE);
- cur_p->len = frag_size(frag);
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ cur_p->len = skb_frag_size(frag);
cur_p->app0 = 0;
frag++;
}
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index a40fab44b9ae..d423d18b4ad6 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -314,7 +314,7 @@ config TOSHIBA_FIR
config AU1000_FIR
tristate "Alchemy Au1000 SIR/FIR"
- depends on SOC_AU1000 && IRDA
+ depends on IRDA && MIPS_ALCHEMY
config SMC_IRCC_FIR
tristate "SMSC IrCC (EXPERIMENTAL)"
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 3bb131137033..7145714a5ec9 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -88,8 +88,8 @@ static struct rio_dev **rionet_active;
#define dev_rionet_capable(dev) \
is_rionet_capable(dev->src_ops, dev->dst_ops)
-#define RIONET_MAC_MATCH(x) (*(u32 *)x == 0x00010001)
-#define RIONET_GET_DESTID(x) (*(u16 *)(x + 4))
+#define RIONET_MAC_MATCH(x) (!memcmp((x), "\00\01\00\01", 4))
+#define RIONET_GET_DESTID(x) ((*((u8 *)x + 4) << 8) | *((u8 *)x + 5))
static int rionet_rx_clean(struct net_device *ndev)
{
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 91039ab16728..6ee8410443c4 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -925,12 +925,10 @@ static void virtnet_update_status(struct virtnet_info *vi)
{
u16 v;
- if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS))
- return;
-
- vi->vdev->config->get(vi->vdev,
+ if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS,
offsetof(struct virtio_net_config, status),
- &v, sizeof(v));
+ &v) < 0)
+ return;
/* Ignore unknown (future) status bits */
v &= VIRTIO_NET_S_LINK_UP;
@@ -1006,11 +1004,9 @@ static int virtnet_probe(struct virtio_device *vdev)
}
/* Configuration may specify what MAC to use. Otherwise random. */
- if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
- vdev->config->get(vdev,
+ if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC,
offsetof(struct virtio_net_config, mac),
- dev->dev_addr, dev->addr_len);
- } else
+ dev->dev_addr, dev->addr_len) < 0)
random_ether_addr(dev->dev_addr);
/* Set up our device-specific information */
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 39322d4121b7..4045e5ab0555 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -517,7 +517,7 @@ static char *hex2str(void *buf, size_t len)
goto exit;
while (len--) {
- obuf = pack_hex_byte(obuf, *ibuf++);
+ obuf = hex_byte_pack(obuf, *ibuf++);
*obuf++ = '-';
}
obuf--;
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index 073548836413..09602241901b 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -1,6 +1,6 @@
menuconfig ATH_COMMON
tristate "Atheros Wireless Cards"
- depends on CFG80211
+ depends on CFG80211 && (!UML || BROKEN)
---help---
This will enable the support for the Atheros wireless drivers.
ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 908fdbc3e0ee..0f9ee46cfc97 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -173,8 +173,7 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry);
void ath_hw_cycle_counters_update(struct ath_common *common);
int32_t ath_hw_get_listen_time(struct ath_common *common);
-extern __attribute__((format (printf, 2, 3)))
-void ath_printk(const char *level, const char *fmt, ...);
+extern __printf(2, 3) void ath_printk(const char *level, const char *fmt, ...);
#define _ath_printk(level, common, fmt, ...) \
do { \
@@ -258,7 +257,7 @@ do { \
#else
-static inline __attribute__((format (printf, 3, 4)))
+static inline __attribute__ ((format (printf, 3, 4)))
void ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
const char *fmt, ...)
{
diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h
index 7f37df3125fd..0a3f916a1ef3 100644
--- a/drivers/net/wireless/ath/ath5k/debug.h
+++ b/drivers/net/wireless/ath/ath5k/debug.h
@@ -141,10 +141,10 @@ ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf);
#include <linux/compiler.h>
-static inline void __attribute__ ((format (printf, 3, 4)))
+static inline __printf(3, 4) void
ATH5K_DBG(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {}
-static inline void __attribute__ ((format (printf, 3, 4)))
+static inline __printf(3, 4) void
ATH5K_DBG_UNLIMIT(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...)
{}
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index 9288a3ce1e39..7b7675f70a10 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -44,8 +44,8 @@ enum ATH6K_DEBUG_MASK {
};
extern unsigned int debug_mask;
-extern int ath6kl_printk(const char *level, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
+extern __printf(2, 3)
+int ath6kl_printk(const char *level, const char *fmt, ...);
#define ath6kl_info(fmt, ...) \
ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index 447a2307c9d9..37110dfd2c96 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -1011,14 +1011,10 @@ static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
}
/* Message printing */
-void b43info(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43err(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43warn(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void b43dbg(struct b43_wl *wl, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
+__printf(2, 3) void b43info(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43err(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43warn(struct b43_wl *wl, const char *fmt, ...);
+__printf(2, 3) void b43dbg(struct b43_wl *wl, const char *fmt, ...);
/* A WARN_ON variant that vanishes when b43 debugging is disabled.
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 12b518251581..1d4fc9db7f5e 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -810,15 +810,15 @@ struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy,
/* Message printing */
-void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
+__printf(2, 3)
+void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...);
+__printf(2, 3)
+void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...);
+__printf(2, 3)
+void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...);
#if B43legacy_DEBUG
-void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
+__printf(2, 3)
+void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...);
#else /* DEBUG */
# define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0)
#endif /* DEBUG */
diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig
index 45e14760c16e..d6c42e69bdbd 100644
--- a/drivers/net/wireless/rtlwifi/Kconfig
+++ b/drivers/net/wireless/rtlwifi/Kconfig
@@ -12,7 +12,7 @@ config RTL8192CE
config RTL8192SE
tristate "Realtek RTL8192SE/RTL8191SE PCIe Wireless Network Adapter"
- depends on MAC80211 && EXPERIMENTAL
+ depends on MAC80211 && EXPERIMENTAL && PCI
select FW_LOADER
select RTLWIFI
---help---
@@ -23,7 +23,7 @@ config RTL8192SE
config RTL8192DE
tristate "Realtek RTL8192DE/RTL8188DE PCIe Wireless Network Adapter"
- depends on MAC80211 && EXPERIMENTAL
+ depends on MAC80211 && EXPERIMENTAL && PCI
select FW_LOADER
select RTLWIFI
---help---
diff --git a/drivers/of/base.c b/drivers/of/base.c
index b970562e0111..9b6588ef0673 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -716,6 +716,90 @@ int of_property_read_string(struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_read_string);
/**
+ * of_property_read_string_index - Find and read a string from a multiple
+ * strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the string in the list of strings
+ * @out_string: pointer to null terminated return string, modified only if
+ * return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string_index(struct device_node *np, const char *propname,
+ int index, const char **output)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ int i = 0;
+ size_t l = 0, total = 0;
+ const char *p;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if (strnlen(prop->value, prop->length) >= prop->length)
+ return -EILSEQ;
+
+ p = prop->value;
+
+ for (i = 0; total < prop->length; total += l, p += l) {
+ l = strlen(p) + 1;
+ if ((*p != 0) && (i++ == index)) {
+ *output = p;
+ return 0;
+ }
+ }
+ return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_index);
+
+
+/**
+ * of_property_count_strings - Find and return the number of strings from a
+ * multiple strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+int of_property_count_strings(struct device_node *np, const char *propname)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ int i = 0;
+ size_t l = 0, total = 0;
+ const char *p;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if (strnlen(prop->value, prop->length) >= prop->length)
+ return -EILSEQ;
+
+ p = prop->value;
+
+ for (i = 0; total < prop->length; total += l, p += l) {
+ l = strlen(p) + 1;
+ if (*p != 0)
+ i++;
+ }
+ return i;
+}
+EXPORT_SYMBOL_GPL(of_property_count_strings);
+
+/**
* of_parse_phandle - Resolve a phandle property to a device_node pointer
* @np: Pointer to device node holding phandle property
* @phandle_name: Name of property holding a phandle value
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 6a5b5e777dd2..6d3dd3988d0f 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -19,10 +19,12 @@
*/
#include <linux/errno.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/string.h>
+#include <linux/slab.h>
/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */
#ifndef NO_IRQ
@@ -386,3 +388,108 @@ int of_irq_to_resource_table(struct device_node *dev, struct resource *res,
return i;
}
+
+struct intc_desc {
+ struct list_head list;
+ struct device_node *dev;
+ struct device_node *interrupt_parent;
+};
+
+/**
+ * of_irq_init - Scan and init matching interrupt controllers in DT
+ * @matches: 0 terminated array of nodes to match and init function to call
+ *
+ * This function scans the device tree for matching interrupt controller nodes,
+ * and calls their initialization functions in order with parents first.
+ */
+void __init of_irq_init(const struct of_device_id *matches)
+{
+ struct device_node *np, *parent = NULL;
+ struct intc_desc *desc, *temp_desc;
+ struct list_head intc_desc_list, intc_parent_list;
+
+ INIT_LIST_HEAD(&intc_desc_list);
+ INIT_LIST_HEAD(&intc_parent_list);
+
+ for_each_matching_node(np, matches) {
+ if (!of_find_property(np, "interrupt-controller", NULL))
+ continue;
+ /*
+ * Here, we allocate and populate an intc_desc with the node
+ * pointer, interrupt-parent device_node etc.
+ */
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (WARN_ON(!desc))
+ goto err;
+
+ desc->dev = np;
+ desc->interrupt_parent = of_irq_find_parent(np);
+ list_add_tail(&desc->list, &intc_desc_list);
+ }
+
+ /*
+ * The root irq controller is the one without an interrupt-parent.
+ * That one goes first, followed by the controllers that reference it,
+ * followed by the ones that reference the 2nd level controllers, etc.
+ */
+ while (!list_empty(&intc_desc_list)) {
+ /*
+ * Process all controllers with the current 'parent'.
+ * First pass will be looking for NULL as the parent.
+ * The assumption is that NULL parent means a root controller.
+ */
+ list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
+ const struct of_device_id *match;
+ int ret;
+ of_irq_init_cb_t irq_init_cb;
+
+ if (desc->interrupt_parent != parent)
+ continue;
+
+ list_del(&desc->list);
+ match = of_match_node(matches, desc->dev);
+ if (WARN(!match->data,
+ "of_irq_init: no init function for %s\n",
+ match->compatible)) {
+ kfree(desc);
+ continue;
+ }
+
+ pr_debug("of_irq_init: init %s @ %p, parent %p\n",
+ match->compatible,
+ desc->dev, desc->interrupt_parent);
+ irq_init_cb = match->data;
+ ret = irq_init_cb(desc->dev, desc->interrupt_parent);
+ if (ret) {
+ kfree(desc);
+ continue;
+ }
+
+ /*
+ * This one is now set up; add it to the parent list so
+ * its children can get processed in a subsequent pass.
+ */
+ list_add_tail(&desc->list, &intc_parent_list);
+ }
+
+ /* Get the next pending parent that might have children */
+ desc = list_first_entry(&intc_parent_list, typeof(*desc), list);
+ if (list_empty(&intc_parent_list) || !desc) {
+ pr_err("of_irq_init: children remain, but no parents\n");
+ break;
+ }
+ list_del(&desc->list);
+ parent = desc->dev;
+ kfree(desc);
+ }
+
+ list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
+ list_del(&desc->list);
+ kfree(desc);
+ }
+err:
+ list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
+ list_del(&desc->list);
+ kfree(desc);
+ }
+}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index ed5a6d3c26aa..cbd5d701c7e0 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -310,18 +310,21 @@ static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *l
struct device_node *np)
{
struct resource res;
- if (lookup) {
- for(; lookup->name != NULL; lookup++) {
- if (!of_device_is_compatible(np, lookup->compatible))
- continue;
- if (of_address_to_resource(np, 0, &res))
- continue;
- if (res.start != lookup->phys_addr)
- continue;
- pr_debug("%s: devname=%s\n", np->full_name, lookup->name);
- return lookup;
- }
+
+ if (!lookup)
+ return NULL;
+
+ for(; lookup->name != NULL; lookup++) {
+ if (!of_device_is_compatible(np, lookup->compatible))
+ continue;
+ if (of_address_to_resource(np, 0, &res))
+ continue;
+ if (res.start != lookup->phys_addr)
+ continue;
+ pr_debug("%s: devname=%s\n", np->full_name, lookup->name);
+ return lookup;
}
+
return NULL;
}
@@ -329,8 +332,9 @@ static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *l
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
- * disallow recursive creation of child buses
+ * @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent for new device, or NULL for top level.
+ * @strict: require compatible property
*
* Creates a platform_device for the provided device_node, and optionally
* recursively create devices for all the child nodes.
diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c
index f56d7de7c751..22a75e610f12 100644
--- a/drivers/pcmcia/pxa2xx_balloon3.c
+++ b/drivers/pcmcia/pxa2xx_balloon3.c
@@ -97,7 +97,7 @@ static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
- __raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG |
+ __raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG +
((state->flags & SS_RESET) ?
BALLOON3_FPGA_SETnCLR : 0));
return 0;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 10cf2500522b..f4e3d82379d7 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -61,15 +61,18 @@ config ASUS_LAPTOP
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_SPARSEKMAP
+ select INPUT_POLLDEV
---help---
- This is the new Linux driver for Asus laptops. It may also support some
- MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
- standard ACPI events and input events. It also adds
- support for video output switching, LCD backlight control, Bluetooth and
- Wlan control, and most importantly, allows you to blink those fancy LEDs.
+ This is a driver for Asus laptops, Lenovo SL and the Pegatron
+ Lucid tablet. It may also support some MEDION, JVC or VICTOR
+ laptops. It makes all the extra buttons generate standard
+ ACPI events and input events, and on the Lucid the built-in
+ accelerometer appears as an input device. It also adds
+ support for video output switching, LCD backlight control,
+ Bluetooth and Wlan control, and most importantly, allows you
+ to blink those fancy LEDs.
- For more information and a userspace daemon for handling the extra
- buttons see <http://acpi4asus.sf.net>.
+ For more information see <http://acpi4asus.sf.net>.
If you have an ACPI-compatible ASUS laptop, say Y or M here.
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index af2bb20cb2fb..b848277171a4 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -190,6 +190,7 @@ enum interface_flags {
ACER_AMW0,
ACER_AMW0_V2,
ACER_WMID,
+ ACER_WMID_v2,
};
#define ACER_DEFAULT_WIRELESS 0
@@ -205,6 +206,7 @@ static int threeg = -1;
static int force_series;
static bool ec_raw_mode;
static bool has_type_aa;
+static u16 commun_func_bitmap;
module_param(mailled, int, 0444);
module_param(brightness, int, 0444);
@@ -464,6 +466,15 @@ static struct dmi_system_id acer_quirks[] = {
},
.driver_data = &quirk_lenovo_ideapad_s205,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Lenovo 3000 N200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"),
+ },
+ .driver_data = &quirk_fujitsu_amilo_li_1718,
+ },
{}
};
@@ -868,6 +879,174 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
return WMI_execute_u32(method_id, (u32)value, NULL);
}
+static acpi_status wmid3_get_device_status(u32 *value, u16 device)
+{
+ struct wmid3_gds_return_value return_value;
+ acpi_status status;
+ union acpi_object *obj;
+ struct wmid3_gds_input_param params = {
+ .function_num = 0x1,
+ .hotkey_number = 0x01,
+ .devices = device,
+ };
+ struct acpi_buffer input = {
+ sizeof(struct wmid3_gds_input_param),
+ &params
+ };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = output.pointer;
+
+ if (!obj)
+ return AE_ERROR;
+ else if (obj->type != ACPI_TYPE_BUFFER) {
+ kfree(obj);
+ return AE_ERROR;
+ }
+ if (obj->buffer.length != 8) {
+ pr_warn("Unknown buffer length %d\n", obj->buffer.length);
+ kfree(obj);
+ return AE_ERROR;
+ }
+
+ return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+ kfree(obj);
+
+ if (return_value.error_code || return_value.ec_return_value)
+ pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n",
+ device,
+ return_value.error_code,
+ return_value.ec_return_value);
+ else
+ *value = !!(return_value.devices & device);
+
+ return status;
+}
+
+static acpi_status wmid_v2_get_u32(u32 *value, u32 cap)
+{
+ u16 device;
+
+ switch (cap) {
+ case ACER_CAP_WIRELESS:
+ device = ACER_WMID3_GDS_WIRELESS;
+ break;
+ case ACER_CAP_BLUETOOTH:
+ device = ACER_WMID3_GDS_BLUETOOTH;
+ break;
+ case ACER_CAP_THREEG:
+ device = ACER_WMID3_GDS_THREEG;
+ break;
+ default:
+ return AE_ERROR;
+ }
+ return wmid3_get_device_status(value, device);
+}
+
+static acpi_status wmid3_set_device_status(u32 value, u16 device)
+{
+ struct wmid3_gds_return_value return_value;
+ acpi_status status;
+ union acpi_object *obj;
+ u16 devices;
+ struct wmid3_gds_input_param params = {
+ .function_num = 0x1,
+ .hotkey_number = 0x01,
+ .devices = commun_func_bitmap,
+ };
+ struct acpi_buffer input = {
+ sizeof(struct wmid3_gds_input_param),
+ &params
+ };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = output.pointer;
+
+ if (!obj)
+ return AE_ERROR;
+ else if (obj->type != ACPI_TYPE_BUFFER) {
+ kfree(obj);
+ return AE_ERROR;
+ }
+ if (obj->buffer.length != 8) {
+ pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ kfree(obj);
+ return AE_ERROR;
+ }
+
+ return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+ kfree(obj);
+
+ if (return_value.error_code || return_value.ec_return_value) {
+ pr_warning("Get Current Device Status failed: "
+ "0x%x - 0x%x\n", return_value.error_code,
+ return_value.ec_return_value);
+ return status;
+ }
+
+ devices = return_value.devices;
+ params.function_num = 0x2;
+ params.hotkey_number = 0x01;
+ params.devices = (value) ? (devices | device) : (devices & ~device);
+
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = output2.pointer;
+
+ if (!obj)
+ return AE_ERROR;
+ else if (obj->type != ACPI_TYPE_BUFFER) {
+ kfree(obj);
+ return AE_ERROR;
+ }
+ if (obj->buffer.length != 4) {
+ pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ kfree(obj);
+ return AE_ERROR;
+ }
+
+ return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+ kfree(obj);
+
+ if (return_value.error_code || return_value.ec_return_value)
+ pr_warning("Set Device Status failed: "
+ "0x%x - 0x%x\n", return_value.error_code,
+ return_value.ec_return_value);
+
+ return status;
+}
+
+static acpi_status wmid_v2_set_u32(u32 value, u32 cap)
+{
+ u16 device;
+
+ switch (cap) {
+ case ACER_CAP_WIRELESS:
+ device = ACER_WMID3_GDS_WIRELESS;
+ break;
+ case ACER_CAP_BLUETOOTH:
+ device = ACER_WMID3_GDS_BLUETOOTH;
+ break;
+ case ACER_CAP_THREEG:
+ device = ACER_WMID3_GDS_THREEG;
+ break;
+ default:
+ return AE_ERROR;
+ }
+ return wmid3_set_device_status(value, device);
+}
+
static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
{
struct hotkey_function_type_aa *type_aa;
@@ -881,6 +1060,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
pr_info("Function bitmap for Communication Button: 0x%x\n",
type_aa->commun_func_bitmap);
+ commun_func_bitmap = type_aa->commun_func_bitmap;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
interface->capability |= ACER_CAP_WIRELESS;
@@ -913,17 +1093,13 @@ static acpi_status WMID_set_capabilities(void)
return AE_ERROR;
}
- dmi_walk(type_aa_dmi_decode, NULL);
- if (!has_type_aa) {
+ pr_info("Function bitmap for Communication Device: 0x%x\n", devices);
+ if (devices & 0x07)
interface->capability |= ACER_CAP_WIRELESS;
- if (devices & 0x40)
- interface->capability |= ACER_CAP_THREEG;
- if (devices & 0x10)
- interface->capability |= ACER_CAP_BLUETOOTH;
- }
-
- /* WMID always provides brightness methods */
- interface->capability |= ACER_CAP_BRIGHTNESS;
+ if (devices & 0x40)
+ interface->capability |= ACER_CAP_THREEG;
+ if (devices & 0x10)
+ interface->capability |= ACER_CAP_BLUETOOTH;
if (!(devices & 0x20))
max_brightness = 0x9;
@@ -936,6 +1112,10 @@ static struct wmi_interface wmid_interface = {
.type = ACER_WMID,
};
+static struct wmi_interface wmid_v2_interface = {
+ .type = ACER_WMID_v2,
+};
+
/*
* Generic Device (interface-independent)
*/
@@ -956,6 +1136,14 @@ static acpi_status get_u32(u32 *value, u32 cap)
case ACER_WMID:
status = WMID_get_u32(value, cap, interface);
break;
+ case ACER_WMID_v2:
+ if (cap & (ACER_CAP_WIRELESS |
+ ACER_CAP_BLUETOOTH |
+ ACER_CAP_THREEG))
+ status = wmid_v2_get_u32(value, cap);
+ else if (wmi_has_guid(WMID_GUID2))
+ status = WMID_get_u32(value, cap, interface);
+ break;
}
return status;
@@ -989,6 +1177,13 @@ static acpi_status set_u32(u32 value, u32 cap)
}
case ACER_WMID:
return WMID_set_u32(value, cap, interface);
+ case ACER_WMID_v2:
+ if (cap & (ACER_CAP_WIRELESS |
+ ACER_CAP_BLUETOOTH |
+ ACER_CAP_THREEG))
+ return wmid_v2_set_u32(value, cap);
+ else if (wmi_has_guid(WMID_GUID2))
+ return WMID_set_u32(value, cap, interface);
default:
return AE_BAD_PARAMETER;
}
@@ -1095,186 +1290,6 @@ static void acer_backlight_exit(void)
backlight_device_unregister(acer_backlight_device);
}
-static acpi_status wmid3_get_device_status(u32 *value, u16 device)
-{
- struct wmid3_gds_return_value return_value;
- acpi_status status;
- union acpi_object *obj;
- struct wmid3_gds_input_param params = {
- .function_num = 0x1,
- .hotkey_number = 0x01,
- .devices = device,
- };
- struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
- &params
- };
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
- if (ACPI_FAILURE(status))
- return status;
-
- obj = output.pointer;
-
- if (!obj)
- return AE_ERROR;
- else if (obj->type != ACPI_TYPE_BUFFER) {
- kfree(obj);
- return AE_ERROR;
- }
- if (obj->buffer.length != 8) {
- pr_warn("Unknown buffer length %d\n", obj->buffer.length);
- kfree(obj);
- return AE_ERROR;
- }
-
- return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
- kfree(obj);
-
- if (return_value.error_code || return_value.ec_return_value)
- pr_warn("Get Device Status failed: 0x%x - 0x%x\n",
- return_value.error_code,
- return_value.ec_return_value);
- else
- *value = !!(return_value.devices & device);
-
- return status;
-}
-
-static acpi_status get_device_status(u32 *value, u32 cap)
-{
- if (wmi_has_guid(WMID_GUID3)) {
- u16 device;
-
- switch (cap) {
- case ACER_CAP_WIRELESS:
- device = ACER_WMID3_GDS_WIRELESS;
- break;
- case ACER_CAP_BLUETOOTH:
- device = ACER_WMID3_GDS_BLUETOOTH;
- break;
- case ACER_CAP_THREEG:
- device = ACER_WMID3_GDS_THREEG;
- break;
- default:
- return AE_ERROR;
- }
- return wmid3_get_device_status(value, device);
-
- } else {
- return get_u32(value, cap);
- }
-}
-
-static acpi_status wmid3_set_device_status(u32 value, u16 device)
-{
- struct wmid3_gds_return_value return_value;
- acpi_status status;
- union acpi_object *obj;
- u16 devices;
- struct wmid3_gds_input_param params = {
- .function_num = 0x1,
- .hotkey_number = 0x01,
- .devices = ACER_WMID3_GDS_WIRELESS |
- ACER_WMID3_GDS_THREEG |
- ACER_WMID3_GDS_WIMAX |
- ACER_WMID3_GDS_BLUETOOTH,
- };
- struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
- &params
- };
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
-
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
- if (ACPI_FAILURE(status))
- return status;
-
- obj = output.pointer;
-
- if (!obj)
- return AE_ERROR;
- else if (obj->type != ACPI_TYPE_BUFFER) {
- kfree(obj);
- return AE_ERROR;
- }
- if (obj->buffer.length != 8) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
- kfree(obj);
- return AE_ERROR;
- }
-
- return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
- kfree(obj);
-
- if (return_value.error_code || return_value.ec_return_value) {
- pr_warning("Get Current Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
- return_value.ec_return_value);
- return status;
- }
-
- devices = return_value.devices;
- params.function_num = 0x2;
- params.hotkey_number = 0x01;
- params.devices = (value) ? (devices | device) : (devices & ~device);
-
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
- if (ACPI_FAILURE(status))
- return status;
-
- obj = output2.pointer;
-
- if (!obj)
- return AE_ERROR;
- else if (obj->type != ACPI_TYPE_BUFFER) {
- kfree(obj);
- return AE_ERROR;
- }
- if (obj->buffer.length != 4) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
- kfree(obj);
- return AE_ERROR;
- }
-
- return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
- kfree(obj);
-
- if (return_value.error_code || return_value.ec_return_value)
- pr_warning("Set Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
- return_value.ec_return_value);
-
- return status;
-}
-
-static acpi_status set_device_status(u32 value, u32 cap)
-{
- if (wmi_has_guid(WMID_GUID3)) {
- u16 device;
-
- switch (cap) {
- case ACER_CAP_WIRELESS:
- device = ACER_WMID3_GDS_WIRELESS;
- break;
- case ACER_CAP_BLUETOOTH:
- device = ACER_WMID3_GDS_BLUETOOTH;
- break;
- case ACER_CAP_THREEG:
- device = ACER_WMID3_GDS_THREEG;
- break;
- default:
- return AE_ERROR;
- }
- return wmid3_set_device_status(value, device);
-
- } else {
- return set_u32(value, cap);
- }
-}
-
/*
* Rfkill devices
*/
@@ -1285,12 +1300,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
u32 state;
acpi_status status;
- status = get_u32(&state, ACER_CAP_WIRELESS);
- if (ACPI_SUCCESS(status)) {
- if (quirks->wireless == 3) {
- rfkill_set_hw_state(wireless_rfkill, !state);
- } else {
- rfkill_set_sw_state(wireless_rfkill, !state);
+ if (has_cap(ACER_CAP_WIRELESS)) {
+ status = get_u32(&state, ACER_CAP_WIRELESS);
+ if (ACPI_SUCCESS(status)) {
+ if (quirks->wireless == 3)
+ rfkill_set_hw_state(wireless_rfkill, !state);
+ else
+ rfkill_set_sw_state(wireless_rfkill, !state);
}
}
@@ -1301,8 +1317,7 @@ static void acer_rfkill_update(struct work_struct *ignored)
}
if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
- status = wmid3_get_device_status(&state,
- ACER_WMID3_GDS_THREEG);
+ status = get_u32(&state, ACER_WMID3_GDS_THREEG);
if (ACPI_SUCCESS(status))
rfkill_set_sw_state(threeg_rfkill, !state);
}
@@ -1316,7 +1331,7 @@ static int acer_rfkill_set(void *data, bool blocked)
u32 cap = (unsigned long)data;
if (rfkill_inited) {
- status = set_device_status(!blocked, cap);
+ status = set_u32(!blocked, cap);
if (ACPI_FAILURE(status))
return -ENODEV;
}
@@ -1343,7 +1358,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
if (!rfkill_dev)
return ERR_PTR(-ENOMEM);
- status = get_device_status(&state, cap);
+ status = get_u32(&state, cap);
err = rfkill_register(rfkill_dev);
if (err) {
@@ -1359,19 +1374,24 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
static int acer_rfkill_init(struct device *dev)
{
- wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
- "acer-wireless", ACER_CAP_WIRELESS);
- if (IS_ERR(wireless_rfkill))
- return PTR_ERR(wireless_rfkill);
+ int err;
+
+ if (has_cap(ACER_CAP_WIRELESS)) {
+ wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
+ "acer-wireless", ACER_CAP_WIRELESS);
+ if (IS_ERR(wireless_rfkill)) {
+ err = PTR_ERR(wireless_rfkill);
+ goto error_wireless;
+ }
+ }
if (has_cap(ACER_CAP_BLUETOOTH)) {
bluetooth_rfkill = acer_rfkill_register(dev,
RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
ACER_CAP_BLUETOOTH);
if (IS_ERR(bluetooth_rfkill)) {
- rfkill_unregister(wireless_rfkill);
- rfkill_destroy(wireless_rfkill);
- return PTR_ERR(bluetooth_rfkill);
+ err = PTR_ERR(bluetooth_rfkill);
+ goto error_bluetooth;
}
}
@@ -1380,30 +1400,44 @@ static int acer_rfkill_init(struct device *dev)
RFKILL_TYPE_WWAN, "acer-threeg",
ACER_CAP_THREEG);
if (IS_ERR(threeg_rfkill)) {
- rfkill_unregister(wireless_rfkill);
- rfkill_destroy(wireless_rfkill);
- rfkill_unregister(bluetooth_rfkill);
- rfkill_destroy(bluetooth_rfkill);
- return PTR_ERR(threeg_rfkill);
+ err = PTR_ERR(threeg_rfkill);
+ goto error_threeg;
}
}
rfkill_inited = true;
- if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+ if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
+ has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
schedule_delayed_work(&acer_rfkill_work,
round_jiffies_relative(HZ));
return 0;
+
+error_threeg:
+ if (has_cap(ACER_CAP_BLUETOOTH)) {
+ rfkill_unregister(bluetooth_rfkill);
+ rfkill_destroy(bluetooth_rfkill);
+ }
+error_bluetooth:
+ if (has_cap(ACER_CAP_WIRELESS)) {
+ rfkill_unregister(wireless_rfkill);
+ rfkill_destroy(wireless_rfkill);
+ }
+error_wireless:
+ return err;
}
static void acer_rfkill_exit(void)
{
- if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+ if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
+ has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
cancel_delayed_work_sync(&acer_rfkill_work);
- rfkill_unregister(wireless_rfkill);
- rfkill_destroy(wireless_rfkill);
+ if (has_cap(ACER_CAP_WIRELESS)) {
+ rfkill_unregister(wireless_rfkill);
+ rfkill_destroy(wireless_rfkill);
+ }
if (has_cap(ACER_CAP_BLUETOOTH)) {
rfkill_unregister(bluetooth_rfkill);
@@ -1428,11 +1462,7 @@ static ssize_t show_bool_threeg(struct device *dev,
pr_info("This threeg sysfs will be removed in 2012"
" - used by: %s\n", current->comm);
- if (wmi_has_guid(WMID_GUID3))
- status = wmid3_get_device_status(&result,
- ACER_WMID3_GDS_THREEG);
- else
- status = get_u32(&result, ACER_CAP_THREEG);
+ status = get_u32(&result, ACER_CAP_THREEG);
if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result);
return sprintf(buf, "Read error\n");
@@ -1464,6 +1494,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "AMW0 v2\n");
case ACER_WMID:
return sprintf(buf, "WMID\n");
+ case ACER_WMID_v2:
+ return sprintf(buf, "WMID v2\n");
default:
return sprintf(buf, "Error!\n");
}
@@ -1883,12 +1915,20 @@ static int __init acer_wmi_init(void)
if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
interface = &wmid_interface;
+ if (wmi_has_guid(WMID_GUID3))
+ interface = &wmid_v2_interface;
+
+ if (interface)
+ dmi_walk(type_aa_dmi_decode, NULL);
+
if (wmi_has_guid(WMID_GUID2) && interface) {
- if (ACPI_FAILURE(WMID_set_capabilities())) {
+ if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) {
pr_err("Unable to detect available WMID devices\n");
return -ENODEV;
}
- } else if (!wmi_has_guid(WMID_GUID2) && interface) {
+ /* WMID always provides brightness methods */
+ interface->capability |= ACER_CAP_BRIGHTNESS;
+ } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) {
pr_err("No WMID device detection method found\n");
return -ENODEV;
}
@@ -1912,7 +1952,7 @@ static int __init acer_wmi_init(void)
set_quirks();
- if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
+ if (acpi_video_backlight_support()) {
interface->capability &= ~ACER_CAP_BRIGHTNESS;
pr_info("Brightness must be controlled by "
"generic video driver\n");
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index fa6d7ec68b26..edaccad9b5bf 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
* Copyright (C) 2006-2007 Corentin Chary
+ * Copyright (C) 2011 Wind River Systems
*
* 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
@@ -48,6 +49,7 @@
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/input-polldev.h>
#include <linux/rfkill.h>
#include <linux/slab.h>
#include <linux/dmi.h>
@@ -83,26 +85,32 @@ static int wlan_status = 1;
static int bluetooth_status = 1;
static int wimax_status = -1;
static int wwan_status = -1;
+static int als_status;
module_param(wlan_status, int, 0444);
MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). "
- "default is 1");
+ "default is -1");
module_param(bluetooth_status, int, 0444);
MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). "
- "default is 1");
+ "default is -1");
module_param(wimax_status, int, 0444);
MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). "
- "default is 1");
+ "default is -1");
module_param(wwan_status, int, 0444);
MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). "
- "default is 1");
+ "default is -1");
+
+module_param(als_status, int, 0444);
+MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
+ "(0 = disabled, 1 = enabled). "
+ "default is 0");
/*
* Some events we use, same for all Asus
@@ -173,6 +181,29 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
#define METHOD_KBD_LIGHT_SET "SLKB"
#define METHOD_KBD_LIGHT_GET "GLKB"
+/* For Pegatron Lucid tablet */
+#define DEVICE_NAME_PEGA "Lucid"
+
+#define METHOD_PEGA_ENABLE "ENPR"
+#define METHOD_PEGA_DISABLE "DAPR"
+#define PEGA_WLAN 0x00
+#define PEGA_BLUETOOTH 0x01
+#define PEGA_WWAN 0x02
+#define PEGA_ALS 0x04
+#define PEGA_ALS_POWER 0x05
+
+#define METHOD_PEGA_READ "RDLN"
+#define PEGA_READ_ALS_H 0x02
+#define PEGA_READ_ALS_L 0x03
+
+#define PEGA_ACCEL_NAME "pega_accel"
+#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer"
+#define METHOD_XLRX "XLRX"
+#define METHOD_XLRY "XLRY"
+#define METHOD_XLRZ "XLRZ"
+#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */
+#define PEGA_ACC_RETRIES 3
+
/*
* Define a specific led structure to keep the main structure clean
*/
@@ -185,6 +216,15 @@ struct asus_led {
};
/*
+ * Same thing for rfkill
+ */
+struct asus_pega_rfkill {
+ int control_id; /* type of control. Maps to PEGA_* values */
+ struct rfkill *rfkill;
+ struct asus_laptop *asus;
+};
+
+/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
*/
@@ -198,6 +238,7 @@ struct asus_laptop {
struct input_dev *inputdev;
struct key_entry *keymap;
+ struct input_polled_dev *pega_accel_poll;
struct asus_led mled;
struct asus_led tled;
@@ -209,9 +250,18 @@ struct asus_laptop {
int wireless_status;
bool have_rsts;
+ bool is_pega_lucid;
+ bool pega_acc_live;
+ int pega_acc_x;
+ int pega_acc_y;
+ int pega_acc_z;
struct rfkill *gps_rfkill;
+ struct asus_pega_rfkill wlanrfk;
+ struct asus_pega_rfkill btrfk;
+ struct asus_pega_rfkill wwanrfk;
+
acpi_handle handle; /* the handle of the hotk device */
u32 ledd_status; /* status of the LED display */
u8 light_level; /* light sensor level */
@@ -323,6 +373,127 @@ static int acpi_check_handle(acpi_handle handle, const char *method,
return 0;
}
+static bool asus_check_pega_lucid(struct asus_laptop *asus)
+{
+ return !strcmp(asus->name, DEVICE_NAME_PEGA) &&
+ !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL);
+}
+
+static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
+{
+ char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE;
+ return write_acpi_int(asus->handle, method, unit);
+}
+
+static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
+{
+ int i, delta;
+ unsigned long long val;
+ for (i = 0; i < PEGA_ACC_RETRIES; i++) {
+ acpi_evaluate_integer(asus->handle, method, NULL, &val);
+
+ /* The output is noisy. From reading the ASL
+ * dissassembly, timeout errors are returned with 1's
+ * in the high word, and the lack of locking around
+ * thei hi/lo byte reads means that a transition
+ * between (for example) -1 and 0 could be read as
+ * 0xff00 or 0x00ff. */
+ delta = abs(curr - (short)val);
+ if (delta < 128 && !(val & ~0xffff))
+ break;
+ }
+ return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
+}
+
+static void pega_accel_poll(struct input_polled_dev *ipd)
+{
+ struct device *parent = ipd->input->dev.parent;
+ struct asus_laptop *asus = dev_get_drvdata(parent);
+
+ /* In some cases, the very first call to poll causes a
+ * recursive fault under the polldev worker. This is
+ * apparently related to very early userspace access to the
+ * device, and perhaps a firmware bug. Fake the first report. */
+ if (!asus->pega_acc_live) {
+ asus->pega_acc_live = true;
+ input_report_abs(ipd->input, ABS_X, 0);
+ input_report_abs(ipd->input, ABS_Y, 0);
+ input_report_abs(ipd->input, ABS_Z, 0);
+ input_sync(ipd->input);
+ return;
+ }
+
+ asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX);
+ asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY);
+ asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ);
+
+ /* Note transform, convert to "right/up/out" in the native
+ * landscape orientation (i.e. the vector is the direction of
+ * "real up" in the device's cartiesian coordinates). */
+ input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x);
+ input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y);
+ input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z);
+ input_sync(ipd->input);
+}
+
+static void pega_accel_exit(struct asus_laptop *asus)
+{
+ if (asus->pega_accel_poll) {
+ input_unregister_polled_device(asus->pega_accel_poll);
+ input_free_polled_device(asus->pega_accel_poll);
+ }
+ asus->pega_accel_poll = NULL;
+}
+
+static int pega_accel_init(struct asus_laptop *asus)
+{
+ int err;
+ struct input_polled_dev *ipd;
+
+ if (!asus->is_pega_lucid)
+ return -ENODEV;
+
+ if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) ||
+ acpi_check_handle(asus->handle, METHOD_XLRY, NULL) ||
+ acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
+ return -ENODEV;
+
+ ipd = input_allocate_polled_device();
+ if (!ipd)
+ return -ENOMEM;
+
+ ipd->poll = pega_accel_poll;
+ ipd->poll_interval = 125;
+ ipd->poll_interval_min = 50;
+ ipd->poll_interval_max = 2000;
+
+ ipd->input->name = PEGA_ACCEL_DESC;
+ ipd->input->phys = PEGA_ACCEL_NAME "/input0";
+ ipd->input->dev.parent = &asus->platform_device->dev;
+ ipd->input->id.bustype = BUS_HOST;
+
+ set_bit(EV_ABS, ipd->input->evbit);
+ input_set_abs_params(ipd->input, ABS_X,
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
+ input_set_abs_params(ipd->input, ABS_Y,
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
+ input_set_abs_params(ipd->input, ABS_Z,
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
+
+ err = input_register_polled_device(ipd);
+ if (err)
+ goto exit;
+
+ asus->pega_accel_poll = ipd;
+ return 0;
+
+exit:
+ input_free_polled_device(ipd);
+ return err;
+}
+
/* Generic LED function */
static int asus_led_set(struct asus_laptop *asus, const char *method,
int value)
@@ -430,17 +601,17 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
static void asus_led_exit(struct asus_laptop *asus)
{
- if (asus->mled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->mled.led.dev))
led_classdev_unregister(&asus->mled.led);
- if (asus->tled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->tled.led.dev))
led_classdev_unregister(&asus->tled.led);
- if (asus->pled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->pled.led.dev))
led_classdev_unregister(&asus->pled.led);
- if (asus->rled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->rled.led.dev))
led_classdev_unregister(&asus->rled.led);
- if (asus->gled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->gled.led.dev))
led_classdev_unregister(&asus->gled.led);
- if (asus->kled.led.dev)
+ if (!IS_ERR_OR_NULL(asus->kled.led.dev))
led_classdev_unregister(&asus->kled.led);
if (asus->led_workqueue) {
destroy_workqueue(asus->led_workqueue);
@@ -474,6 +645,13 @@ static int asus_led_init(struct asus_laptop *asus)
int r;
/*
+ * The Pegatron Lucid has no physical leds, but all methods are
+ * available in the DSDT...
+ */
+ if (asus->is_pega_lucid)
+ return 0;
+
+ /*
* Functions that actually update the LED's are called from a
* workqueue. By doing this as separate work rather than when the LED
* subsystem asks, we avoid messing with the Asus ACPI stuff during a
@@ -907,8 +1085,18 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
*/
static void asus_als_switch(struct asus_laptop *asus, int value)
{
- if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value))
- pr_warn("Error setting light sensor switch\n");
+ int ret;
+
+ if (asus->is_pega_lucid) {
+ ret = asus_pega_lucid_set(asus, PEGA_ALS, value);
+ if (!ret)
+ ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value);
+ } else {
+ ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value);
+ }
+ if (ret)
+ pr_warning("Error setting light sensor switch\n");
+
asus->light_switch = value;
}
@@ -964,6 +1152,35 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
return rv;
}
+static int pega_int_read(struct asus_laptop *asus, int arg, int *result)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg,
+ &buffer);
+ if (!err) {
+ union acpi_object *obj = buffer.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ *result = obj->integer.value;
+ else
+ err = -EIO;
+ }
+ return err;
+}
+
+static ssize_t show_lsvalue(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_laptop *asus = dev_get_drvdata(dev);
+ int err, hi, lo;
+
+ err = pega_int_read(asus, PEGA_READ_ALS_H, &hi);
+ if (!err)
+ err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
+ if (!err)
+ return sprintf(buf, "%d\n", 10 * hi + lo);
+ return err;
+}
+
/*
* GPS
*/
@@ -1062,6 +1279,86 @@ static int asus_rfkill_init(struct asus_laptop *asus)
return result;
}
+static int pega_rfkill_set(void *data, bool blocked)
+{
+ struct asus_pega_rfkill *pega_rfk = data;
+
+ int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
+ pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
+
+ return ret;
+}
+
+static const struct rfkill_ops pega_rfkill_ops = {
+ .set_block = pega_rfkill_set,
+};
+
+static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
+{
+ pr_warn("Terminating %d\n", pega_rfk->control_id);
+ if (pega_rfk->rfkill) {
+ rfkill_unregister(pega_rfk->rfkill);
+ rfkill_destroy(pega_rfk->rfkill);
+ pega_rfk->rfkill = NULL;
+ }
+}
+
+static void pega_rfkill_exit(struct asus_laptop *asus)
+{
+ pega_rfkill_terminate(&asus->wwanrfk);
+ pega_rfkill_terminate(&asus->btrfk);
+ pega_rfkill_terminate(&asus->wlanrfk);
+}
+
+static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
+ const char *name, int controlid, int rfkill_type)
+{
+ int result;
+
+ pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
+ pega_rfk->control_id = controlid;
+ pega_rfk->asus = asus;
+ pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
+ rfkill_type, &pega_rfkill_ops, pega_rfk);
+ if (!pega_rfk->rfkill)
+ return -EINVAL;
+
+ result = rfkill_register(pega_rfk->rfkill);
+ if (result) {
+ rfkill_destroy(pega_rfk->rfkill);
+ pega_rfk->rfkill = NULL;
+ }
+
+ return result;
+}
+
+static int pega_rfkill_init(struct asus_laptop *asus)
+{
+ int ret = 0;
+
+ if(!asus->is_pega_lucid)
+ return -ENODEV;
+
+ ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
+ if(ret)
+ return ret;
+ ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
+ if(ret)
+ goto err_btrfk;
+ ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
+ if(ret)
+ goto err_wwanrfk;
+
+ pr_warn("Pega rfkill init succeeded\n");
+ return 0;
+err_wwanrfk:
+ pega_rfkill_terminate(&asus->btrfk);
+err_btrfk:
+ pega_rfkill_terminate(&asus->wlanrfk);
+
+ return ret;
+}
+
/*
* Input device (i.e. hotkeys)
*/
@@ -1141,6 +1438,14 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
}
return ;
}
+
+ /* Accelerometer "coarse orientation change" event */
+ if (asus->pega_accel_poll && event == 0xEA) {
+ kobject_uevent(&asus->pega_accel_poll->input->dev.kobj,
+ KOBJ_CHANGE);
+ return ;
+ }
+
asus_input_notify(asus, event);
}
@@ -1152,6 +1457,7 @@ static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax);
static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp);
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
+static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL);
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
@@ -1164,6 +1470,7 @@ static struct attribute *asus_attributes[] = {
&dev_attr_wwan.attr,
&dev_attr_display.attr,
&dev_attr_ledd.attr,
+ &dev_attr_ls_value.attr,
&dev_attr_ls_level.attr,
&dev_attr_ls_switch.attr,
&dev_attr_gps.attr,
@@ -1180,6 +1487,19 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,
acpi_handle handle = asus->handle;
bool supported;
+ if (asus->is_pega_lucid) {
+ /* no ls_level interface on the Lucid */
+ if (attr == &dev_attr_ls_switch.attr)
+ supported = true;
+ else if (attr == &dev_attr_ls_level.attr)
+ supported = false;
+ else
+ goto normal;
+
+ return supported;
+ }
+
+normal:
if (attr == &dev_attr_wlan.attr) {
supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
@@ -1202,8 +1522,9 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,
} else if (attr == &dev_attr_ls_switch.attr ||
attr == &dev_attr_ls_level.attr) {
supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
- !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
-
+ !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
+ } else if (attr == &dev_attr_ls_value.attr) {
+ supported = asus->is_pega_lucid;
} else if (attr == &dev_attr_gps.attr) {
supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
!acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
@@ -1258,7 +1579,7 @@ static struct platform_driver platform_driver = {
.driver = {
.name = ASUS_LAPTOP_FILE,
.owner = THIS_MODULE,
- }
+ },
};
/*
@@ -1388,11 +1709,13 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
asus->ledd_status = 0xFFF;
/* Set initial values of light sensor and level */
- asus->light_switch = 0; /* Default to light sensor disabled */
+ asus->light_switch = !!als_status;
asus->light_level = 5; /* level 5 for sensor sensitivity */
- if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
- !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
+ if (asus->is_pega_lucid) {
+ asus_als_switch(asus, asus->light_switch);
+ } else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
asus_als_switch(asus, asus->light_switch);
asus_als_level(asus, asus->light_level);
}
@@ -1439,9 +1762,10 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
goto fail_platform;
/*
- * Register the platform device first. It is used as a parent for the
- * sub-devices below.
+ * Need platform type detection first, then the platform
+ * device. It is used as a parent for the sub-devices below.
*/
+ asus->is_pega_lucid = asus_check_pega_lucid(asus);
result = asus_platform_init(asus);
if (result)
goto fail_platform;
@@ -1465,9 +1789,21 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
if (result)
goto fail_rfkill;
+ result = pega_accel_init(asus);
+ if (result && result != -ENODEV)
+ goto fail_pega_accel;
+
+ result = pega_rfkill_init(asus);
+ if (result && result != -ENODEV)
+ goto fail_pega_rfkill;
+
asus_device_present = true;
return 0;
+fail_pega_rfkill:
+ pega_accel_exit(asus);
+fail_pega_accel:
+ asus_rfkill_exit(asus);
fail_rfkill:
asus_led_exit(asus);
fail_led:
@@ -1491,6 +1827,8 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
asus_rfkill_exit(asus);
asus_led_exit(asus);
asus_input_exit(asus);
+ pega_accel_exit(asus);
+ pega_rfkill_exit(asus);
asus_platform_exit(asus);
kfree(asus->name);
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 95cba9ebf6c0..d1049ee3c9e8 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -453,7 +453,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
- if (asus->tpd_led.dev)
+ if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
+ led_classdev_unregister(&asus->kbd_led);
+ if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
led_classdev_unregister(&asus->tpd_led);
if (asus->led_workqueue)
destroy_workqueue(asus->led_workqueue);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index f31fa4efa725..a43cfd906c6d 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -60,6 +60,22 @@ struct calling_interface_structure {
struct calling_interface_token tokens[];
} __packed;
+struct quirk_entry {
+ u8 touchpad_led;
+};
+
+static struct quirk_entry *quirks;
+
+static struct quirk_entry quirk_dell_vostro_v130 = {
+ .touchpad_led = 1,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
static int da_command_address;
static int da_command_code;
static int da_num_tokens;
@@ -149,6 +165,27 @@ static struct dmi_system_id __devinitdata dell_blacklist[] = {
{}
};
+static struct dmi_system_id __devinitdata dell_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Vostro V130",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Vostro V131",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+};
+
static struct calling_interface_buffer *buffer;
static struct page *bufferpage;
static DEFINE_MUTEX(buffer_mutex);
@@ -552,6 +589,44 @@ static const struct backlight_ops dell_ops = {
.update_status = dell_send_intensity,
};
+static void touchpad_led_on()
+{
+ int command = 0x97;
+ char data = 1;
+ i8042_command(&data, command | 1 << 12);
+}
+
+static void touchpad_led_off()
+{
+ int command = 0x97;
+ char data = 2;
+ i8042_command(&data, command | 1 << 12);
+}
+
+static void touchpad_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value > 0)
+ touchpad_led_on();
+ else
+ touchpad_led_off();
+}
+
+static struct led_classdev touchpad_led = {
+ .name = "dell-laptop::touchpad",
+ .brightness_set = touchpad_led_set,
+};
+
+static int __devinit touchpad_led_init(struct device *dev)
+{
+ return led_classdev_register(dev, &touchpad_led);
+}
+
+static void touchpad_led_exit(void)
+{
+ led_classdev_unregister(&touchpad_led);
+}
+
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -584,6 +659,10 @@ static int __init dell_init(void)
if (!dmi_check_system(dell_device_table))
return -ENODEV;
+ quirks = NULL;
+ /* find if this machine support other functions */
+ dmi_check_system(dell_quirks);
+
dmi_walk(find_tokens, NULL);
if (!da_tokens) {
@@ -626,6 +705,9 @@ static int __init dell_init(void)
goto fail_filter;
}
+ if (quirks && quirks->touchpad_led)
+ touchpad_led_init(&platform_device->dev);
+
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
@@ -692,6 +774,8 @@ fail_platform_driver:
static void __exit dell_exit(void)
{
debugfs_remove_recursive(dell_laptop_dir);
+ if (quirks && quirks->touchpad_led)
+ touchpad_led_exit();
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 1c45d92e2163..ea44abd8df48 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -568,7 +568,7 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
static void eeepc_led_exit(struct eeepc_laptop *eeepc)
{
- if (eeepc->tpd_led.dev)
+ if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
led_classdev_unregister(&eeepc->tpd_led);
if (eeepc->led_workqueue)
destroy_workqueue(eeepc->led_workqueue);
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index 1b52d00e2f90..22b2dfa73148 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -76,6 +76,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
/* For automatic insertion of the module */
static struct acpi_device_id lis3lv02d_device_ids[] = {
{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
+ {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
@@ -209,6 +210,8 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted),
AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right),
AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap),
+ AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted),
+ AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted),
/* Intel-based HP Pavilion dv5 */
AXIS_DMI_MATCH2("HPDV5_I",
PRODUCT_NAME, "HP Pavilion dv5",
@@ -227,7 +230,12 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
+ AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted),
AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
+ AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap),
+ AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
+ AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
+ AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
{ NULL, }
/* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910"
@@ -320,7 +328,7 @@ static int lis3lv02d_add(struct acpi_device *device)
INIT_WORK(&hpled_led.work, delayed_set_status_worker);
ret = led_classdev_register(NULL, &hpled_led.led_classdev);
if (ret) {
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(&lis3_dev);
lis3lv02d_poweroff(&lis3_dev);
flush_work(&hpled_led.work);
return ret;
@@ -334,7 +342,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type)
if (!device)
return -EINVAL;
- lis3lv02d_joystick_disable();
+ lis3lv02d_joystick_disable(&lis3_dev);
lis3lv02d_poweroff(&lis3_dev);
led_classdev_unregister(&hpled_led.led_classdev);
@@ -354,8 +362,7 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
static int lis3lv02d_resume(struct acpi_device *device)
{
- lis3lv02d_poweron(&lis3_dev);
- return 0;
+ return lis3lv02d_poweron(&lis3_dev);
}
#else
#define lis3lv02d_suspend NULL
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 0c595410e788..a36addf106a0 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -34,6 +34,8 @@
#include <linux/input/sparse-keymap.h>
#include <linux/backlight.h>
#include <linux/fb.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -42,15 +44,41 @@
#define CFG_WIFI_BIT (18)
#define CFG_CAMERA_BIT (19)
+enum {
+ VPCCMD_R_VPC1 = 0x10,
+ VPCCMD_R_BL_MAX,
+ VPCCMD_R_BL,
+ VPCCMD_W_BL,
+ VPCCMD_R_WIFI,
+ VPCCMD_W_WIFI,
+ VPCCMD_R_BT,
+ VPCCMD_W_BT,
+ VPCCMD_R_BL_POWER,
+ VPCCMD_R_NOVO,
+ VPCCMD_R_VPC2,
+ VPCCMD_R_TOUCHPAD,
+ VPCCMD_W_TOUCHPAD,
+ VPCCMD_R_CAMERA,
+ VPCCMD_W_CAMERA,
+ VPCCMD_R_3G,
+ VPCCMD_W_3G,
+ VPCCMD_R_ODD, /* 0x21 */
+ VPCCMD_R_RF = 0x23,
+ VPCCMD_W_RF,
+ VPCCMD_W_BL_POWER = 0x33,
+};
+
struct ideapad_private {
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
struct input_dev *inputdev;
struct backlight_device *blightdev;
+ struct dentry *debug;
unsigned long cfg;
};
static acpi_handle ideapad_handle;
+static struct ideapad_private *ideapad_priv;
static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
@@ -164,6 +192,146 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
}
/*
+ * debugfs
+ */
+#define DEBUGFS_EVENT_LEN (4096)
+static int debugfs_status_show(struct seq_file *s, void *data)
+{
+ unsigned long value;
+
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value))
+ seq_printf(s, "Backlight max:\t%lu\n", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value))
+ seq_printf(s, "Backlight now:\t%lu\n", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value))
+ seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
+ seq_printf(s, "=====================\n");
+
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value))
+ seq_printf(s, "Radio status:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value))
+ seq_printf(s, "Wifi status:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value))
+ seq_printf(s, "BT status:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value))
+ seq_printf(s, "3G status:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+ seq_printf(s, "=====================\n");
+
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value))
+ seq_printf(s, "Touchpad status:%s(%lu)\n",
+ value ? "On" : "Off", value);
+ if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value))
+ seq_printf(s, "Camera status:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+
+ return 0;
+}
+
+static int debugfs_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_status_show, NULL);
+}
+
+static const struct file_operations debugfs_status_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int debugfs_cfg_show(struct seq_file *s, void *data)
+{
+ if (!ideapad_priv) {
+ seq_printf(s, "cfg: N/A\n");
+ } else {
+ seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
+ ideapad_priv->cfg);
+ if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg))
+ seq_printf(s, "Bluetooth ");
+ if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg))
+ seq_printf(s, "3G ");
+ if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg))
+ seq_printf(s, "Wireless ");
+ if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg))
+ seq_printf(s, "Camera ");
+ seq_printf(s, "\nGraphic: ");
+ switch ((ideapad_priv->cfg)&0x700) {
+ case 0x100:
+ seq_printf(s, "Intel");
+ break;
+ case 0x200:
+ seq_printf(s, "ATI");
+ break;
+ case 0x300:
+ seq_printf(s, "Nvidia");
+ break;
+ case 0x400:
+ seq_printf(s, "Intel and ATI");
+ break;
+ case 0x500:
+ seq_printf(s, "Intel and Nvidia");
+ break;
+ }
+ seq_printf(s, "\n");
+ }
+ return 0;
+}
+
+static int debugfs_cfg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_cfg_show, NULL);
+}
+
+static const struct file_operations debugfs_cfg_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_cfg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
+{
+ struct dentry *node;
+
+ priv->debug = debugfs_create_dir("ideapad", NULL);
+ if (priv->debug == NULL) {
+ pr_err("failed to create debugfs directory");
+ goto errout;
+ }
+
+ node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL,
+ &debugfs_cfg_fops);
+ if (!node) {
+ pr_err("failed to create cfg in debugfs");
+ goto errout;
+ }
+
+ node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL,
+ &debugfs_status_fops);
+ if (!node) {
+ pr_err("failed to create event in debugfs");
+ goto errout;
+ }
+
+ return 0;
+
+errout:
+ return -ENOMEM;
+}
+
+static void ideapad_debugfs_exit(struct ideapad_private *priv)
+{
+ debugfs_remove_recursive(priv->debug);
+ priv->debug = NULL;
+}
+
+/*
* sysfs
*/
static ssize_t show_ideapad_cam(struct device *dev,
@@ -172,7 +340,7 @@ static ssize_t show_ideapad_cam(struct device *dev,
{
unsigned long result;
- if (read_ec_data(ideapad_handle, 0x1D, &result))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
}
@@ -187,7 +355,7 @@ static ssize_t store_ideapad_cam(struct device *dev,
return 0;
if (sscanf(buf, "%i", &state) != 1)
return -EINVAL;
- ret = write_ec_cmd(ideapad_handle, 0x1E, state);
+ ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
if (ret < 0)
return ret;
return count;
@@ -195,20 +363,8 @@ static ssize_t store_ideapad_cam(struct device *dev,
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
-static ssize_t show_ideapad_cfg(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct ideapad_private *priv = dev_get_drvdata(dev);
-
- return sprintf(buf, "0x%.8lX\n", priv->cfg);
-}
-
-static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
-
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
- &dev_attr_cfg.attr,
NULL
};
@@ -244,9 +400,9 @@ struct ideapad_rfk_data {
};
const struct ideapad_rfk_data ideapad_rfk_data[] = {
- { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
- { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH },
- { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN },
+ { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
+ { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
+ { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
};
static int ideapad_rfk_set(void *data, bool blocked)
@@ -260,13 +416,12 @@ static struct rfkill_ops ideapad_rfk_ops = {
.set_block = ideapad_rfk_set,
};
-static void ideapad_sync_rfk_state(struct acpi_device *adevice)
+static void ideapad_sync_rfk_state(struct ideapad_private *priv)
{
- struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
unsigned long hw_blocked;
int i;
- if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked))
return;
hw_blocked = !hw_blocked;
@@ -363,8 +518,10 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
* input device
*/
static const struct key_entry ideapad_keymap[] = {
- { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
- { KE_KEY, 0x0D, { KEY_WLAN } },
+ { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 13, { KEY_WLAN } },
+ { KE_KEY, 16, { KEY_PROG1 } },
+ { KE_KEY, 17, { KEY_PROG2 } },
{ KE_END, 0 },
};
@@ -419,6 +576,18 @@ static void ideapad_input_report(struct ideapad_private *priv,
sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
}
+static void ideapad_input_novokey(struct ideapad_private *priv)
+{
+ unsigned long long_pressed;
+
+ if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed))
+ return;
+ if (long_pressed)
+ ideapad_input_report(priv, 17);
+ else
+ ideapad_input_report(priv, 16);
+}
+
/*
* backlight
*/
@@ -426,16 +595,17 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
{
unsigned long now;
- if (read_ec_data(ideapad_handle, 0x12, &now))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
return -EIO;
return now;
}
static int ideapad_backlight_update_status(struct backlight_device *blightdev)
{
- if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
+ if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL,
+ blightdev->props.brightness))
return -EIO;
- if (write_ec_cmd(ideapad_handle, 0x33,
+ if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER,
blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
return -EIO;
@@ -453,11 +623,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
struct backlight_properties props;
unsigned long max, now, power;
- if (read_ec_data(ideapad_handle, 0x11, &max))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max))
return -EIO;
- if (read_ec_data(ideapad_handle, 0x12, &now))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
return -EIO;
- if (read_ec_data(ideapad_handle, 0x18, &power))
+ if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
return -EIO;
memset(&props, 0, sizeof(struct backlight_properties));
@@ -493,7 +663,9 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
unsigned long power;
struct backlight_device *blightdev = priv->blightdev;
- if (read_ec_data(ideapad_handle, 0x18, &power))
+ if (!blightdev)
+ return;
+ if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
return;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
}
@@ -504,7 +676,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
/* if we control brightness via acpi video driver */
if (priv->blightdev == NULL) {
- read_ec_data(ideapad_handle, 0x12, &now);
+ read_ec_data(ideapad_handle, VPCCMD_R_BL, &now);
return;
}
@@ -533,6 +705,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
if (!priv)
return -ENOMEM;
dev_set_drvdata(&adevice->dev, priv);
+ ideapad_priv = priv;
ideapad_handle = adevice->handle;
priv->cfg = cfg;
@@ -540,6 +713,10 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
if (ret)
goto platform_failed;
+ ret = ideapad_debugfs_init(priv);
+ if (ret)
+ goto debugfs_failed;
+
ret = ideapad_input_init(priv);
if (ret)
goto input_failed;
@@ -550,7 +727,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
else
priv->rfk[i] = NULL;
}
- ideapad_sync_rfk_state(adevice);
+ ideapad_sync_rfk_state(priv);
if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv);
@@ -565,6 +742,8 @@ backlight_failed:
ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv);
input_failed:
+ ideapad_debugfs_exit(priv);
+debugfs_failed:
ideapad_platform_exit(priv);
platform_failed:
kfree(priv);
@@ -580,6 +759,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv);
+ ideapad_debugfs_exit(priv);
ideapad_platform_exit(priv);
dev_set_drvdata(&adevice->dev, NULL);
kfree(priv);
@@ -593,9 +773,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
acpi_handle handle = adevice->handle;
unsigned long vpc1, vpc2, vpc_bit;
- if (read_ec_data(handle, 0x10, &vpc1))
+ if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
return;
- if (read_ec_data(handle, 0x1A, &vpc2))
+ if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
return;
vpc1 = (vpc2 << 8) | vpc1;
@@ -603,11 +783,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
if (test_bit(vpc_bit, &vpc1)) {
switch (vpc_bit) {
case 9:
- ideapad_sync_rfk_state(adevice);
+ ideapad_sync_rfk_state(priv);
break;
case 4:
ideapad_backlight_notify_brightness(priv);
break;
+ case 3:
+ ideapad_input_novokey(priv);
+ break;
case 2:
ideapad_backlight_notify_power(priv);
break;
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
index b93a03259c16..2d0f9136ea9a 100644
--- a/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/drivers/platform/x86/intel_scu_ipcutil.c
@@ -24,7 +24,7 @@
#include <linux/init.h>
#include <asm/intel_scu_ipc.h>
-static u32 major;
+static int major;
#define MAX_FW_SIZE 264192
@@ -117,7 +117,11 @@ static const struct file_operations scu_ipc_fops = {
static int __init ipc_module_init(void)
{
- return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);
+ major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);
+ if (major < 0)
+ return major;
+
+ return 0;
}
static void __exit ipc_module_exit(void)
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index 359163011044..09e26bfd4643 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -226,6 +226,7 @@ static struct backlight_device *backlight_device;
static struct mutex sabi_mutex;
static struct platform_device *sdev;
static struct rfkill *rfk;
+static bool has_stepping_quirk;
static int force;
module_param(force, bool, 0);
@@ -370,15 +371,28 @@ static u8 read_brightness(void)
&sretval);
if (!retval) {
user_brightness = sretval.retval[0];
- if (user_brightness != 0)
+ if (user_brightness > sabi_config->min_brightness)
user_brightness -= sabi_config->min_brightness;
+ else
+ user_brightness = 0;
}
return user_brightness;
}
static void set_brightness(u8 user_brightness)
{
- u8 user_level = user_brightness - sabi_config->min_brightness;
+ u8 user_level = user_brightness + sabi_config->min_brightness;
+
+ if (has_stepping_quirk && user_level != 0) {
+ /*
+ * short circuit if the specified level is what's already set
+ * to prevent the screen from flickering needlessly
+ */
+ if (user_brightness == read_brightness())
+ return;
+
+ sabi_set_command(sabi_config->commands.set_brightness, 0);
+ }
sabi_set_command(sabi_config->commands.set_brightness, user_level);
}
@@ -388,6 +402,40 @@ static int get_brightness(struct backlight_device *bd)
return (int)read_brightness();
}
+static void check_for_stepping_quirk(void)
+{
+ u8 initial_level;
+ u8 check_level;
+ u8 orig_level = read_brightness();
+
+ /*
+ * Some laptops exhibit the strange behaviour of stepping toward
+ * (rather than setting) the brightness except when changing to/from
+ * brightness level 0. This behaviour is checked for here and worked
+ * around in set_brightness.
+ */
+
+ if (orig_level == 0)
+ set_brightness(1);
+
+ initial_level = read_brightness();
+
+ if (initial_level <= 2)
+ check_level = initial_level + 2;
+ else
+ check_level = initial_level - 2;
+
+ has_stepping_quirk = false;
+ set_brightness(check_level);
+
+ if (read_brightness() != check_level) {
+ has_stepping_quirk = true;
+ pr_info("enabled workaround for brightness stepping quirk\n");
+ }
+
+ set_brightness(orig_level);
+}
+
static int update_status(struct backlight_device *bd)
{
set_brightness(bd->props.brightness);
@@ -621,6 +669,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
.callback = dmi_check_cb,
},
{
+ .ident = "N220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N220"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
.ident = "N150/N210/N220/N230",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
@@ -641,6 +699,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
.callback = dmi_check_cb,
},
{
+ .ident = "R700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
+ DMI_MATCH(DMI_BOARD_NAME, "SR700"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
.ident = "R530/R730",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
@@ -686,6 +753,33 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
},
.callback = dmi_check_cb,
},
+ {
+ .ident = "R528/R728",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
+ DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
+ .ident = "NC210/NC110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
+ DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
+ .ident = "X520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
+ DMI_MATCH(DMI_BOARD_NAME, "X520"),
+ },
+ .callback = dmi_check_cb,
+ },
{ },
};
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -770,7 +864,7 @@ static int __init samsung_init(void)
sabi_iface = ioremap_nocache(ifaceP, 16);
if (!sabi_iface) {
pr_err("Can't remap %x\n", ifaceP);
- goto exit;
+ goto error_no_signature;
}
if (debug) {
printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
@@ -794,6 +888,9 @@ static int __init samsung_init(void)
}
}
+ /* Check for stepping quirk */
+ check_for_stepping_quirk();
+
/* knock up a platform device to hang stuff off of */
sdev = platform_device_register_simple("samsung", -1, NULL, 0);
if (IS_ERR(sdev))
@@ -802,7 +899,8 @@ static int __init samsung_init(void)
/* create a backlight device to talk to this one */
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = sabi_config->max_brightness;
+ props.max_brightness = sabi_config->max_brightness -
+ sabi_config->min_brightness;
backlight_device = backlight_device_register("samsung", &sdev->dev,
NULL, &backlight_ops,
&props);
@@ -821,7 +919,6 @@ static int __init samsung_init(void)
if (retval)
goto error_file_create;
-exit:
return 0;
error_file_create:
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index bbd182e178cb..c006dee5ebfe 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -3281,7 +3281,7 @@ static int sony_pic_add(struct acpi_device *device)
/* request IRQ */
list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
- IRQF_DISABLED, "sony-laptop", &spic_dev)) {
+ 0, "sony-laptop", &spic_dev)) {
dprintk("IRQ: %d - triggering: %d - "
"polarity: %d - shr: %d\n",
irq->irq.interrupts[0],
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index 4c20447ddbb7..d528daa0e81c 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -41,6 +41,7 @@ static const struct key_entry topstar_keymap[] = {
{ KE_KEY, 0x8c, { KEY_MEDIA } },
/* Known non hotkey events don't handled or that we don't care yet */
+ { KE_IGNORE, 0x82, }, /* backlight event */
{ KE_IGNORE, 0x8e, },
{ KE_IGNORE, 0x8f, },
{ KE_IGNORE, 0x90, },
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index cb009b2629ee..13ef8c37471d 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -47,7 +47,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/backlight.h>
-#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
@@ -63,11 +62,7 @@ MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
/* Toshiba ACPI method paths */
-#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
-#define TOSH_INTERFACE_1 "\\_SB_.VALD"
-#define TOSH_INTERFACE_2 "\\_SB_.VALZ"
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
-#define GHCI_METHOD ".GHCI"
/* Toshiba HCI interface definitions
*
@@ -111,6 +106,25 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS_BT_ATTACH 0x40
#define HCI_WIRELESS_BT_POWER 0x80
+struct toshiba_acpi_dev {
+ struct acpi_device *acpi_dev;
+ const char *method_hci;
+ struct rfkill *bt_rfk;
+ struct input_dev *hotkey_dev;
+ struct backlight_device *backlight_dev;
+ struct led_classdev led_dev;
+
+ int force_fan;
+ int last_key_event;
+ int key_event_valid;
+
+ int illumination_supported:1;
+ int video_supported:1;
+ int fan_supported:1;
+
+ struct mutex mutex;
+};
+
static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6208", 0},
@@ -119,7 +133,7 @@ static const struct acpi_device_id toshiba_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
-static const struct key_entry toshiba_acpi_keymap[] __initconst = {
+static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0x101, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
@@ -155,15 +169,6 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value)
/* acpi interface wrappers
*/
-static int is_valid_acpi_path(const char *methodName)
-{
- acpi_handle handle;
- acpi_status status;
-
- status = acpi_get_handle(NULL, (char *)methodName, &handle);
- return !ACPI_FAILURE(status);
-}
-
static int write_acpi_int(const char *methodName, int val)
{
struct acpi_object_list params;
@@ -176,32 +181,14 @@ static int write_acpi_int(const char *methodName, int val)
in_objs[0].integer.value = val;
status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
- return (status == AE_OK);
-}
-
-#if 0
-static int read_acpi_int(const char *methodName, int *pVal)
-{
- struct acpi_buffer results;
- union acpi_object out_objs[1];
- acpi_status status;
-
- results.length = sizeof(out_objs);
- results.pointer = out_objs;
-
- status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
- *pVal = out_objs[0].integer.value;
-
- return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
+ return (status == AE_OK) ? 0 : -EIO;
}
-#endif
-
-static const char *method_hci /*= 0*/ ;
/* Perform a raw HCI call. Here we don't care about input or output buffer
* format.
*/
-static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
+static acpi_status hci_raw(struct toshiba_acpi_dev *dev,
+ const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
{
struct acpi_object_list params;
union acpi_object in_objs[HCI_WORDS];
@@ -220,7 +207,8 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
results.length = sizeof(out_objs);
results.pointer = out_objs;
- status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
+ status = acpi_evaluate_object(dev->acpi_dev->handle,
+ (char *)dev->method_hci, &params,
&results);
if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
for (i = 0; i < out_objs->package.count; ++i) {
@@ -237,85 +225,79 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
* may be useful (such as "not supported").
*/
-static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
+static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
u32 out[HCI_WORDS];
- acpi_status status = hci_raw(in, out);
+ acpi_status status = hci_raw(dev, in, out);
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
-static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
+static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
- acpi_status status = hci_raw(in, out);
+ acpi_status status = hci_raw(dev, in, out);
*out1 = out[2];
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
-static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result)
+static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 in2, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
u32 out[HCI_WORDS];
- acpi_status status = hci_raw(in, out);
+ acpi_status status = hci_raw(dev, in, out);
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
-static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
+static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *out2, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
u32 out[HCI_WORDS];
- acpi_status status = hci_raw(in, out);
+ acpi_status status = hci_raw(dev, in, out);
*out1 = out[2];
*out2 = out[3];
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
-struct toshiba_acpi_dev {
- struct platform_device *p_dev;
- struct rfkill *bt_rfk;
- struct input_dev *hotkey_dev;
- int illumination_installed;
- acpi_handle handle;
-
- const char *bt_name;
-
- struct mutex mutex;
-};
-
/* Illumination support */
-static int toshiba_illumination_available(void)
+static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
in[0] = 0xf100;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("Illumination device not available\n");
return 0;
}
in[0] = 0xf400;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
return 1;
}
static void toshiba_illumination_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
/* First request : initialize communication. */
in[0] = 0xf100;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("Illumination device not available\n");
return;
@@ -326,7 +308,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
in[0] = 0xf400;
in[1] = 0x14e;
in[2] = 1;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("ACPI call for illumination failed\n");
return;
@@ -336,7 +318,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
in[0] = 0xf400;
in[1] = 0x14e;
in[2] = 0;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("ACPI call for illumination failed.\n");
return;
@@ -347,11 +329,13 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
in[0] = 0xf200;
in[1] = 0;
in[2] = 0;
- hci_raw(in, out);
+ hci_raw(dev, in, out);
}
static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
@@ -359,7 +343,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
/* First request : initialize communication. */
in[0] = 0xf100;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("Illumination device not available\n");
return LED_OFF;
@@ -368,7 +352,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
/* Check the illumination */
in[0] = 0xf300;
in[1] = 0x14e;
- status = hci_raw(in, out);
+ status = hci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
pr_info("ACPI call for illumination failed.\n");
return LED_OFF;
@@ -380,46 +364,35 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
in[0] = 0xf200;
in[1] = 0;
in[2] = 0;
- hci_raw(in, out);
+ hci_raw(dev, in, out);
return result;
}
-static struct led_classdev toshiba_led = {
- .name = "toshiba::illumination",
- .max_brightness = 1,
- .brightness_set = toshiba_illumination_set,
- .brightness_get = toshiba_illumination_get,
-};
-
-static struct toshiba_acpi_dev toshiba_acpi = {
- .bt_name = "Toshiba Bluetooth",
-};
-
/* Bluetooth rfkill handlers */
-static u32 hci_get_bt_present(bool *present)
+static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0;
- hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
+ hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
if (hci_result == HCI_SUCCESS)
*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
return hci_result;
}
-static u32 hci_get_radio_state(bool *radio_state)
+static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0x0001;
- hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
+ hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
return hci_result;
@@ -436,8 +409,8 @@ static int bt_rfkill_set_block(void *data, bool blocked)
value = (blocked == false);
mutex_lock(&dev->mutex);
- if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
- err = -EBUSY;
+ if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) {
+ err = -EIO;
goto out;
}
@@ -446,11 +419,11 @@ static int bt_rfkill_set_block(void *data, bool blocked)
goto out;
}
- hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
- hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
+ hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
+ hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- err = -EBUSY;
+ err = -EIO;
else
err = 0;
out:
@@ -467,7 +440,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
mutex_lock(&dev->mutex);
- hci_result = hci_get_radio_state(&value);
+ hci_result = hci_get_radio_state(dev, &value);
if (hci_result != HCI_SUCCESS) {
/* Can't do anything useful */
mutex_unlock(&dev->mutex);
@@ -488,63 +461,64 @@ static const struct rfkill_ops toshiba_rfk_ops = {
};
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
-static struct backlight_device *toshiba_backlight_device;
-static int force_fan;
-static int last_key_event;
-static int key_event_valid;
static int get_lcd(struct backlight_device *bd)
{
+ struct toshiba_acpi_dev *dev = bl_get_data(bd);
u32 hci_result;
u32 value;
- hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
- if (hci_result == HCI_SUCCESS) {
+ hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
+ if (hci_result == HCI_SUCCESS)
return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
- } else
- return -EFAULT;
+
+ return -EIO;
}
static int lcd_proc_show(struct seq_file *m, void *v)
{
- int value = get_lcd(NULL);
+ struct toshiba_acpi_dev *dev = m->private;
+ int value;
+
+ if (!dev->backlight_dev)
+ return -ENODEV;
+ value = get_lcd(dev->backlight_dev);
if (value >= 0) {
seq_printf(m, "brightness: %d\n", value);
seq_printf(m, "brightness_levels: %d\n",
HCI_LCD_BRIGHTNESS_LEVELS);
- } else {
- pr_err("Error reading LCD brightness\n");
+ return 0;
}
- return 0;
+ pr_err("Error reading LCD brightness\n");
+ return -EIO;
}
static int lcd_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, lcd_proc_show, NULL);
+ return single_open(file, lcd_proc_show, PDE(inode)->data);
}
-static int set_lcd(int value)
+static int set_lcd(struct toshiba_acpi_dev *dev, int value)
{
u32 hci_result;
value = value << HCI_LCD_BRIGHTNESS_SHIFT;
- hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
- if (hci_result != HCI_SUCCESS)
- return -EFAULT;
-
- return 0;
+ hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
+ return hci_result == HCI_SUCCESS ? 0 : -EIO;
}
static int set_lcd_status(struct backlight_device *bd)
{
- return set_lcd(bd->props.brightness);
+ struct toshiba_acpi_dev *dev = bl_get_data(bd);
+ return set_lcd(dev, bd->props.brightness);
}
static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
+ struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
char cmd[42];
size_t len;
int value;
@@ -557,7 +531,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
if (sscanf(cmd, " brightness : %i", &value) == 1 &&
value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
- ret = set_lcd(value);
+ ret = set_lcd(dev, value);
if (ret == 0)
ret = count;
} else {
@@ -575,41 +549,49 @@ static const struct file_operations lcd_proc_fops = {
.write = lcd_proc_write,
};
-static int video_proc_show(struct seq_file *m, void *v)
+static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
{
u32 hci_result;
+
+ hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result);
+ return hci_result == HCI_SUCCESS ? 0 : -EIO;
+}
+
+static int video_proc_show(struct seq_file *m, void *v)
+{
+ struct toshiba_acpi_dev *dev = m->private;
u32 value;
+ int ret;
- hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
- if (hci_result == HCI_SUCCESS) {
+ ret = get_video_status(dev, &value);
+ if (!ret) {
int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
seq_printf(m, "lcd_out: %d\n", is_lcd);
seq_printf(m, "crt_out: %d\n", is_crt);
seq_printf(m, "tv_out: %d\n", is_tv);
- } else {
- pr_err("Error reading video out status\n");
}
- return 0;
+ return ret;
}
static int video_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, video_proc_show, NULL);
+ return single_open(file, video_proc_show, PDE(inode)->data);
}
static ssize_t video_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
+ struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
char *cmd, *buffer;
+ int ret;
int value;
int remain = count;
int lcd_out = -1;
int crt_out = -1;
int tv_out = -1;
- u32 hci_result;
u32 video_out;
cmd = kmalloc(count + 1, GFP_KERNEL);
@@ -644,8 +626,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
kfree(cmd);
- hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
- if (hci_result == HCI_SUCCESS) {
+ ret = get_video_status(dev, &video_out);
+ if (!ret) {
unsigned int new_video_out = video_out;
if (lcd_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
@@ -656,12 +638,10 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
/* To avoid unnecessary video disruption, only write the new
* video setting if something changed. */
if (new_video_out != video_out)
- write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
- } else {
- return -EFAULT;
+ ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
}
- return count;
+ return ret ? ret : count;
}
static const struct file_operations video_proc_fops = {
@@ -673,30 +653,38 @@ static const struct file_operations video_proc_fops = {
.write = video_proc_write,
};
-static int fan_proc_show(struct seq_file *m, void *v)
+static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status)
{
u32 hci_result;
+
+ hci_read1(dev, HCI_FAN, status, &hci_result);
+ return hci_result == HCI_SUCCESS ? 0 : -EIO;
+}
+
+static int fan_proc_show(struct seq_file *m, void *v)
+{
+ struct toshiba_acpi_dev *dev = m->private;
+ int ret;
u32 value;
- hci_read1(HCI_FAN, &value, &hci_result);
- if (hci_result == HCI_SUCCESS) {
+ ret = get_fan_status(dev, &value);
+ if (!ret) {
seq_printf(m, "running: %d\n", (value > 0));
- seq_printf(m, "force_on: %d\n", force_fan);
- } else {
- pr_err("Error reading fan status\n");
+ seq_printf(m, "force_on: %d\n", dev->force_fan);
}
- return 0;
+ return ret;
}
static int fan_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, fan_proc_show, NULL);
+ return single_open(file, fan_proc_show, PDE(inode)->data);
}
static ssize_t fan_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
+ struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
char cmd[42];
size_t len;
int value;
@@ -709,11 +697,11 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
if (sscanf(cmd, " force_on : %i", &value) == 1 &&
value >= 0 && value <= 1) {
- hci_write1(HCI_FAN, value, &hci_result);
+ hci_write1(dev, HCI_FAN, value, &hci_result);
if (hci_result != HCI_SUCCESS)
- return -EFAULT;
+ return -EIO;
else
- force_fan = value;
+ dev->force_fan = value;
} else {
return -EINVAL;
}
@@ -732,42 +720,43 @@ static const struct file_operations fan_proc_fops = {
static int keys_proc_show(struct seq_file *m, void *v)
{
+ struct toshiba_acpi_dev *dev = m->private;
u32 hci_result;
u32 value;
- if (!key_event_valid) {
- hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+ if (!dev->key_event_valid) {
+ hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
- key_event_valid = 1;
- last_key_event = value;
+ dev->key_event_valid = 1;
+ dev->last_key_event = value;
} else if (hci_result == HCI_EMPTY) {
/* better luck next time */
} else if (hci_result == HCI_NOT_SUPPORTED) {
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
* become disabled. */
- hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
pr_notice("Re-enabled hotkeys\n");
} else {
pr_err("Error reading hotkey status\n");
- goto end;
+ return -EIO;
}
}
- seq_printf(m, "hotkey_ready: %d\n", key_event_valid);
- seq_printf(m, "hotkey: 0x%04x\n", last_key_event);
-end:
+ seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid);
+ seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event);
return 0;
}
static int keys_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, keys_proc_show, NULL);
+ return single_open(file, keys_proc_show, PDE(inode)->data);
}
static ssize_t keys_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
+ struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
char cmd[42];
size_t len;
int value;
@@ -778,7 +767,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf,
cmd[len] = '\0';
if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
- key_event_valid = 0;
+ dev->key_event_valid = 0;
} else {
return -EINVAL;
}
@@ -820,21 +809,35 @@ static const struct file_operations version_proc_fops = {
#define PROC_TOSHIBA "toshiba"
-static void __init create_toshiba_proc_entries(void)
+static void __devinit
+create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
{
- proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops);
- proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops);
- proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops);
- proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops);
- proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops);
+ if (dev->backlight_dev)
+ proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
+ &lcd_proc_fops, dev);
+ if (dev->video_supported)
+ proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir,
+ &video_proc_fops, dev);
+ if (dev->fan_supported)
+ proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir,
+ &fan_proc_fops, dev);
+ if (dev->hotkey_dev)
+ proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir,
+ &keys_proc_fops, dev);
+ proc_create_data("version", S_IRUGO, toshiba_proc_dir,
+ &version_proc_fops, dev);
}
-static void remove_toshiba_proc_entries(void)
+static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
{
- remove_proc_entry("lcd", toshiba_proc_dir);
- remove_proc_entry("video", toshiba_proc_dir);
- remove_proc_entry("fan", toshiba_proc_dir);
- remove_proc_entry("keys", toshiba_proc_dir);
+ if (dev->backlight_dev)
+ remove_proc_entry("lcd", toshiba_proc_dir);
+ if (dev->video_supported)
+ remove_proc_entry("video", toshiba_proc_dir);
+ if (dev->fan_supported)
+ remove_proc_entry("fan", toshiba_proc_dir);
+ if (dev->hotkey_dev)
+ remove_proc_entry("keys", toshiba_proc_dir);
remove_proc_entry("version", toshiba_proc_dir);
}
@@ -843,224 +846,256 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
-static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
-{
- u32 hci_result, value;
-
- if (event != 0x80)
- return;
- do {
- hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
- if (hci_result == HCI_SUCCESS) {
- if (value == 0x100)
- continue;
- /* act on key press; ignore key release */
- if (value & 0x80)
- continue;
-
- if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
- value, 1, true)) {
- pr_info("Unknown key %x\n",
- value);
- }
- } else if (hci_result == HCI_NOT_SUPPORTED) {
- /* This is a workaround for an unresolved issue on
- * some machines where system events sporadically
- * become disabled. */
- hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
- pr_notice("Re-enabled hotkeys\n");
- }
- } while (hci_result != HCI_EMPTY);
-}
-
-static int __init toshiba_acpi_setup_keyboard(char *device)
+static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
acpi_status status;
int error;
- status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
- if (ACPI_FAILURE(status)) {
- pr_info("Unable to get notification device\n");
- return -ENODEV;
- }
-
- toshiba_acpi.hotkey_dev = input_allocate_device();
- if (!toshiba_acpi.hotkey_dev) {
+ dev->hotkey_dev = input_allocate_device();
+ if (!dev->hotkey_dev) {
pr_info("Unable to register input device\n");
return -ENOMEM;
}
- toshiba_acpi.hotkey_dev->name = "Toshiba input device";
- toshiba_acpi.hotkey_dev->phys = device;
- toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
+ dev->hotkey_dev->name = "Toshiba input device";
+ dev->hotkey_dev->phys = "toshiba_acpi/input0";
+ dev->hotkey_dev->id.bustype = BUS_HOST;
- error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
- toshiba_acpi_keymap, NULL);
+ error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
if (error)
goto err_free_dev;
- status = acpi_install_notify_handler(toshiba_acpi.handle,
- ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
- if (ACPI_FAILURE(status)) {
- pr_info("Unable to install hotkey notification\n");
- error = -ENODEV;
- goto err_free_keymap;
- }
-
- status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+ status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
pr_info("Unable to enable hotkeys\n");
error = -ENODEV;
- goto err_remove_notify;
+ goto err_free_keymap;
}
- error = input_register_device(toshiba_acpi.hotkey_dev);
+ error = input_register_device(dev->hotkey_dev);
if (error) {
pr_info("Unable to register input device\n");
- goto err_remove_notify;
+ goto err_free_keymap;
}
return 0;
- err_remove_notify:
- acpi_remove_notify_handler(toshiba_acpi.handle,
- ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
err_free_keymap:
- sparse_keymap_free(toshiba_acpi.hotkey_dev);
+ sparse_keymap_free(dev->hotkey_dev);
err_free_dev:
- input_free_device(toshiba_acpi.hotkey_dev);
- toshiba_acpi.hotkey_dev = NULL;
+ input_free_device(dev->hotkey_dev);
+ dev->hotkey_dev = NULL;
return error;
}
-static void toshiba_acpi_exit(void)
+static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
{
- if (toshiba_acpi.hotkey_dev) {
- acpi_remove_notify_handler(toshiba_acpi.handle,
- ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
- sparse_keymap_free(toshiba_acpi.hotkey_dev);
- input_unregister_device(toshiba_acpi.hotkey_dev);
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+
+ remove_toshiba_proc_entries(dev);
+
+ if (dev->hotkey_dev) {
+ input_unregister_device(dev->hotkey_dev);
+ sparse_keymap_free(dev->hotkey_dev);
}
- if (toshiba_acpi.bt_rfk) {
- rfkill_unregister(toshiba_acpi.bt_rfk);
- rfkill_destroy(toshiba_acpi.bt_rfk);
+ if (dev->bt_rfk) {
+ rfkill_unregister(dev->bt_rfk);
+ rfkill_destroy(dev->bt_rfk);
}
- if (toshiba_backlight_device)
- backlight_device_unregister(toshiba_backlight_device);
+ if (dev->backlight_dev)
+ backlight_device_unregister(dev->backlight_dev);
- remove_toshiba_proc_entries();
+ if (dev->illumination_supported)
+ led_classdev_unregister(&dev->led_dev);
- if (toshiba_proc_dir)
- remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ kfree(dev);
+
+ return 0;
+}
+
+static const char * __devinit find_hci_method(acpi_handle handle)
+{
+ acpi_status status;
+ acpi_handle hci_handle;
- if (toshiba_acpi.illumination_installed)
- led_classdev_unregister(&toshiba_led);
+ status = acpi_get_handle(handle, "GHCI", &hci_handle);
+ if (ACPI_SUCCESS(status))
+ return "GHCI";
- platform_device_unregister(toshiba_acpi.p_dev);
+ status = acpi_get_handle(handle, "SPFC", &hci_handle);
+ if (ACPI_SUCCESS(status))
+ return "SPFC";
- return;
+ return NULL;
}
-static int __init toshiba_acpi_init(void)
+static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
{
+ struct toshiba_acpi_dev *dev;
+ const char *hci_method;
u32 hci_result;
+ u32 dummy;
bool bt_present;
int ret = 0;
struct backlight_properties props;
- if (acpi_disabled)
- return -ENODEV;
-
- /* simple device detection: look for HCI method */
- if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
- method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
- if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
- pr_info("Unable to activate hotkeys\n");
- } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
- method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
- if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
- pr_info("Unable to activate hotkeys\n");
- } else
- return -ENODEV;
-
pr_info("Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);
- pr_info(" HCI method: %s\n", method_hci);
-
- mutex_init(&toshiba_acpi.mutex);
-
- toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
- -1, NULL, 0);
- if (IS_ERR(toshiba_acpi.p_dev)) {
- ret = PTR_ERR(toshiba_acpi.p_dev);
- pr_err("unable to register platform device\n");
- toshiba_acpi.p_dev = NULL;
- toshiba_acpi_exit();
- return ret;
+
+ hci_method = find_hci_method(acpi_dev->handle);
+ if (!hci_method) {
+ pr_err("HCI interface not found\n");
+ return -ENODEV;
}
- force_fan = 0;
- key_event_valid = 0;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->acpi_dev = acpi_dev;
+ dev->method_hci = hci_method;
+ acpi_dev->driver_data = dev;
- /* enable event fifo */
- hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+ if (toshiba_acpi_setup_keyboard(dev))
+ pr_info("Unable to activate hotkeys\n");
- toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
- if (!toshiba_proc_dir) {
- toshiba_acpi_exit();
- return -ENODEV;
- } else {
- create_toshiba_proc_entries();
- }
+ mutex_init(&dev->mutex);
+
+ /* enable event fifo */
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
- toshiba_backlight_device = backlight_device_register("toshiba",
- &toshiba_acpi.p_dev->dev,
- NULL,
- &toshiba_backlight_data,
- &props);
- if (IS_ERR(toshiba_backlight_device)) {
- ret = PTR_ERR(toshiba_backlight_device);
+ dev->backlight_dev = backlight_device_register("toshiba",
+ &acpi_dev->dev,
+ dev,
+ &toshiba_backlight_data,
+ &props);
+ if (IS_ERR(dev->backlight_dev)) {
+ ret = PTR_ERR(dev->backlight_dev);
pr_err("Could not register toshiba backlight device\n");
- toshiba_backlight_device = NULL;
- toshiba_acpi_exit();
- return ret;
+ dev->backlight_dev = NULL;
+ goto error;
}
+ dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev);
/* Register rfkill switch for Bluetooth */
- if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
- toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
- &toshiba_acpi.p_dev->dev,
- RFKILL_TYPE_BLUETOOTH,
- &toshiba_rfk_ops,
- &toshiba_acpi);
- if (!toshiba_acpi.bt_rfk) {
+ if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
+ dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
+ &acpi_dev->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &toshiba_rfk_ops,
+ dev);
+ if (!dev->bt_rfk) {
pr_err("unable to allocate rfkill device\n");
- toshiba_acpi_exit();
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error;
}
- ret = rfkill_register(toshiba_acpi.bt_rfk);
+ ret = rfkill_register(dev->bt_rfk);
if (ret) {
pr_err("unable to register rfkill device\n");
- rfkill_destroy(toshiba_acpi.bt_rfk);
- toshiba_acpi_exit();
- return ret;
+ rfkill_destroy(dev->bt_rfk);
+ goto error;
}
}
- toshiba_acpi.illumination_installed = 0;
- if (toshiba_illumination_available()) {
- if (!led_classdev_register(&(toshiba_acpi.p_dev->dev),
- &toshiba_led))
- toshiba_acpi.illumination_installed = 1;
+ if (toshiba_illumination_available(dev)) {
+ dev->led_dev.name = "toshiba::illumination";
+ dev->led_dev.max_brightness = 1;
+ dev->led_dev.brightness_set = toshiba_illumination_set;
+ dev->led_dev.brightness_get = toshiba_illumination_get;
+ if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
+ dev->illumination_supported = 1;
}
+ /* Determine whether or not BIOS supports fan and video interfaces */
+
+ ret = get_video_status(dev, &dummy);
+ dev->video_supported = !ret;
+
+ ret = get_fan_status(dev, &dummy);
+ dev->fan_supported = !ret;
+
+ create_toshiba_proc_entries(dev);
+
return 0;
+
+error:
+ toshiba_acpi_remove(acpi_dev, 0);
+ return ret;
+}
+
+static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
+{
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ u32 hci_result, value;
+
+ if (event != 0x80)
+ return;
+ do {
+ hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
+ if (hci_result == HCI_SUCCESS) {
+ if (value == 0x100)
+ continue;
+ /* act on key press; ignore key release */
+ if (value & 0x80)
+ continue;
+
+ if (!sparse_keymap_report_event(dev->hotkey_dev,
+ value, 1, true)) {
+ pr_info("Unknown key %x\n",
+ value);
+ }
+ } else if (hci_result == HCI_NOT_SUPPORTED) {
+ /* This is a workaround for an unresolved issue on
+ * some machines where system events sporadically
+ * become disabled. */
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
+ pr_notice("Re-enabled hotkeys\n");
+ }
+ } while (hci_result != HCI_EMPTY);
+}
+
+
+static struct acpi_driver toshiba_acpi_driver = {
+ .name = "Toshiba ACPI driver",
+ .owner = THIS_MODULE,
+ .ids = toshiba_device_ids,
+ .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+ .ops = {
+ .add = toshiba_acpi_add,
+ .remove = toshiba_acpi_remove,
+ .notify = toshiba_acpi_notify,
+ },
+};
+
+static int __init toshiba_acpi_init(void)
+{
+ int ret;
+
+ toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
+ if (!toshiba_proc_dir) {
+ pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
+ return -ENODEV;
+ }
+
+ ret = acpi_bus_register_driver(&toshiba_acpi_driver);
+ if (ret) {
+ pr_err("Failed to register ACPI driver: %d\n", ret);
+ remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ }
+
+ return ret;
+}
+
+static void __exit toshiba_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&toshiba_acpi_driver);
+ if (toshiba_proc_dir)
+ remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
}
module_init(toshiba_acpi_init);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index f23d5a84e7b1..9b88be42b6cd 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -754,9 +754,13 @@ static void wmi_free_devices(void)
struct wmi_block *wblock, *next;
/* Delete devices for all the GUIDs */
- list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
+ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
+ list_del(&wblock->list);
if (wblock->dev.class)
device_unregister(&wblock->dev);
+ else
+ kfree(wblock);
+ }
}
static bool guid_already_parsed(const char *guid_string)
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 57de051a74b3..9f88641e67f9 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -70,6 +70,7 @@ config BATTERY_DS2760
config BATTERY_DS2780
tristate "DS2780 battery driver"
+ depends on HAS_IOMEM
select W1
select W1_SLAVE_DS2780
help
diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c
index 1fefe82e12e3..91a783d72360 100644
--- a/drivers/power/ds2780_battery.c
+++ b/drivers/power/ds2780_battery.c
@@ -39,6 +39,7 @@ struct ds2780_device_info {
struct device *dev;
struct power_supply bat;
struct device *w1_dev;
+ struct task_struct *mutex_holder;
};
enum current_types {
@@ -49,8 +50,8 @@ enum current_types {
static const char model[] = "DS2780";
static const char manufacturer[] = "Maxim/Dallas";
-static inline struct ds2780_device_info *to_ds2780_device_info(
- struct power_supply *psy)
+static inline struct ds2780_device_info *
+to_ds2780_device_info(struct power_supply *psy)
{
return container_of(psy, struct ds2780_device_info, bat);
}
@@ -60,17 +61,28 @@ static inline struct power_supply *to_power_supply(struct device *dev)
return dev_get_drvdata(dev);
}
-static inline int ds2780_read8(struct device *dev, u8 *val, int addr)
+static inline int ds2780_battery_io(struct ds2780_device_info *dev_info,
+ char *buf, int addr, size_t count, int io)
{
- return w1_ds2780_io(dev, val, addr, sizeof(u8), 0);
+ if (dev_info->mutex_holder == current)
+ return w1_ds2780_io_nolock(dev_info->w1_dev, buf, addr, count, io);
+ else
+ return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io);
+}
+
+static inline int ds2780_read8(struct ds2780_device_info *dev_info, u8 *val,
+ int addr)
+{
+ return ds2780_battery_io(dev_info, val, addr, sizeof(u8), 0);
}
-static int ds2780_read16(struct device *dev, s16 *val, int addr)
+static int ds2780_read16(struct ds2780_device_info *dev_info, s16 *val,
+ int addr)
{
int ret;
u8 raw[2];
- ret = w1_ds2780_io(dev, raw, addr, sizeof(u8) * 2, 0);
+ ret = ds2780_battery_io(dev_info, raw, addr, sizeof(raw), 0);
if (ret < 0)
return ret;
@@ -79,16 +91,16 @@ static int ds2780_read16(struct device *dev, s16 *val, int addr)
return 0;
}
-static inline int ds2780_read_block(struct device *dev, u8 *val, int addr,
- size_t count)
+static inline int ds2780_read_block(struct ds2780_device_info *dev_info,
+ u8 *val, int addr, size_t count)
{
- return w1_ds2780_io(dev, val, addr, count, 0);
+ return ds2780_battery_io(dev_info, val, addr, count, 0);
}
-static inline int ds2780_write(struct device *dev, u8 *val, int addr,
- size_t count)
+static inline int ds2780_write(struct ds2780_device_info *dev_info, u8 *val,
+ int addr, size_t count)
{
- return w1_ds2780_io(dev, val, addr, count, 1);
+ return ds2780_battery_io(dev_info, val, addr, count, 1);
}
static inline int ds2780_store_eeprom(struct device *dev, int addr)
@@ -122,7 +134,7 @@ static int ds2780_set_sense_register(struct ds2780_device_info *dev_info,
{
int ret;
- ret = ds2780_write(dev_info->w1_dev, &conductance,
+ ret = ds2780_write(dev_info, &conductance,
DS2780_RSNSP_REG, sizeof(u8));
if (ret < 0)
return ret;
@@ -134,7 +146,7 @@ static int ds2780_set_sense_register(struct ds2780_device_info *dev_info,
static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info,
u16 *rsgain)
{
- return ds2780_read16(dev_info->w1_dev, rsgain, DS2780_RSGAIN_MSB_REG);
+ return ds2780_read16(dev_info, rsgain, DS2780_RSGAIN_MSB_REG);
}
/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
@@ -144,8 +156,8 @@ static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info,
int ret;
u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
- ret = ds2780_write(dev_info->w1_dev, raw,
- DS2780_RSGAIN_MSB_REG, sizeof(u8) * 2);
+ ret = ds2780_write(dev_info, raw,
+ DS2780_RSGAIN_MSB_REG, sizeof(raw));
if (ret < 0)
return ret;
@@ -167,7 +179,7 @@ static int ds2780_get_voltage(struct ds2780_device_info *dev_info,
* Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
* voltage LSB register
*/
- ret = ds2780_read16(dev_info->w1_dev, &voltage_raw,
+ ret = ds2780_read16(dev_info, &voltage_raw,
DS2780_VOLT_MSB_REG);
if (ret < 0)
return ret;
@@ -196,7 +208,7 @@ static int ds2780_get_temperature(struct ds2780_device_info *dev_info,
* Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
* temperature LSB register
*/
- ret = ds2780_read16(dev_info->w1_dev, &temperature_raw,
+ ret = ds2780_read16(dev_info, &temperature_raw,
DS2780_TEMP_MSB_REG);
if (ret < 0)
return ret;
@@ -222,13 +234,13 @@ static int ds2780_get_current(struct ds2780_device_info *dev_info,
* The units of measurement for current are dependent on the value of
* the sense resistor.
*/
- ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG);
+ ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
if (ret < 0)
return ret;
if (sense_res_raw == 0) {
dev_err(dev_info->dev, "sense resistor value is 0\n");
- return -ENXIO;
+ return -EINVAL;
}
sense_res = 1000 / sense_res_raw;
@@ -248,7 +260,7 @@ static int ds2780_get_current(struct ds2780_device_info *dev_info,
* Bits 7 - 0 of the current value are in bits 7 - 0 of the current
* LSB register
*/
- ret = ds2780_read16(dev_info->w1_dev, &current_raw, reg_msb);
+ ret = ds2780_read16(dev_info, &current_raw, reg_msb);
if (ret < 0)
return ret;
@@ -267,7 +279,7 @@ static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info,
* The units of measurement for accumulated current are dependent on
* the value of the sense resistor.
*/
- ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG);
+ ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
if (ret < 0)
return ret;
@@ -285,7 +297,7 @@ static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info,
* Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
* LSB register
*/
- ret = ds2780_read16(dev_info->w1_dev, &current_raw, DS2780_ACR_MSB_REG);
+ ret = ds2780_read16(dev_info, &current_raw, DS2780_ACR_MSB_REG);
if (ret < 0)
return ret;
@@ -299,7 +311,7 @@ static int ds2780_get_capacity(struct ds2780_device_info *dev_info,
int ret;
u8 raw;
- ret = ds2780_read8(dev_info->w1_dev, &raw, DS2780_RARC_REG);
+ ret = ds2780_read8(dev_info, &raw, DS2780_RARC_REG);
if (ret < 0)
return ret;
@@ -345,7 +357,7 @@ static int ds2780_get_charge_now(struct ds2780_device_info *dev_info,
* Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
* LSB register
*/
- ret = ds2780_read16(dev_info->w1_dev, &charge_raw, DS2780_RAAC_MSB_REG);
+ ret = ds2780_read16(dev_info, &charge_raw, DS2780_RAAC_MSB_REG);
if (ret < 0)
return ret;
@@ -356,7 +368,7 @@ static int ds2780_get_charge_now(struct ds2780_device_info *dev_info,
static int ds2780_get_control_register(struct ds2780_device_info *dev_info,
u8 *control_reg)
{
- return ds2780_read8(dev_info->w1_dev, control_reg, DS2780_CONTROL_REG);
+ return ds2780_read8(dev_info, control_reg, DS2780_CONTROL_REG);
}
static int ds2780_set_control_register(struct ds2780_device_info *dev_info,
@@ -364,7 +376,7 @@ static int ds2780_set_control_register(struct ds2780_device_info *dev_info,
{
int ret;
- ret = ds2780_write(dev_info->w1_dev, &control_reg,
+ ret = ds2780_write(dev_info, &control_reg,
DS2780_CONTROL_REG, sizeof(u8));
if (ret < 0)
return ret;
@@ -503,7 +515,7 @@ static ssize_t ds2780_get_sense_resistor_value(struct device *dev,
struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
- ret = ds2780_read8(dev_info->w1_dev, &sense_resistor, DS2780_RSNSP_REG);
+ ret = ds2780_read8(dev_info, &sense_resistor, DS2780_RSNSP_REG);
if (ret < 0)
return ret;
@@ -584,7 +596,7 @@ static ssize_t ds2780_get_pio_pin(struct device *dev,
struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
- ret = ds2780_read8(dev_info->w1_dev, &sfr, DS2780_SFR_REG);
+ ret = ds2780_read8(dev_info, &sfr, DS2780_SFR_REG);
if (ret < 0)
return ret;
@@ -611,7 +623,7 @@ static ssize_t ds2780_set_pio_pin(struct device *dev,
return -EINVAL;
}
- ret = ds2780_write(dev_info->w1_dev, &new_setting,
+ ret = ds2780_write(dev_info, &new_setting,
DS2780_SFR_REG, sizeof(u8));
if (ret < 0)
return ret;
@@ -632,7 +644,7 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
DS2780_EEPROM_BLOCK1_END -
DS2780_EEPROM_BLOCK1_START + 1 - off);
- return ds2780_read_block(dev_info->w1_dev, buf,
+ return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count);
}
@@ -650,7 +662,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
DS2780_EEPROM_BLOCK1_END -
DS2780_EEPROM_BLOCK1_START + 1 - off);
- ret = ds2780_write(dev_info->w1_dev, buf,
+ ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count);
if (ret < 0)
return ret;
@@ -685,9 +697,8 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
DS2780_EEPROM_BLOCK0_END -
DS2780_EEPROM_BLOCK0_START + 1 - off);
- return ds2780_read_block(dev_info->w1_dev, buf,
+ return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count);
-
}
static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
@@ -704,7 +715,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
DS2780_EEPROM_BLOCK0_END -
DS2780_EEPROM_BLOCK0_START + 1 - off);
- ret = ds2780_write(dev_info->w1_dev, buf,
+ ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count);
if (ret < 0)
return ret;
@@ -768,6 +779,7 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev)
dev_info->bat.properties = ds2780_battery_props;
dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props);
dev_info->bat.get_property = ds2780_battery_get_property;
+ dev_info->mutex_holder = current;
ret = power_supply_register(&pdev->dev, &dev_info->bat);
if (ret) {
@@ -797,6 +809,8 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev)
goto fail_remove_bin_file;
}
+ dev_info->mutex_holder = NULL;
+
return 0;
fail_remove_bin_file:
@@ -816,6 +830,8 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev)
{
struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
+ dev_info->mutex_holder = current;
+
/* remove attributes */
sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
index 8520a7f4dd62..445197d4a8c4 100644
--- a/drivers/pps/clients/Kconfig
+++ b/drivers/pps/clients/Kconfig
@@ -29,4 +29,13 @@ config PPS_CLIENT_PARPORT
If you say yes here you get support for a PPS source connected
with the interrupt pin of your parallel port.
+config PPS_CLIENT_GPIO
+ tristate "PPS client using GPIO"
+ depends on PPS && GENERIC_HARDIRQS
+ help
+ If you say yes here you get support for a PPS source using
+ GPIO. To be useful you must also register a platform device
+ specifying the GPIO pin and other options, usually in your board
+ setup.
+
endif
diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
index 4feb7e9e71ee..a461d15f4a2e 100644
--- a/drivers/pps/clients/Makefile
+++ b/drivers/pps/clients/Makefile
@@ -5,5 +5,6 @@
obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o
obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o
obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o
+obj-$(CONFIG_PPS_CLIENT_GPIO) += pps-gpio.o
ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c
new file mode 100644
index 000000000000..655055545479
--- /dev/null
+++ b/drivers/pps/clients/pps-gpio.c
@@ -0,0 +1,227 @@
+/*
+ * pps-gpio.c -- PPS client driver using GPIO
+ *
+ *
+ * Copyright (C) 2010 Ricardo Martins <rasm@fe.up.pt>
+ * Copyright (C) 2011 James Nuss <jamesnuss@nanometrics.ca>
+ *
+ * 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.
+ */
+
+#define PPS_GPIO_NAME "pps-gpio"
+#define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pps_kernel.h>
+#include <linux/pps-gpio.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+
+/* Info for each registered platform device */
+struct pps_gpio_device_data {
+ int irq; /* IRQ used as PPS source */
+ struct pps_device *pps; /* PPS source device */
+ struct pps_source_info info; /* PPS source information */
+ const struct pps_gpio_platform_data *pdata;
+};
+
+/*
+ * Report the PPS event
+ */
+
+static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
+{
+ const struct pps_gpio_device_data *info;
+ struct pps_event_time ts;
+ int rising_edge;
+
+ /* Get the time stamp first */
+ pps_get_ts(&ts);
+
+ info = data;
+
+ rising_edge = gpio_get_value(info->pdata->gpio_pin);
+ if ((rising_edge && !info->pdata->assert_falling_edge) ||
+ (!rising_edge && info->pdata->assert_falling_edge))
+ pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
+ else if (info->pdata->capture_clear &&
+ ((rising_edge && info->pdata->assert_falling_edge) ||
+ (!rising_edge && !info->pdata->assert_falling_edge)))
+ pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static int pps_gpio_setup(struct platform_device *pdev)
+{
+ int ret;
+ const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+ ret = gpio_request(pdata->gpio_pin, pdata->gpio_label);
+ if (ret) {
+ pr_warning("failed to request GPIO %u\n", pdata->gpio_pin);
+ return -EINVAL;
+ }
+
+ ret = gpio_direction_input(pdata->gpio_pin);
+ if (ret) {
+ pr_warning("failed to set pin direction\n");
+ gpio_free(pdata->gpio_pin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long
+get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata)
+{
+ unsigned long flags = pdata->assert_falling_edge ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+
+ if (pdata->capture_clear) {
+ flags |= ((flags & IRQF_TRIGGER_RISING) ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
+ }
+
+ return flags;
+}
+
+static int pps_gpio_probe(struct platform_device *pdev)
+{
+ struct pps_gpio_device_data *data;
+ int irq;
+ int ret;
+ int err;
+ int pps_default_params;
+ const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+
+ /* GPIO setup */
+ ret = pps_gpio_setup(pdev);
+ if (ret)
+ return -EINVAL;
+
+ /* IRQ setup */
+ irq = gpio_to_irq(pdata->gpio_pin);
+ if (irq < 0) {
+ pr_err("failed to map GPIO to IRQ: %d\n", irq);
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ /* allocate space for device info */
+ data = kzalloc(sizeof(struct pps_gpio_device_data), GFP_KERNEL);
+ if (data == NULL) {
+ err = -ENOMEM;
+ goto return_error;
+ }
+
+ /* initialize PPS specific parts of the bookkeeping data structure. */
+ data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
+ PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
+ if (pdata->capture_clear)
+ data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
+ PPS_ECHOCLEAR;
+ data->info.owner = THIS_MODULE;
+ snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
+ pdev->name, pdev->id);
+
+ /* register PPS source */
+ pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
+ if (pdata->capture_clear)
+ pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
+ data->pps = pps_register_source(&data->info, pps_default_params);
+ if (data->pps == NULL) {
+ kfree(data);
+ pr_err("failed to register IRQ %d as PPS source\n", irq);
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ data->irq = irq;
+ data->pdata = pdata;
+
+ /* register IRQ interrupt handler */
+ ret = request_irq(irq, pps_gpio_irq_handler,
+ get_irqf_trigger_flags(pdata), data->info.name, data);
+ if (ret) {
+ pps_unregister_source(data->pps);
+ kfree(data);
+ pr_err("failed to acquire IRQ %d\n", irq);
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ platform_set_drvdata(pdev, data);
+ dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq);
+
+ return 0;
+
+return_error:
+ gpio_free(pdata->gpio_pin);
+ return err;
+}
+
+static int pps_gpio_remove(struct platform_device *pdev)
+{
+ struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
+ const struct pps_gpio_platform_data *pdata = data->pdata;
+
+ platform_set_drvdata(pdev, NULL);
+ free_irq(data->irq, data);
+ gpio_free(pdata->gpio_pin);
+ pps_unregister_source(data->pps);
+ pr_info("removed IRQ %d as PPS source\n", data->irq);
+ kfree(data);
+ return 0;
+}
+
+static struct platform_driver pps_gpio_driver = {
+ .probe = pps_gpio_probe,
+ .remove = __devexit_p(pps_gpio_remove),
+ .driver = {
+ .name = PPS_GPIO_NAME,
+ .owner = THIS_MODULE
+ },
+};
+
+static int __init pps_gpio_init(void)
+{
+ int ret = platform_driver_register(&pps_gpio_driver);
+ if (ret < 0)
+ pr_err("failed to register platform driver\n");
+ return ret;
+}
+
+static void __exit pps_gpio_exit(void)
+{
+ platform_driver_unregister(&pps_gpio_driver);
+ pr_debug("unregistered platform driver\n");
+}
+
+module_init(pps_gpio_init);
+module_exit(pps_gpio_exit);
+
+MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
+MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
+MODULE_DESCRIPTION("Use GPIO pin as PPS source");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.0");
diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c
index 82583b0ff82d..436b4e4e71a1 100644
--- a/drivers/pps/clients/pps-ktimer.c
+++ b/drivers/pps/clients/pps-ktimer.c
@@ -52,17 +52,6 @@ static void pps_ktimer_event(unsigned long ptr)
}
/*
- * The echo function
- */
-
-static void pps_ktimer_echo(struct pps_device *pps, int event, void *data)
-{
- dev_info(pps->dev, "echo %s %s\n",
- event & PPS_CAPTUREASSERT ? "assert" : "",
- event & PPS_CAPTURECLEAR ? "clear" : "");
-}
-
-/*
* The PPS info struct
*/
@@ -72,7 +61,6 @@ static struct pps_source_info pps_ktimer_info = {
.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
PPS_ECHOASSERT |
PPS_CANWAIT | PPS_TSFMT_TSPEC,
- .echo = pps_ktimer_echo,
.owner = THIS_MODULE,
};
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index c571d6dd8f61..e1b4705ae3ec 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -133,14 +133,6 @@ out_both:
return;
}
-/* the PPS echo function */
-static void pps_echo(struct pps_device *pps, int event, void *data)
-{
- dev_info(pps->dev, "echo %s %s\n",
- event & PPS_CAPTUREASSERT ? "assert" : "",
- event & PPS_CAPTURECLEAR ? "clear" : "");
-}
-
static void parport_attach(struct parport *port)
{
struct pps_client_pp *device;
@@ -151,7 +143,6 @@ static void parport_attach(struct parport *port)
PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
PPS_ECHOASSERT | PPS_ECHOCLEAR | \
PPS_CANWAIT | PPS_TSFMT_TSPEC,
- .echo = pps_echo,
.owner = THIS_MODULE,
.dev = NULL
};
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index a4e8eb9fece6..f197e8ea185c 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -52,6 +52,14 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
ts->sec += offset->sec;
}
+static void pps_echo_client_default(struct pps_device *pps, int event,
+ void *data)
+{
+ dev_info(pps->dev, "echo %s %s\n",
+ event & PPS_CAPTUREASSERT ? "assert" : "",
+ event & PPS_CAPTURECLEAR ? "clear" : "");
+}
+
/*
* Exported functions
*/
@@ -80,13 +88,6 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
err = -EINVAL;
goto pps_register_source_exit;
}
- if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
- info->echo == NULL) {
- pr_err("%s: echo function is not defined\n",
- info->name);
- err = -EINVAL;
- goto pps_register_source_exit;
- }
if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
pr_err("%s: unspecified time format\n",
info->name);
@@ -108,6 +109,11 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
pps->params.mode = default_params;
pps->info = *info;
+ /* check for default echo function */
+ if ((pps->info.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) &&
+ pps->info.echo == NULL)
+ pps->info.echo = pps_echo_client_default;
+
init_waitqueue_head(&pps->queue);
spin_lock_init(&pps->lock);
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
index 070211a5955c..bc8719238793 100644
--- a/drivers/rapidio/Kconfig
+++ b/drivers/rapidio/Kconfig
@@ -1,6 +1,8 @@
#
# RapidIO configuration
#
+source "drivers/rapidio/devices/Kconfig"
+
config RAPIDIO_DISC_TIMEOUT
int "Discovery timeout duration (seconds)"
depends on RAPIDIO
@@ -20,8 +22,6 @@ config RAPIDIO_ENABLE_RX_TX_PORTS
ports for Input/Output direction to allow other traffic
than Maintenance transfers.
-source "drivers/rapidio/switches/Kconfig"
-
config RAPIDIO_DEBUG
bool "RapidIO subsystem debug messages"
depends on RAPIDIO
@@ -32,3 +32,5 @@ config RAPIDIO_DEBUG
going on.
If you are unsure about this, say N here.
+
+source "drivers/rapidio/switches/Kconfig"
diff --git a/drivers/rapidio/Makefile b/drivers/rapidio/Makefile
index 89b8eca825b5..ec3fb8121004 100644
--- a/drivers/rapidio/Makefile
+++ b/drivers/rapidio/Makefile
@@ -4,5 +4,6 @@
obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o
obj-$(CONFIG_RAPIDIO) += switches/
+obj-$(CONFIG_RAPIDIO) += devices/
subdir-ccflags-$(CONFIG_RAPIDIO_DEBUG) := -DDEBUG
diff --git a/drivers/rapidio/devices/Kconfig b/drivers/rapidio/devices/Kconfig
new file mode 100644
index 000000000000..12a9d7f7040b
--- /dev/null
+++ b/drivers/rapidio/devices/Kconfig
@@ -0,0 +1,10 @@
+#
+# RapidIO master port configuration
+#
+
+config RAPIDIO_TSI721
+ bool "IDT Tsi721 PCI Express SRIO Controller support"
+ depends on RAPIDIO && PCIEPORTBUS
+ default "n"
+ ---help---
+ Include support for IDT Tsi721 PCI Express Serial RapidIO controller.
diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile
new file mode 100644
index 000000000000..3b7b4e2dff7c
--- /dev/null
+++ b/drivers/rapidio/devices/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for RapidIO devices
+#
+
+obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
new file mode 100644
index 000000000000..5225930a10cd
--- /dev/null
+++ b/drivers/rapidio/devices/tsi721.c
@@ -0,0 +1,2360 @@
+/*
+ * RapidIO mport driver for Tsi721 PCIExpress-to-SRIO bridge
+ *
+ * Copyright 2011 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ * Chul Kim <chul.kim@idt.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/io.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/delay.h>
+
+#include "tsi721.h"
+
+#define DEBUG_PW /* Inbound Port-Write debugging */
+
+static void tsi721_omsg_handler(struct tsi721_device *priv, int ch);
+static void tsi721_imsg_handler(struct tsi721_device *priv, int ch);
+
+/**
+ * tsi721_lcread - read from local SREP config space
+ * @mport: RapidIO master port info
+ * @index: ID of RapdiIO interface
+ * @offset: Offset into configuration space
+ * @len: Length (in bytes) of the maintenance transaction
+ * @data: Value to be read into
+ *
+ * Generates a local SREP space read. Returns %0 on
+ * success or %-EINVAL on failure.
+ */
+static int tsi721_lcread(struct rio_mport *mport, int index, u32 offset,
+ int len, u32 *data)
+{
+ struct tsi721_device *priv = mport->priv;
+
+ if (len != sizeof(u32))
+ return -EINVAL; /* only 32-bit access is supported */
+
+ *data = ioread32(priv->regs + offset);
+
+ return 0;
+}
+
+/**
+ * tsi721_lcwrite - write into local SREP config space
+ * @mport: RapidIO master port info
+ * @index: ID of RapdiIO interface
+ * @offset: Offset into configuration space
+ * @len: Length (in bytes) of the maintenance transaction
+ * @data: Value to be written
+ *
+ * Generates a local write into SREP configuration space. Returns %0 on
+ * success or %-EINVAL on failure.
+ */
+static int tsi721_lcwrite(struct rio_mport *mport, int index, u32 offset,
+ int len, u32 data)
+{
+ struct tsi721_device *priv = mport->priv;
+
+ if (len != sizeof(u32))
+ return -EINVAL; /* only 32-bit access is supported */
+
+ iowrite32(data, priv->regs + offset);
+
+ return 0;
+}
+
+/**
+ * tsi721_maint_dma - Helper function to generate RapidIO maintenance
+ * transactions using designated Tsi721 DMA channel.
+ * @priv: pointer to tsi721 private data
+ * @sys_size: RapdiIO transport system size
+ * @destid: Destination ID of transaction
+ * @hopcount: Number of hops to target device
+ * @offset: Offset into configuration space
+ * @len: Length (in bytes) of the maintenance transaction
+ * @data: Location to be read from or write into
+ * @do_wr: Operation flag (1 == MAINT_WR)
+ *
+ * Generates a RapidIO maintenance transaction (Read or Write).
+ * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
+ */
+static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
+ u16 destid, u8 hopcount, u32 offset, int len,
+ u32 *data, int do_wr)
+{
+ struct tsi721_dma_desc *bd_ptr;
+ u32 rd_count, swr_ptr, ch_stat;
+ int i, err = 0;
+ u32 op = do_wr ? MAINT_WR : MAINT_RD;
+
+ if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
+ return -EINVAL;
+
+ bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base;
+
+ rd_count = ioread32(
+ priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT));
+
+ /* Initialize DMA descriptor */
+ bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
+ bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04);
+ bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset);
+ bd_ptr[0].raddr_hi = 0;
+ if (do_wr)
+ bd_ptr[0].data[0] = cpu_to_be32p(data);
+ else
+ bd_ptr[0].data[0] = 0xffffffff;
+
+ mb();
+
+ /* Start DMA operation */
+ iowrite32(rd_count + 2,
+ priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+ ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+ i = 0;
+
+ /* Wait until DMA transfer is finished */
+ while ((ch_stat = ioread32(priv->regs +
+ TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) {
+ udelay(1);
+ if (++i >= 5000000) {
+ dev_dbg(&priv->pdev->dev,
+ "%s : DMA[%d] read timeout ch_status=%x\n",
+ __func__, TSI721_DMACH_MAINT, ch_stat);
+ if (!do_wr)
+ *data = 0xffffffff;
+ err = -EIO;
+ goto err_out;
+ }
+ }
+
+ if (ch_stat & TSI721_DMAC_STS_ABORT) {
+ /* If DMA operation aborted due to error,
+ * reinitialize DMA channel
+ */
+ dev_dbg(&priv->pdev->dev, "%s : DMA ABORT ch_stat=%x\n",
+ __func__, ch_stat);
+ dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n",
+ do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset);
+ iowrite32(TSI721_DMAC_INT_ALL,
+ priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT));
+ iowrite32(TSI721_DMAC_CTL_INIT,
+ priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT));
+ udelay(10);
+ iowrite32(0, priv->regs +
+ TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+ udelay(1);
+ if (!do_wr)
+ *data = 0xffffffff;
+ err = -EIO;
+ goto err_out;
+ }
+
+ if (!do_wr)
+ *data = be32_to_cpu(bd_ptr[0].data[0]);
+
+ /*
+ * Update descriptor status FIFO RD pointer.
+ * NOTE: Skipping check and clear FIFO entries because we are waiting
+ * for transfer to be completed.
+ */
+ swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT));
+ iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT));
+err_out:
+
+ return err;
+}
+
+/**
+ * tsi721_cread_dma - Generate a RapidIO maintenance read transaction
+ * using Tsi721 BDMA engine.
+ * @mport: RapidIO master port control structure
+ * @index: ID of RapdiIO interface
+ * @destid: Destination ID of transaction
+ * @hopcount: Number of hops to target device
+ * @offset: Offset into configuration space
+ * @len: Length (in bytes) of the maintenance transaction
+ * @val: Location to be read into
+ *
+ * Generates a RapidIO maintenance read transaction.
+ * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
+ */
+static int tsi721_cread_dma(struct rio_mport *mport, int index, u16 destid,
+ u8 hopcount, u32 offset, int len, u32 *data)
+{
+ struct tsi721_device *priv = mport->priv;
+
+ return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
+ offset, len, data, 0);
+}
+
+/**
+ * tsi721_cwrite_dma - Generate a RapidIO maintenance write transaction
+ * using Tsi721 BDMA engine
+ * @mport: RapidIO master port control structure
+ * @index: ID of RapdiIO interface
+ * @destid: Destination ID of transaction
+ * @hopcount: Number of hops to target device
+ * @offset: Offset into configuration space
+ * @len: Length (in bytes) of the maintenance transaction
+ * @val: Value to be written
+ *
+ * Generates a RapidIO maintenance write transaction.
+ * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
+ */
+static int tsi721_cwrite_dma(struct rio_mport *mport, int index, u16 destid,
+ u8 hopcount, u32 offset, int len, u32 data)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 temp = data;
+
+ return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
+ offset, len, &temp, 1);
+}
+
+/**
+ * tsi721_pw_handler - Tsi721 inbound port-write interrupt handler
+ * @mport: RapidIO master port structure
+ *
+ * Handles inbound port-write interrupts. Copies PW message from an internal
+ * buffer into PW message FIFO and schedules deferred routine to process
+ * queued messages.
+ */
+static int
+tsi721_pw_handler(struct rio_mport *mport)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 pw_stat;
+ u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)];
+
+
+ pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT);
+
+ if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) {
+ pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0));
+ pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1));
+ pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2));
+ pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3));
+
+ /* Queue PW message (if there is room in FIFO),
+ * otherwise discard it.
+ */
+ spin_lock(&priv->pw_fifo_lock);
+ if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE)
+ kfifo_in(&priv->pw_fifo, pw_buf,
+ TSI721_RIO_PW_MSG_SIZE);
+ else
+ priv->pw_discard_count++;
+ spin_unlock(&priv->pw_fifo_lock);
+ }
+
+ /* Clear pending PW interrupts */
+ iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
+ priv->regs + TSI721_RIO_PW_RX_STAT);
+
+ schedule_work(&priv->pw_work);
+
+ return 0;
+}
+
+static void tsi721_pw_dpc(struct work_struct *work)
+{
+ struct tsi721_device *priv = container_of(work, struct tsi721_device,
+ pw_work);
+ u32 msg_buffer[RIO_PW_MSG_SIZE/sizeof(u32)]; /* Use full size PW message
+ buffer for RIO layer */
+
+ /*
+ * Process port-write messages
+ */
+ while (kfifo_out_spinlocked(&priv->pw_fifo, (unsigned char *)msg_buffer,
+ TSI721_RIO_PW_MSG_SIZE, &priv->pw_fifo_lock)) {
+ /* Process one message */
+#ifdef DEBUG_PW
+ {
+ u32 i;
+ pr_debug("%s : Port-Write Message:", __func__);
+ for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); ) {
+ pr_debug("0x%02x: %08x %08x %08x %08x", i*4,
+ msg_buffer[i], msg_buffer[i + 1],
+ msg_buffer[i + 2], msg_buffer[i + 3]);
+ i += 4;
+ }
+ pr_debug("\n");
+ }
+#endif
+ /* Pass the port-write message to RIO core for processing */
+ rio_inb_pwrite_handler((union rio_pw_msg *)msg_buffer);
+ }
+}
+
+/**
+ * tsi721_pw_enable - enable/disable port-write interface init
+ * @mport: Master port implementing the port write unit
+ * @enable: 1=enable; 0=disable port-write message handling
+ */
+static int tsi721_pw_enable(struct rio_mport *mport, int enable)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 rval;
+
+ rval = ioread32(priv->regs + TSI721_RIO_EM_INT_ENABLE);
+
+ if (enable)
+ rval |= TSI721_RIO_EM_INT_ENABLE_PW_RX;
+ else
+ rval &= ~TSI721_RIO_EM_INT_ENABLE_PW_RX;
+
+ /* Clear pending PW interrupts */
+ iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
+ priv->regs + TSI721_RIO_PW_RX_STAT);
+ /* Update enable bits */
+ iowrite32(rval, priv->regs + TSI721_RIO_EM_INT_ENABLE);
+
+ return 0;
+}
+
+/**
+ * tsi721_dsend - Send a RapidIO doorbell
+ * @mport: RapidIO master port info
+ * @index: ID of RapidIO interface
+ * @destid: Destination ID of target device
+ * @data: 16-bit info field of RapidIO doorbell
+ *
+ * Sends a RapidIO doorbell message. Always returns %0.
+ */
+static int tsi721_dsend(struct rio_mport *mport, int index,
+ u16 destid, u16 data)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 offset;
+
+ offset = (((mport->sys_size) ? RIO_TT_CODE_16 : RIO_TT_CODE_8) << 18) |
+ (destid << 2);
+
+ dev_dbg(&priv->pdev->dev,
+ "Send Doorbell 0x%04x to destID 0x%x\n", data, destid);
+ iowrite16be(data, priv->odb_base + offset);
+
+ return 0;
+}
+
+/**
+ * tsi721_dbell_handler - Tsi721 doorbell interrupt handler
+ * @mport: RapidIO master port structure
+ *
+ * Handles inbound doorbell interrupts. Copies doorbell entry from an internal
+ * buffer into DB message FIFO and schedules deferred routine to process
+ * queued DBs.
+ */
+static int
+tsi721_dbell_handler(struct rio_mport *mport)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 regval;
+
+ /* Disable IDB interrupts */
+ regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+ regval &= ~TSI721_SR_CHINT_IDBQRCV;
+ iowrite32(regval,
+ priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+
+ schedule_work(&priv->idb_work);
+
+ return 0;
+}
+
+static void tsi721_db_dpc(struct work_struct *work)
+{
+ struct tsi721_device *priv = container_of(work, struct tsi721_device,
+ idb_work);
+ struct rio_mport *mport;
+ struct rio_dbell *dbell;
+ int found = 0;
+ u32 wr_ptr, rd_ptr;
+ u64 *idb_entry;
+ u32 regval;
+ union {
+ u64 msg;
+ u8 bytes[8];
+ } idb;
+
+ /*
+ * Process queued inbound doorbells
+ */
+ mport = priv->mport;
+
+ wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE));
+ rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
+
+ while (wr_ptr != rd_ptr) {
+ idb_entry = (u64 *)(priv->idb_base +
+ (TSI721_IDB_ENTRY_SIZE * rd_ptr));
+ rd_ptr++;
+ idb.msg = *idb_entry;
+ *idb_entry = 0;
+
+ /* Process one doorbell */
+ list_for_each_entry(dbell, &mport->dbells, node) {
+ if ((dbell->res->start <= DBELL_INF(idb.bytes)) &&
+ (dbell->res->end >= DBELL_INF(idb.bytes))) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes),
+ DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
+ } else {
+ dev_dbg(&priv->pdev->dev,
+ "spurious inb doorbell, sid %2.2x tid %2.2x"
+ " info %4.4x\n", DBELL_SID(idb.bytes),
+ DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
+ }
+ }
+
+ iowrite32(rd_ptr & (IDB_QSIZE - 1),
+ priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
+
+ /* Re-enable IDB interrupts */
+ regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+ regval |= TSI721_SR_CHINT_IDBQRCV;
+ iowrite32(regval,
+ priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+}
+
+/**
+ * tsi721_irqhandler - Tsi721 interrupt handler
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles Tsi721 interrupts signaled using MSI and INTA. Checks reported
+ * interrupt events and calls an event-specific handler(s).
+ */
+static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
+{
+ struct rio_mport *mport = (struct rio_mport *)ptr;
+ struct tsi721_device *priv = mport->priv;
+ u32 dev_int;
+ u32 dev_ch_int;
+ u32 intval;
+ u32 ch_inte;
+
+ dev_int = ioread32(priv->regs + TSI721_DEV_INT);
+ if (!dev_int)
+ return IRQ_NONE;
+
+ dev_ch_int = ioread32(priv->regs + TSI721_DEV_CHAN_INT);
+
+ if (dev_int & TSI721_DEV_INT_SR2PC_CH) {
+ /* Service SR2PC Channel interrupts */
+ if (dev_ch_int & TSI721_INT_SR2PC_CHAN(IDB_QUEUE)) {
+ /* Service Inbound Doorbell interrupt */
+ intval = ioread32(priv->regs +
+ TSI721_SR_CHINT(IDB_QUEUE));
+ if (intval & TSI721_SR_CHINT_IDBQRCV)
+ tsi721_dbell_handler(mport);
+ else
+ dev_info(&priv->pdev->dev,
+ "Unsupported SR_CH_INT %x\n", intval);
+
+ /* Clear interrupts */
+ iowrite32(intval,
+ priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+ ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+ }
+ }
+
+ if (dev_int & TSI721_DEV_INT_SMSG_CH) {
+ int ch;
+
+ /*
+ * Service channel interrupts from Messaging Engine
+ */
+
+ if (dev_ch_int & TSI721_INT_IMSG_CHAN_M) { /* Inbound Msg */
+ /* Disable signaled OB MSG Channel interrupts */
+ ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ ch_inte &= ~(dev_ch_int & TSI721_INT_IMSG_CHAN_M);
+ iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
+
+ /*
+ * Process Inbound Message interrupt for each MBOX
+ */
+ for (ch = 4; ch < RIO_MAX_MBOX + 4; ch++) {
+ if (!(dev_ch_int & TSI721_INT_IMSG_CHAN(ch)))
+ continue;
+ tsi721_imsg_handler(priv, ch);
+ }
+ }
+
+ if (dev_ch_int & TSI721_INT_OMSG_CHAN_M) { /* Outbound Msg */
+ /* Disable signaled OB MSG Channel interrupts */
+ ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ ch_inte &= ~(dev_ch_int & TSI721_INT_OMSG_CHAN_M);
+ iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
+
+ /*
+ * Process Outbound Message interrupts for each MBOX
+ */
+
+ for (ch = 0; ch < RIO_MAX_MBOX; ch++) {
+ if (!(dev_ch_int & TSI721_INT_OMSG_CHAN(ch)))
+ continue;
+ tsi721_omsg_handler(priv, ch);
+ }
+ }
+ }
+
+ if (dev_int & TSI721_DEV_INT_SRIO) {
+ /* Service SRIO MAC interrupts */
+ intval = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
+ if (intval & TSI721_RIO_EM_INT_STAT_PW_RX)
+ tsi721_pw_handler(mport);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void tsi721_interrupts_init(struct tsi721_device *priv)
+{
+ u32 intr;
+
+ /* Enable IDB interrupts */
+ iowrite32(TSI721_SR_CHINT_ALL,
+ priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+ iowrite32(TSI721_SR_CHINT_IDBQRCV,
+ priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+ iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE),
+ priv->regs + TSI721_DEV_CHAN_INTE);
+
+ /* Enable SRIO MAC interrupts */
+ iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
+ priv->regs + TSI721_RIO_EM_DEV_INT_EN);
+
+ if (priv->flags & TSI721_USING_MSIX)
+ intr = TSI721_DEV_INT_SRIO;
+ else
+ intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
+ TSI721_DEV_INT_SMSG_CH;
+
+ iowrite32(intr, priv->regs + TSI721_DEV_INTE);
+ ioread32(priv->regs + TSI721_DEV_INTE);
+}
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles outbound messaging interrupts signaled using MSI-X.
+ */
+static irqreturn_t tsi721_omsg_msix(int irq, void *ptr)
+{
+ struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv;
+ int mbox;
+
+ mbox = (irq - priv->msix[TSI721_VECT_OMB0_DONE].vector) % RIO_MAX_MBOX;
+ tsi721_omsg_handler(priv, mbox);
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsi721_imsg_msix - MSI-X interrupt handler for inbound messaging
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles inbound messaging interrupts signaled using MSI-X.
+ */
+static irqreturn_t tsi721_imsg_msix(int irq, void *ptr)
+{
+ struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv;
+ int mbox;
+
+ mbox = (irq - priv->msix[TSI721_VECT_IMB0_RCV].vector) % RIO_MAX_MBOX;
+ tsi721_imsg_handler(priv, mbox + 4);
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsi721_srio_msix - Tsi721 MSI-X SRIO MAC interrupt handler
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles Tsi721 interrupts from SRIO MAC.
+ */
+static irqreturn_t tsi721_srio_msix(int irq, void *ptr)
+{
+ struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv;
+ u32 srio_int;
+
+ /* Service SRIO MAC interrupts */
+ srio_int = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
+ if (srio_int & TSI721_RIO_EM_INT_STAT_PW_RX)
+ tsi721_pw_handler((struct rio_mport *)ptr);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsi721_sr2pc_ch_msix - Tsi721 MSI-X SR2PC Channel interrupt handler
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles Tsi721 interrupts from SR2PC Channel.
+ * NOTE: At this moment services only one SR2PC channel associated with inbound
+ * doorbells.
+ */
+static irqreturn_t tsi721_sr2pc_ch_msix(int irq, void *ptr)
+{
+ struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv;
+ u32 sr_ch_int;
+
+ /* Service Inbound DB interrupt from SR2PC channel */
+ sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+ if (sr_ch_int & TSI721_SR_CHINT_IDBQRCV)
+ tsi721_dbell_handler((struct rio_mport *)ptr);
+
+ /* Clear interrupts */
+ iowrite32(sr_ch_int, priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+ /* Read back to ensure that interrupt was cleared */
+ sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsi721_request_msix - register interrupt service for MSI-X mode.
+ * @mport: RapidIO master port structure
+ *
+ * Registers MSI-X interrupt service routines for interrupts that are active
+ * immediately after mport initialization. Messaging interrupt service routines
+ * should be registered during corresponding open requests.
+ */
+static int tsi721_request_msix(struct rio_mport *mport)
+{
+ struct tsi721_device *priv = mport->priv;
+ int err = 0;
+
+ err = request_irq(priv->msix[TSI721_VECT_IDB].vector,
+ tsi721_sr2pc_ch_msix, 0,
+ priv->msix[TSI721_VECT_IDB].irq_name, (void *)mport);
+ if (err)
+ goto out;
+
+ err = request_irq(priv->msix[TSI721_VECT_PWRX].vector,
+ tsi721_srio_msix, 0,
+ priv->msix[TSI721_VECT_PWRX].irq_name, (void *)mport);
+ if (err)
+ free_irq(
+ priv->msix[TSI721_VECT_IDB].vector,
+ (void *)mport);
+out:
+ return err;
+}
+
+/**
+ * tsi721_enable_msix - Attempts to enable MSI-X support for Tsi721.
+ * @priv: pointer to tsi721 private data
+ *
+ * Configures MSI-X support for Tsi721. Supports only an exact number
+ * of requested vectors.
+ */
+static int tsi721_enable_msix(struct tsi721_device *priv)
+{
+ struct msix_entry entries[TSI721_VECT_MAX];
+ int err;
+ int i;
+
+ entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE);
+ entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT;
+
+ /*
+ * Initialize MSI-X entries for Messaging Engine:
+ * this driver supports four RIO mailboxes (inbound and outbound)
+ * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore
+ * offset +4 is added to IB MBOX number.
+ */
+ for (i = 0; i < RIO_MAX_MBOX; i++) {
+ entries[TSI721_VECT_IMB0_RCV + i].entry =
+ TSI721_MSIX_IMSG_DQ_RCV(i + 4);
+ entries[TSI721_VECT_IMB0_INT + i].entry =
+ TSI721_MSIX_IMSG_INT(i + 4);
+ entries[TSI721_VECT_OMB0_DONE + i].entry =
+ TSI721_MSIX_OMSG_DONE(i);
+ entries[TSI721_VECT_OMB0_INT + i].entry =
+ TSI721_MSIX_OMSG_INT(i);
+ }
+
+ err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries));
+ if (err) {
+ if (err > 0)
+ dev_info(&priv->pdev->dev,
+ "Only %d MSI-X vectors available, "
+ "not using MSI-X\n", err);
+ return err;
+ }
+
+ /*
+ * Copy MSI-X vector information into tsi721 private structure
+ */
+ priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector;
+ snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX,
+ DRV_NAME "-idb@pci:%s", pci_name(priv->pdev));
+ priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector;
+ snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX,
+ DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev));
+
+ for (i = 0; i < RIO_MAX_MBOX; i++) {
+ priv->msix[TSI721_VECT_IMB0_RCV + i].vector =
+ entries[TSI721_VECT_IMB0_RCV + i].vector;
+ snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s",
+ i, pci_name(priv->pdev));
+
+ priv->msix[TSI721_VECT_IMB0_INT + i].vector =
+ entries[TSI721_VECT_IMB0_INT + i].vector;
+ snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s",
+ i, pci_name(priv->pdev));
+
+ priv->msix[TSI721_VECT_OMB0_DONE + i].vector =
+ entries[TSI721_VECT_OMB0_DONE + i].vector;
+ snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s",
+ i, pci_name(priv->pdev));
+
+ priv->msix[TSI721_VECT_OMB0_INT + i].vector =
+ entries[TSI721_VECT_OMB0_INT + i].vector;
+ snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s",
+ i, pci_name(priv->pdev));
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PCI_MSI */
+
+static int tsi721_request_irq(struct rio_mport *mport)
+{
+ struct tsi721_device *priv = mport->priv;
+ int err;
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX)
+ err = tsi721_request_msix(mport);
+ else
+#endif
+ err = request_irq(priv->pdev->irq, tsi721_irqhandler,
+ (priv->flags & TSI721_USING_MSI) ? 0 : IRQF_SHARED,
+ DRV_NAME, (void *)mport);
+
+ if (err)
+ dev_err(&priv->pdev->dev,
+ "Unable to allocate interrupt, Error: %d\n", err);
+
+ return err;
+}
+
+/**
+ * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
+ * translation regions.
+ * @priv: pointer to tsi721 private data
+ *
+ * Disables SREP translation regions.
+ */
+static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
+{
+ int i;
+
+ /* Disable all PC2SR translation windows */
+ for (i = 0; i < TSI721_OBWIN_NUM; i++)
+ iowrite32(0, priv->regs + TSI721_OBWINLB(i));
+}
+
+/**
+ * tsi721_init_sr2pc_mapping - initializes inbound (SRIO->PCIe)
+ * translation regions.
+ * @priv: pointer to tsi721 private data
+ *
+ * Disables inbound windows.
+ */
+static void tsi721_init_sr2pc_mapping(struct tsi721_device *priv)
+{
+ int i;
+
+ /* Disable all SR2PC inbound windows */
+ for (i = 0; i < TSI721_IBWIN_NUM; i++)
+ iowrite32(0, priv->regs + TSI721_IBWINLB(i));
+}
+
+/**
+ * tsi721_port_write_init - Inbound port write interface init
+ * @priv: pointer to tsi721 private data
+ *
+ * Initializes inbound port write handler.
+ * Returns %0 on success or %-ENOMEM on failure.
+ */
+static int tsi721_port_write_init(struct tsi721_device *priv)
+{
+ priv->pw_discard_count = 0;
+ INIT_WORK(&priv->pw_work, tsi721_pw_dpc);
+ spin_lock_init(&priv->pw_fifo_lock);
+ if (kfifo_alloc(&priv->pw_fifo,
+ TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
+ dev_err(&priv->pdev->dev, "PW FIFO allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Use reliable port-write capture mode */
+ iowrite32(TSI721_RIO_PW_CTL_PWC_REL, priv->regs + TSI721_RIO_PW_CTL);
+ return 0;
+}
+
+static int tsi721_doorbell_init(struct tsi721_device *priv)
+{
+ /* Outbound Doorbells do not require any setup.
+ * Tsi721 uses dedicated PCI BAR1 to generate doorbells.
+ * That BAR1 was mapped during the probe routine.
+ */
+
+ /* Initialize Inbound Doorbell processing DPC and queue */
+ priv->db_discard_count = 0;
+ INIT_WORK(&priv->idb_work, tsi721_db_dpc);
+
+ /* Allocate buffer for inbound doorbells queue */
+ priv->idb_base = dma_alloc_coherent(&priv->pdev->dev,
+ IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
+ &priv->idb_dma, GFP_KERNEL);
+ if (!priv->idb_base)
+ return -ENOMEM;
+
+ memset(priv->idb_base, 0, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE);
+
+ dev_dbg(&priv->pdev->dev, "Allocated IDB buffer @ %p (phys = %llx)\n",
+ priv->idb_base, (unsigned long long)priv->idb_dma);
+
+ iowrite32(TSI721_IDQ_SIZE_VAL(IDB_QSIZE),
+ priv->regs + TSI721_IDQ_SIZE(IDB_QUEUE));
+ iowrite32(((u64)priv->idb_dma >> 32),
+ priv->regs + TSI721_IDQ_BASEU(IDB_QUEUE));
+ iowrite32(((u64)priv->idb_dma & TSI721_IDQ_BASEL_ADDR),
+ priv->regs + TSI721_IDQ_BASEL(IDB_QUEUE));
+ /* Enable accepting all inbound doorbells */
+ iowrite32(0, priv->regs + TSI721_IDQ_MASK(IDB_QUEUE));
+
+ iowrite32(TSI721_IDQ_INIT, priv->regs + TSI721_IDQ_CTL(IDB_QUEUE));
+
+ iowrite32(0, priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
+
+ return 0;
+}
+
+static void tsi721_doorbell_free(struct tsi721_device *priv)
+{
+ if (priv->idb_base == NULL)
+ return;
+
+ /* Free buffer allocated for inbound doorbell queue */
+ dma_free_coherent(&priv->pdev->dev, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
+ priv->idb_base, priv->idb_dma);
+ priv->idb_base = NULL;
+}
+
+static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
+{
+ struct tsi721_dma_desc *bd_ptr;
+ u64 *sts_ptr;
+ dma_addr_t bd_phys, sts_phys;
+ int sts_size;
+ int bd_num = priv->bdma[chnum].bd_num;
+
+ dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum);
+
+ /*
+ * Initialize DMA channel for maintenance requests
+ */
+
+ /* Allocate space for DMA descriptors */
+ bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
+ bd_num * sizeof(struct tsi721_dma_desc),
+ &bd_phys, GFP_KERNEL);
+ if (!bd_ptr)
+ return -ENOMEM;
+
+ priv->bdma[chnum].bd_phys = bd_phys;
+ priv->bdma[chnum].bd_base = bd_ptr;
+
+ memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
+
+ dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n",
+ bd_ptr, (unsigned long long)bd_phys);
+
+ /* Allocate space for descriptor status FIFO */
+ sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
+ bd_num : TSI721_DMA_MINSTSSZ;
+ sts_size = roundup_pow_of_two(sts_size);
+ sts_ptr = dma_alloc_coherent(&priv->pdev->dev,
+ sts_size * sizeof(struct tsi721_dma_sts),
+ &sts_phys, GFP_KERNEL);
+ if (!sts_ptr) {
+ /* Free space allocated for DMA descriptors */
+ dma_free_coherent(&priv->pdev->dev,
+ bd_num * sizeof(struct tsi721_dma_desc),
+ bd_ptr, bd_phys);
+ priv->bdma[chnum].bd_base = NULL;
+ return -ENOMEM;
+ }
+
+ priv->bdma[chnum].sts_phys = sts_phys;
+ priv->bdma[chnum].sts_base = sts_ptr;
+ priv->bdma[chnum].sts_size = sts_size;
+
+ memset(sts_ptr, 0, sts_size);
+
+ dev_dbg(&priv->pdev->dev,
+ "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
+ sts_ptr, (unsigned long long)sts_phys, sts_size);
+
+ /* Initialize DMA descriptors ring */
+ bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
+ bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
+ TSI721_DMAC_DPTRL_MASK);
+ bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
+
+ /* Setup DMA descriptor pointers */
+ iowrite32(((u64)bd_phys >> 32),
+ priv->regs + TSI721_DMAC_DPTRH(chnum));
+ iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
+ priv->regs + TSI721_DMAC_DPTRL(chnum));
+
+ /* Setup descriptor status FIFO */
+ iowrite32(((u64)sts_phys >> 32),
+ priv->regs + TSI721_DMAC_DSBH(chnum));
+ iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
+ priv->regs + TSI721_DMAC_DSBL(chnum));
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
+ priv->regs + TSI721_DMAC_DSSZ(chnum));
+
+ /* Clear interrupt bits */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ priv->regs + TSI721_DMAC_INT(chnum));
+
+ ioread32(priv->regs + TSI721_DMAC_INT(chnum));
+
+ /* Toggle DMA channel initialization */
+ iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum));
+ ioread32(priv->regs + TSI721_DMAC_CTL(chnum));
+ udelay(10);
+
+ return 0;
+}
+
+static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum)
+{
+ u32 ch_stat;
+
+ if (priv->bdma[chnum].bd_base == NULL)
+ return 0;
+
+ /* Check if DMA channel still running */
+ ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum));
+ if (ch_stat & TSI721_DMAC_STS_RUN)
+ return -EFAULT;
+
+ /* Put DMA channel into init state */
+ iowrite32(TSI721_DMAC_CTL_INIT,
+ priv->regs + TSI721_DMAC_CTL(chnum));
+
+ /* Free space allocated for DMA descriptors */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc),
+ priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys);
+ priv->bdma[chnum].bd_base = NULL;
+
+ /* Free space allocated for status FIFO */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts),
+ priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys);
+ priv->bdma[chnum].sts_base = NULL;
+ return 0;
+}
+
+static int tsi721_bdma_init(struct tsi721_device *priv)
+{
+ /* Initialize BDMA channel allocated for RapidIO maintenance read/write
+ * request generation
+ */
+ priv->bdma[TSI721_DMACH_MAINT].bd_num = 2;
+ if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) {
+ dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA"
+ " channel %d, aborting\n", TSI721_DMACH_MAINT);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void tsi721_bdma_free(struct tsi721_device *priv)
+{
+ tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT);
+}
+
+/* Enable Inbound Messaging Interrupts */
+static void
+tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
+ u32 inte_mask)
+{
+ u32 rval;
+
+ if (!inte_mask)
+ return;
+
+ /* Clear pending Inbound Messaging interrupts */
+ iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
+
+ /* Enable Inbound Messaging interrupts */
+ rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
+ iowrite32(rval | inte_mask, priv->regs + TSI721_IBDMAC_INTE(ch));
+
+ if (priv->flags & TSI721_USING_MSIX)
+ return; /* Finished if we are in MSI-X mode */
+
+ /*
+ * For MSI and INTA interrupt signalling we need to enable next levels
+ */
+
+ /* Enable Device Channel Interrupt */
+ rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ iowrite32(rval | TSI721_INT_IMSG_CHAN(ch),
+ priv->regs + TSI721_DEV_CHAN_INTE);
+}
+
+/* Disable Inbound Messaging Interrupts */
+static void
+tsi721_imsg_interrupt_disable(struct tsi721_device *priv, int ch,
+ u32 inte_mask)
+{
+ u32 rval;
+
+ if (!inte_mask)
+ return;
+
+ /* Clear pending Inbound Messaging interrupts */
+ iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
+
+ /* Disable Inbound Messaging interrupts */
+ rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
+ rval &= ~inte_mask;
+ iowrite32(rval, priv->regs + TSI721_IBDMAC_INTE(ch));
+
+ if (priv->flags & TSI721_USING_MSIX)
+ return; /* Finished if we are in MSI-X mode */
+
+ /*
+ * For MSI and INTA interrupt signalling we need to disable next levels
+ */
+
+ /* Disable Device Channel Interrupt */
+ rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ rval &= ~TSI721_INT_IMSG_CHAN(ch);
+ iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
+}
+
+/* Enable Outbound Messaging interrupts */
+static void
+tsi721_omsg_interrupt_enable(struct tsi721_device *priv, int ch,
+ u32 inte_mask)
+{
+ u32 rval;
+
+ if (!inte_mask)
+ return;
+
+ /* Clear pending Outbound Messaging interrupts */
+ iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
+
+ /* Enable Outbound Messaging channel interrupts */
+ rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
+ iowrite32(rval | inte_mask, priv->regs + TSI721_OBDMAC_INTE(ch));
+
+ if (priv->flags & TSI721_USING_MSIX)
+ return; /* Finished if we are in MSI-X mode */
+
+ /*
+ * For MSI and INTA interrupt signalling we need to enable next levels
+ */
+
+ /* Enable Device Channel Interrupt */
+ rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ iowrite32(rval | TSI721_INT_OMSG_CHAN(ch),
+ priv->regs + TSI721_DEV_CHAN_INTE);
+}
+
+/* Disable Outbound Messaging interrupts */
+static void
+tsi721_omsg_interrupt_disable(struct tsi721_device *priv, int ch,
+ u32 inte_mask)
+{
+ u32 rval;
+
+ if (!inte_mask)
+ return;
+
+ /* Clear pending Outbound Messaging interrupts */
+ iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
+
+ /* Disable Outbound Messaging interrupts */
+ rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
+ rval &= ~inte_mask;
+ iowrite32(rval, priv->regs + TSI721_OBDMAC_INTE(ch));
+
+ if (priv->flags & TSI721_USING_MSIX)
+ return; /* Finished if we are in MSI-X mode */
+
+ /*
+ * For MSI and INTA interrupt signalling we need to disable next levels
+ */
+
+ /* Disable Device Channel Interrupt */
+ rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ rval &= ~TSI721_INT_OMSG_CHAN(ch);
+ iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
+}
+
+/**
+ * tsi721_add_outb_message - Add message to the Tsi721 outbound message queue
+ * @mport: Master port with outbound message queue
+ * @rdev: Target of outbound message
+ * @mbox: Outbound mailbox
+ * @buffer: Message to add to outbound queue
+ * @len: Length of message
+ */
+static int
+tsi721_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
+ void *buffer, size_t len)
+{
+ struct tsi721_device *priv = mport->priv;
+ struct tsi721_omsg_desc *desc;
+ u32 tx_slot;
+
+ if (!priv->omsg_init[mbox] ||
+ len > TSI721_MSG_MAX_SIZE || len < 8)
+ return -EINVAL;
+
+ tx_slot = priv->omsg_ring[mbox].tx_slot;
+
+ /* Copy copy message into transfer buffer */
+ memcpy(priv->omsg_ring[mbox].omq_base[tx_slot], buffer, len);
+
+ if (len & 0x7)
+ len += 8;
+
+ /* Build descriptor associated with buffer */
+ desc = priv->omsg_ring[mbox].omd_base;
+ desc[tx_slot].type_id = cpu_to_le32((DTYPE4 << 29) | rdev->destid);
+ if (tx_slot % 4 == 0)
+ desc[tx_slot].type_id |= cpu_to_le32(TSI721_OMD_IOF);
+
+ desc[tx_slot].msg_info =
+ cpu_to_le32((mport->sys_size << 26) | (mbox << 22) |
+ (0xe << 12) | (len & 0xff8));
+ desc[tx_slot].bufptr_lo =
+ cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] &
+ 0xffffffff);
+ desc[tx_slot].bufptr_hi =
+ cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] >> 32);
+
+ priv->omsg_ring[mbox].wr_count++;
+
+ /* Go to next descriptor */
+ if (++priv->omsg_ring[mbox].tx_slot == priv->omsg_ring[mbox].size) {
+ priv->omsg_ring[mbox].tx_slot = 0;
+ /* Move through the ring link descriptor at the end */
+ priv->omsg_ring[mbox].wr_count++;
+ }
+
+ mb();
+
+ /* Set new write count value */
+ iowrite32(priv->omsg_ring[mbox].wr_count,
+ priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
+ ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
+
+ return 0;
+}
+
+/**
+ * tsi721_omsg_handler - Outbound Message Interrupt Handler
+ * @priv: pointer to tsi721 private data
+ * @ch: number of OB MSG channel to service
+ *
+ * Services channel interrupts from outbound messaging engine.
+ */
+static void tsi721_omsg_handler(struct tsi721_device *priv, int ch)
+{
+ u32 omsg_int;
+
+ spin_lock(&priv->omsg_ring[ch].lock);
+
+ omsg_int = ioread32(priv->regs + TSI721_OBDMAC_INT(ch));
+
+ if (omsg_int & TSI721_OBDMAC_INT_ST_FULL)
+ dev_info(&priv->pdev->dev,
+ "OB MBOX%d: Status FIFO is full\n", ch);
+
+ if (omsg_int & (TSI721_OBDMAC_INT_DONE | TSI721_OBDMAC_INT_IOF_DONE)) {
+ u32 srd_ptr;
+ u64 *sts_ptr, last_ptr = 0, prev_ptr = 0;
+ int i, j;
+ u32 tx_slot;
+
+ /*
+ * Find last successfully processed descriptor
+ */
+
+ /* Check and clear descriptor status FIFO entries */
+ srd_ptr = priv->omsg_ring[ch].sts_rdptr;
+ sts_ptr = priv->omsg_ring[ch].sts_base;
+ j = srd_ptr * 8;
+ while (sts_ptr[j]) {
+ for (i = 0; i < 8 && sts_ptr[j]; i++, j++) {
+ prev_ptr = last_ptr;
+ last_ptr = le64_to_cpu(sts_ptr[j]);
+ sts_ptr[j] = 0;
+ }
+
+ ++srd_ptr;
+ srd_ptr %= priv->omsg_ring[ch].sts_size;
+ j = srd_ptr * 8;
+ }
+
+ if (last_ptr == 0)
+ goto no_sts_update;
+
+ priv->omsg_ring[ch].sts_rdptr = srd_ptr;
+ iowrite32(srd_ptr, priv->regs + TSI721_OBDMAC_DSRP(ch));
+
+ if (!priv->mport->outb_msg[ch].mcback)
+ goto no_sts_update;
+
+ /* Inform upper layer about transfer completion */
+
+ tx_slot = (last_ptr - (u64)priv->omsg_ring[ch].omd_phys)/
+ sizeof(struct tsi721_omsg_desc);
+
+ /*
+ * Check if this is a Link Descriptor (LD).
+ * If yes, ignore LD and use descriptor processed
+ * before LD.
+ */
+ if (tx_slot == priv->omsg_ring[ch].size) {
+ if (prev_ptr)
+ tx_slot = (prev_ptr -
+ (u64)priv->omsg_ring[ch].omd_phys)/
+ sizeof(struct tsi721_omsg_desc);
+ else
+ goto no_sts_update;
+ }
+
+ /* Move slot index to the next message to be sent */
+ ++tx_slot;
+ if (tx_slot == priv->omsg_ring[ch].size)
+ tx_slot = 0;
+ BUG_ON(tx_slot >= priv->omsg_ring[ch].size);
+ priv->mport->outb_msg[ch].mcback(priv->mport,
+ priv->omsg_ring[ch].dev_id, ch,
+ tx_slot);
+ }
+
+no_sts_update:
+
+ if (omsg_int & TSI721_OBDMAC_INT_ERROR) {
+ /*
+ * Outbound message operation aborted due to error,
+ * reinitialize OB MSG channel
+ */
+
+ dev_dbg(&priv->pdev->dev, "OB MSG ABORT ch_stat=%x\n",
+ ioread32(priv->regs + TSI721_OBDMAC_STS(ch)));
+
+ iowrite32(TSI721_OBDMAC_INT_ERROR,
+ priv->regs + TSI721_OBDMAC_INT(ch));
+ iowrite32(TSI721_OBDMAC_CTL_INIT,
+ priv->regs + TSI721_OBDMAC_CTL(ch));
+ ioread32(priv->regs + TSI721_OBDMAC_CTL(ch));
+
+ /* Inform upper level to clear all pending tx slots */
+ if (priv->mport->outb_msg[ch].mcback)
+ priv->mport->outb_msg[ch].mcback(priv->mport,
+ priv->omsg_ring[ch].dev_id, ch,
+ priv->omsg_ring[ch].tx_slot);
+ /* Synch tx_slot tracking */
+ iowrite32(priv->omsg_ring[ch].tx_slot,
+ priv->regs + TSI721_OBDMAC_DRDCNT(ch));
+ ioread32(priv->regs + TSI721_OBDMAC_DRDCNT(ch));
+ priv->omsg_ring[ch].wr_count = priv->omsg_ring[ch].tx_slot;
+ priv->omsg_ring[ch].sts_rdptr = 0;
+ }
+
+ /* Clear channel interrupts */
+ iowrite32(omsg_int, priv->regs + TSI721_OBDMAC_INT(ch));
+
+ if (!(priv->flags & TSI721_USING_MSIX)) {
+ u32 ch_inte;
+
+ /* Re-enable channel interrupts */
+ ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ ch_inte |= TSI721_INT_OMSG_CHAN(ch);
+ iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
+ }
+
+ spin_unlock(&priv->omsg_ring[ch].lock);
+}
+
+/**
+ * tsi721_open_outb_mbox - Initialize Tsi721 outbound mailbox
+ * @mport: Master port implementing Outbound Messaging Engine
+ * @dev_id: Device specific pointer to pass on event
+ * @mbox: Mailbox to open
+ * @entries: Number of entries in the outbound mailbox ring
+ */
+static int tsi721_open_outb_mbox(struct rio_mport *mport, void *dev_id,
+ int mbox, int entries)
+{
+ struct tsi721_device *priv = mport->priv;
+ struct tsi721_omsg_desc *bd_ptr;
+ int i, rc = 0;
+
+ if ((entries < TSI721_OMSGD_MIN_RING_SIZE) ||
+ (entries > (TSI721_OMSGD_RING_SIZE)) ||
+ (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ priv->omsg_ring[mbox].dev_id = dev_id;
+ priv->omsg_ring[mbox].size = entries;
+ priv->omsg_ring[mbox].sts_rdptr = 0;
+ spin_lock_init(&priv->omsg_ring[mbox].lock);
+
+ /* Outbound Msg Buffer allocation based on
+ the number of maximum descriptor entries */
+ for (i = 0; i < entries; i++) {
+ priv->omsg_ring[mbox].omq_base[i] =
+ dma_alloc_coherent(
+ &priv->pdev->dev, TSI721_MSG_BUFFER_SIZE,
+ &priv->omsg_ring[mbox].omq_phys[i],
+ GFP_KERNEL);
+ if (priv->omsg_ring[mbox].omq_base[i] == NULL) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate OB MSG data buffer for"
+ " MBOX%d\n", mbox);
+ rc = -ENOMEM;
+ goto out_buf;
+ }
+ }
+
+ /* Outbound message descriptor allocation */
+ priv->omsg_ring[mbox].omd_base = dma_alloc_coherent(
+ &priv->pdev->dev,
+ (entries + 1) * sizeof(struct tsi721_omsg_desc),
+ &priv->omsg_ring[mbox].omd_phys, GFP_KERNEL);
+ if (priv->omsg_ring[mbox].omd_base == NULL) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate OB MSG descriptor memory "
+ "for MBOX%d\n", mbox);
+ rc = -ENOMEM;
+ goto out_buf;
+ }
+
+ priv->omsg_ring[mbox].tx_slot = 0;
+
+ /* Outbound message descriptor status FIFO allocation */
+ priv->omsg_ring[mbox].sts_size = roundup_pow_of_two(entries + 1);
+ priv->omsg_ring[mbox].sts_base = dma_alloc_coherent(&priv->pdev->dev,
+ priv->omsg_ring[mbox].sts_size *
+ sizeof(struct tsi721_dma_sts),
+ &priv->omsg_ring[mbox].sts_phys, GFP_KERNEL);
+ if (priv->omsg_ring[mbox].sts_base == NULL) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate OB MSG descriptor status FIFO "
+ "for MBOX%d\n", mbox);
+ rc = -ENOMEM;
+ goto out_desc;
+ }
+
+ memset(priv->omsg_ring[mbox].sts_base, 0,
+ entries * sizeof(struct tsi721_dma_sts));
+
+ /*
+ * Configure Outbound Messaging Engine
+ */
+
+ /* Setup Outbound Message descriptor pointer */
+ iowrite32(((u64)priv->omsg_ring[mbox].omd_phys >> 32),
+ priv->regs + TSI721_OBDMAC_DPTRH(mbox));
+ iowrite32(((u64)priv->omsg_ring[mbox].omd_phys &
+ TSI721_OBDMAC_DPTRL_MASK),
+ priv->regs + TSI721_OBDMAC_DPTRL(mbox));
+
+ /* Setup Outbound Message descriptor status FIFO */
+ iowrite32(((u64)priv->omsg_ring[mbox].sts_phys >> 32),
+ priv->regs + TSI721_OBDMAC_DSBH(mbox));
+ iowrite32(((u64)priv->omsg_ring[mbox].sts_phys &
+ TSI721_OBDMAC_DSBL_MASK),
+ priv->regs + TSI721_OBDMAC_DSBL(mbox));
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(priv->omsg_ring[mbox].sts_size),
+ priv->regs + (u32)TSI721_OBDMAC_DSSZ(mbox));
+
+ /* Enable interrupts */
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ /* Request interrupt service if we are in MSI-X mode */
+ rc = request_irq(
+ priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
+ tsi721_omsg_msix, 0,
+ priv->msix[TSI721_VECT_OMB0_DONE + mbox].irq_name,
+ (void *)mport);
+
+ if (rc) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "OBOX%d-DONE\n", mbox);
+ goto out_stat;
+ }
+
+ rc = request_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector,
+ tsi721_omsg_msix, 0,
+ priv->msix[TSI721_VECT_OMB0_INT + mbox].irq_name,
+ (void *)mport);
+
+ if (rc) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "MBOX%d-INT\n", mbox);
+ free_irq(
+ priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
+ (void *)mport);
+ goto out_stat;
+ }
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tsi721_omsg_interrupt_enable(priv, mbox, TSI721_OBDMAC_INT_ALL);
+
+ /* Initialize Outbound Message descriptors ring */
+ bd_ptr = priv->omsg_ring[mbox].omd_base;
+ bd_ptr[entries].type_id = cpu_to_le32(DTYPE5 << 29);
+ bd_ptr[entries].msg_info = 0;
+ bd_ptr[entries].next_lo =
+ cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys &
+ TSI721_OBDMAC_DPTRL_MASK);
+ bd_ptr[entries].next_hi =
+ cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys >> 32);
+ priv->omsg_ring[mbox].wr_count = 0;
+ mb();
+
+ /* Initialize Outbound Message engine */
+ iowrite32(TSI721_OBDMAC_CTL_INIT, priv->regs + TSI721_OBDMAC_CTL(mbox));
+ ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
+ udelay(10);
+
+ priv->omsg_init[mbox] = 1;
+
+ return 0;
+
+#ifdef CONFIG_PCI_MSI
+out_stat:
+ dma_free_coherent(&priv->pdev->dev,
+ priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
+ priv->omsg_ring[mbox].sts_base,
+ priv->omsg_ring[mbox].sts_phys);
+
+ priv->omsg_ring[mbox].sts_base = NULL;
+#endif /* CONFIG_PCI_MSI */
+
+out_desc:
+ dma_free_coherent(&priv->pdev->dev,
+ (entries + 1) * sizeof(struct tsi721_omsg_desc),
+ priv->omsg_ring[mbox].omd_base,
+ priv->omsg_ring[mbox].omd_phys);
+
+ priv->omsg_ring[mbox].omd_base = NULL;
+
+out_buf:
+ for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
+ if (priv->omsg_ring[mbox].omq_base[i]) {
+ dma_free_coherent(&priv->pdev->dev,
+ TSI721_MSG_BUFFER_SIZE,
+ priv->omsg_ring[mbox].omq_base[i],
+ priv->omsg_ring[mbox].omq_phys[i]);
+
+ priv->omsg_ring[mbox].omq_base[i] = NULL;
+ }
+ }
+
+out:
+ return rc;
+}
+
+/**
+ * tsi721_close_outb_mbox - Close Tsi721 outbound mailbox
+ * @mport: Master port implementing the outbound message unit
+ * @mbox: Mailbox to close
+ */
+static void tsi721_close_outb_mbox(struct rio_mport *mport, int mbox)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 i;
+
+ if (!priv->omsg_init[mbox])
+ return;
+ priv->omsg_init[mbox] = 0;
+
+ /* Disable Interrupts */
+
+ tsi721_omsg_interrupt_disable(priv, mbox, TSI721_OBDMAC_INT_ALL);
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ free_irq(priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
+ (void *)mport);
+ free_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector,
+ (void *)mport);
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ /* Free OMSG Descriptor Status FIFO */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
+ priv->omsg_ring[mbox].sts_base,
+ priv->omsg_ring[mbox].sts_phys);
+
+ priv->omsg_ring[mbox].sts_base = NULL;
+
+ /* Free OMSG descriptors */
+ dma_free_coherent(&priv->pdev->dev,
+ (priv->omsg_ring[mbox].size + 1) *
+ sizeof(struct tsi721_omsg_desc),
+ priv->omsg_ring[mbox].omd_base,
+ priv->omsg_ring[mbox].omd_phys);
+
+ priv->omsg_ring[mbox].omd_base = NULL;
+
+ /* Free message buffers */
+ for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
+ if (priv->omsg_ring[mbox].omq_base[i]) {
+ dma_free_coherent(&priv->pdev->dev,
+ TSI721_MSG_BUFFER_SIZE,
+ priv->omsg_ring[mbox].omq_base[i],
+ priv->omsg_ring[mbox].omq_phys[i]);
+
+ priv->omsg_ring[mbox].omq_base[i] = NULL;
+ }
+ }
+}
+
+/**
+ * tsi721_imsg_handler - Inbound Message Interrupt Handler
+ * @priv: pointer to tsi721 private data
+ * @ch: inbound message channel number to service
+ *
+ * Services channel interrupts from inbound messaging engine.
+ */
+static void tsi721_imsg_handler(struct tsi721_device *priv, int ch)
+{
+ u32 mbox = ch - 4;
+ u32 imsg_int;
+
+ spin_lock(&priv->imsg_ring[mbox].lock);
+
+ imsg_int = ioread32(priv->regs + TSI721_IBDMAC_INT(ch));
+
+ if (imsg_int & TSI721_IBDMAC_INT_SRTO)
+ dev_info(&priv->pdev->dev, "IB MBOX%d SRIO timeout\n",
+ mbox);
+
+ if (imsg_int & TSI721_IBDMAC_INT_PC_ERROR)
+ dev_info(&priv->pdev->dev, "IB MBOX%d PCIe error\n",
+ mbox);
+
+ if (imsg_int & TSI721_IBDMAC_INT_FQ_LOW)
+ dev_info(&priv->pdev->dev,
+ "IB MBOX%d IB free queue low\n", mbox);
+
+ /* Clear IB channel interrupts */
+ iowrite32(imsg_int, priv->regs + TSI721_IBDMAC_INT(ch));
+
+ /* If an IB Msg is received notify the upper layer */
+ if (imsg_int & TSI721_IBDMAC_INT_DQ_RCV &&
+ priv->mport->inb_msg[mbox].mcback)
+ priv->mport->inb_msg[mbox].mcback(priv->mport,
+ priv->imsg_ring[mbox].dev_id, mbox, -1);
+
+ if (!(priv->flags & TSI721_USING_MSIX)) {
+ u32 ch_inte;
+
+ /* Re-enable channel interrupts */
+ ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
+ ch_inte |= TSI721_INT_IMSG_CHAN(ch);
+ iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
+ }
+
+ spin_unlock(&priv->imsg_ring[mbox].lock);
+}
+
+/**
+ * tsi721_open_inb_mbox - Initialize Tsi721 inbound mailbox
+ * @mport: Master port implementing the Inbound Messaging Engine
+ * @dev_id: Device specific pointer to pass on event
+ * @mbox: Mailbox to open
+ * @entries: Number of entries in the inbound mailbox ring
+ */
+static int tsi721_open_inb_mbox(struct rio_mport *mport, void *dev_id,
+ int mbox, int entries)
+{
+ struct tsi721_device *priv = mport->priv;
+ int ch = mbox + 4;
+ int i;
+ u64 *free_ptr;
+ int rc = 0;
+
+ if ((entries < TSI721_IMSGD_MIN_RING_SIZE) ||
+ (entries > TSI721_IMSGD_RING_SIZE) ||
+ (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Initialize IB Messaging Ring */
+ priv->imsg_ring[mbox].dev_id = dev_id;
+ priv->imsg_ring[mbox].size = entries;
+ priv->imsg_ring[mbox].rx_slot = 0;
+ priv->imsg_ring[mbox].desc_rdptr = 0;
+ priv->imsg_ring[mbox].fq_wrptr = 0;
+ for (i = 0; i < priv->imsg_ring[mbox].size; i++)
+ priv->imsg_ring[mbox].imq_base[i] = NULL;
+ spin_lock_init(&priv->imsg_ring[mbox].lock);
+
+ /* Allocate buffers for incoming messages */
+ priv->imsg_ring[mbox].buf_base =
+ dma_alloc_coherent(&priv->pdev->dev,
+ entries * TSI721_MSG_BUFFER_SIZE,
+ &priv->imsg_ring[mbox].buf_phys,
+ GFP_KERNEL);
+
+ if (priv->imsg_ring[mbox].buf_base == NULL) {
+ dev_err(&priv->pdev->dev,
+ "Failed to allocate buffers for IB MBOX%d\n", mbox);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Allocate memory for circular free list */
+ priv->imsg_ring[mbox].imfq_base =
+ dma_alloc_coherent(&priv->pdev->dev,
+ entries * 8,
+ &priv->imsg_ring[mbox].imfq_phys,
+ GFP_KERNEL);
+
+ if (priv->imsg_ring[mbox].imfq_base == NULL) {
+ dev_err(&priv->pdev->dev,
+ "Failed to allocate free queue for IB MBOX%d\n", mbox);
+ rc = -ENOMEM;
+ goto out_buf;
+ }
+
+ /* Allocate memory for Inbound message descriptors */
+ priv->imsg_ring[mbox].imd_base =
+ dma_alloc_coherent(&priv->pdev->dev,
+ entries * sizeof(struct tsi721_imsg_desc),
+ &priv->imsg_ring[mbox].imd_phys, GFP_KERNEL);
+
+ if (priv->imsg_ring[mbox].imd_base == NULL) {
+ dev_err(&priv->pdev->dev,
+ "Failed to allocate descriptor memory for IB MBOX%d\n",
+ mbox);
+ rc = -ENOMEM;
+ goto out_dma;
+ }
+
+ /* Fill free buffer pointer list */
+ free_ptr = priv->imsg_ring[mbox].imfq_base;
+ for (i = 0; i < entries; i++)
+ free_ptr[i] = cpu_to_le64(
+ (u64)(priv->imsg_ring[mbox].buf_phys) +
+ i * 0x1000);
+
+ mb();
+
+ /*
+ * For mapping of inbound SRIO Messages into appropriate queues we need
+ * to set Inbound Device ID register in the messaging engine. We do it
+ * once when first inbound mailbox is requested.
+ */
+ if (!(priv->flags & TSI721_IMSGID_SET)) {
+ iowrite32((u32)priv->mport->host_deviceid,
+ priv->regs + TSI721_IB_DEVID);
+ priv->flags |= TSI721_IMSGID_SET;
+ }
+
+ /*
+ * Configure Inbound Messaging channel (ch = mbox + 4)
+ */
+
+ /* Setup Inbound Message free queue */
+ iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys >> 32),
+ priv->regs + TSI721_IBDMAC_FQBH(ch));
+ iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys &
+ TSI721_IBDMAC_FQBL_MASK),
+ priv->regs+TSI721_IBDMAC_FQBL(ch));
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
+ priv->regs + TSI721_IBDMAC_FQSZ(ch));
+
+ /* Setup Inbound Message descriptor queue */
+ iowrite32(((u64)priv->imsg_ring[mbox].imd_phys >> 32),
+ priv->regs + TSI721_IBDMAC_DQBH(ch));
+ iowrite32(((u32)priv->imsg_ring[mbox].imd_phys &
+ (u32)TSI721_IBDMAC_DQBL_MASK),
+ priv->regs+TSI721_IBDMAC_DQBL(ch));
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
+ priv->regs + TSI721_IBDMAC_DQSZ(ch));
+
+ /* Enable interrupts */
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ /* Request interrupt service if we are in MSI-X mode */
+ rc = request_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
+ tsi721_imsg_msix, 0,
+ priv->msix[TSI721_VECT_IMB0_RCV + mbox].irq_name,
+ (void *)mport);
+
+ if (rc) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "IBOX%d-DONE\n", mbox);
+ goto out_desc;
+ }
+
+ rc = request_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector,
+ tsi721_imsg_msix, 0,
+ priv->msix[TSI721_VECT_IMB0_INT + mbox].irq_name,
+ (void *)mport);
+
+ if (rc) {
+ dev_dbg(&priv->pdev->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "IBOX%d-INT\n", mbox);
+ free_irq(
+ priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
+ (void *)mport);
+ goto out_desc;
+ }
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tsi721_imsg_interrupt_enable(priv, ch, TSI721_IBDMAC_INT_ALL);
+
+ /* Initialize Inbound Message Engine */
+ iowrite32(TSI721_IBDMAC_CTL_INIT, priv->regs + TSI721_IBDMAC_CTL(ch));
+ ioread32(priv->regs + TSI721_IBDMAC_CTL(ch));
+ udelay(10);
+ priv->imsg_ring[mbox].fq_wrptr = entries - 1;
+ iowrite32(entries - 1, priv->regs + TSI721_IBDMAC_FQWP(ch));
+
+ priv->imsg_init[mbox] = 1;
+ return 0;
+
+#ifdef CONFIG_PCI_MSI
+out_desc:
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
+ priv->imsg_ring[mbox].imd_base,
+ priv->imsg_ring[mbox].imd_phys);
+
+ priv->imsg_ring[mbox].imd_base = NULL;
+#endif /* CONFIG_PCI_MSI */
+
+out_dma:
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * 8,
+ priv->imsg_ring[mbox].imfq_base,
+ priv->imsg_ring[mbox].imfq_phys);
+
+ priv->imsg_ring[mbox].imfq_base = NULL;
+
+out_buf:
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
+ priv->imsg_ring[mbox].buf_base,
+ priv->imsg_ring[mbox].buf_phys);
+
+ priv->imsg_ring[mbox].buf_base = NULL;
+
+out:
+ return rc;
+}
+
+/**
+ * tsi721_close_inb_mbox - Shut down Tsi721 inbound mailbox
+ * @mport: Master port implementing the Inbound Messaging Engine
+ * @mbox: Mailbox to close
+ */
+static void tsi721_close_inb_mbox(struct rio_mport *mport, int mbox)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 rx_slot;
+ int ch = mbox + 4;
+
+ if (!priv->imsg_init[mbox]) /* mbox isn't initialized yet */
+ return;
+ priv->imsg_init[mbox] = 0;
+
+ /* Disable Inbound Messaging Engine */
+
+ /* Disable Interrupts */
+ tsi721_imsg_interrupt_disable(priv, ch, TSI721_OBDMAC_INT_MASK);
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ free_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
+ (void *)mport);
+ free_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector,
+ (void *)mport);
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ /* Clear Inbound Buffer Queue */
+ for (rx_slot = 0; rx_slot < priv->imsg_ring[mbox].size; rx_slot++)
+ priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
+
+ /* Free memory allocated for message buffers */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
+ priv->imsg_ring[mbox].buf_base,
+ priv->imsg_ring[mbox].buf_phys);
+
+ priv->imsg_ring[mbox].buf_base = NULL;
+
+ /* Free memory allocated for free pointr list */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * 8,
+ priv->imsg_ring[mbox].imfq_base,
+ priv->imsg_ring[mbox].imfq_phys);
+
+ priv->imsg_ring[mbox].imfq_base = NULL;
+
+ /* Free memory allocated for RX descriptors */
+ dma_free_coherent(&priv->pdev->dev,
+ priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
+ priv->imsg_ring[mbox].imd_base,
+ priv->imsg_ring[mbox].imd_phys);
+
+ priv->imsg_ring[mbox].imd_base = NULL;
+}
+
+/**
+ * tsi721_add_inb_buffer - Add buffer to the Tsi721 inbound message queue
+ * @mport: Master port implementing the Inbound Messaging Engine
+ * @mbox: Inbound mailbox number
+ * @buf: Buffer to add to inbound queue
+ */
+static int tsi721_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
+{
+ struct tsi721_device *priv = mport->priv;
+ u32 rx_slot;
+ int rc = 0;
+
+ rx_slot = priv->imsg_ring[mbox].rx_slot;
+ if (priv->imsg_ring[mbox].imq_base[rx_slot]) {
+ dev_err(&priv->pdev->dev,
+ "Error adding inbound buffer %d, buffer exists\n",
+ rx_slot);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ priv->imsg_ring[mbox].imq_base[rx_slot] = buf;
+
+ if (++priv->imsg_ring[mbox].rx_slot == priv->imsg_ring[mbox].size)
+ priv->imsg_ring[mbox].rx_slot = 0;
+
+out:
+ return rc;
+}
+
+/**
+ * tsi721_get_inb_message - Fetch inbound message from the Tsi721 MSG Queue
+ * @mport: Master port implementing the Inbound Messaging Engine
+ * @mbox: Inbound mailbox number
+ *
+ * Returns pointer to the message on success or NULL on failure.
+ */
+static void *tsi721_get_inb_message(struct rio_mport *mport, int mbox)
+{
+ struct tsi721_device *priv = mport->priv;
+ struct tsi721_imsg_desc *desc;
+ u32 rx_slot;
+ void *rx_virt = NULL;
+ u64 rx_phys;
+ void *buf = NULL;
+ u64 *free_ptr;
+ int ch = mbox + 4;
+ int msg_size;
+
+ if (!priv->imsg_init[mbox])
+ return NULL;
+
+ desc = priv->imsg_ring[mbox].imd_base;
+ desc += priv->imsg_ring[mbox].desc_rdptr;
+
+ if (!(le32_to_cpu(desc->msg_info) & TSI721_IMD_HO))
+ goto out;
+
+ rx_slot = priv->imsg_ring[mbox].rx_slot;
+ while (priv->imsg_ring[mbox].imq_base[rx_slot] == NULL) {
+ if (++rx_slot == priv->imsg_ring[mbox].size)
+ rx_slot = 0;
+ }
+
+ rx_phys = ((u64)le32_to_cpu(desc->bufptr_hi) << 32) |
+ le32_to_cpu(desc->bufptr_lo);
+
+ rx_virt = priv->imsg_ring[mbox].buf_base +
+ (rx_phys - (u64)priv->imsg_ring[mbox].buf_phys);
+
+ buf = priv->imsg_ring[mbox].imq_base[rx_slot];
+ msg_size = le32_to_cpu(desc->msg_info) & TSI721_IMD_BCOUNT;
+ if (msg_size == 0)
+ msg_size = RIO_MAX_MSG_SIZE;
+
+ memcpy(buf, rx_virt, msg_size);
+ priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
+
+ desc->msg_info &= cpu_to_le32(~TSI721_IMD_HO);
+ if (++priv->imsg_ring[mbox].desc_rdptr == priv->imsg_ring[mbox].size)
+ priv->imsg_ring[mbox].desc_rdptr = 0;
+
+ iowrite32(priv->imsg_ring[mbox].desc_rdptr,
+ priv->regs + TSI721_IBDMAC_DQRP(ch));
+
+ /* Return free buffer into the pointer list */
+ free_ptr = priv->imsg_ring[mbox].imfq_base;
+ free_ptr[priv->imsg_ring[mbox].fq_wrptr] = cpu_to_le64(rx_phys);
+
+ if (++priv->imsg_ring[mbox].fq_wrptr == priv->imsg_ring[mbox].size)
+ priv->imsg_ring[mbox].fq_wrptr = 0;
+
+ iowrite32(priv->imsg_ring[mbox].fq_wrptr,
+ priv->regs + TSI721_IBDMAC_FQWP(ch));
+out:
+ return buf;
+}
+
+/**
+ * tsi721_messages_init - Initialization of Messaging Engine
+ * @priv: pointer to tsi721 private data
+ *
+ * Configures Tsi721 messaging engine.
+ */
+static int tsi721_messages_init(struct tsi721_device *priv)
+{
+ int ch;
+
+ iowrite32(0, priv->regs + TSI721_SMSG_ECC_LOG);
+ iowrite32(0, priv->regs + TSI721_RETRY_GEN_CNT);
+ iowrite32(0, priv->regs + TSI721_RETRY_RX_CNT);
+
+ /* Set SRIO Message Request/Response Timeout */
+ iowrite32(TSI721_RQRPTO_VAL, priv->regs + TSI721_RQRPTO);
+
+ /* Initialize Inbound Messaging Engine Registers */
+ for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) {
+ /* Clear interrupt bits */
+ iowrite32(TSI721_IBDMAC_INT_MASK,
+ priv->regs + TSI721_IBDMAC_INT(ch));
+ /* Clear Status */
+ iowrite32(0, priv->regs + TSI721_IBDMAC_STS(ch));
+
+ iowrite32(TSI721_SMSG_ECC_COR_LOG_MASK,
+ priv->regs + TSI721_SMSG_ECC_COR_LOG(ch));
+ iowrite32(TSI721_SMSG_ECC_NCOR_MASK,
+ priv->regs + TSI721_SMSG_ECC_NCOR(ch));
+ }
+
+ return 0;
+}
+
+/**
+ * tsi721_disable_ints - disables all device interrupts
+ * @priv: pointer to tsi721 private data
+ */
+static void tsi721_disable_ints(struct tsi721_device *priv)
+{
+ int ch;
+
+ /* Disable all device level interrupts */
+ iowrite32(0, priv->regs + TSI721_DEV_INTE);
+
+ /* Disable all Device Channel interrupts */
+ iowrite32(0, priv->regs + TSI721_DEV_CHAN_INTE);
+
+ /* Disable all Inbound Msg Channel interrupts */
+ for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++)
+ iowrite32(0, priv->regs + TSI721_IBDMAC_INTE(ch));
+
+ /* Disable all Outbound Msg Channel interrupts */
+ for (ch = 0; ch < TSI721_OMSG_CHNUM; ch++)
+ iowrite32(0, priv->regs + TSI721_OBDMAC_INTE(ch));
+
+ /* Disable all general messaging interrupts */
+ iowrite32(0, priv->regs + TSI721_SMSG_INTE);
+
+ /* Disable all BDMA Channel interrupts */
+ for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
+ iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch));
+
+ /* Disable all general BDMA interrupts */
+ iowrite32(0, priv->regs + TSI721_BDMA_INTE);
+
+ /* Disable all SRIO Channel interrupts */
+ for (ch = 0; ch < TSI721_SRIO_MAXCH; ch++)
+ iowrite32(0, priv->regs + TSI721_SR_CHINTE(ch));
+
+ /* Disable all general SR2PC interrupts */
+ iowrite32(0, priv->regs + TSI721_SR2PC_GEN_INTE);
+
+ /* Disable all PC2SR interrupts */
+ iowrite32(0, priv->regs + TSI721_PC2SR_INTE);
+
+ /* Disable all I2C interrupts */
+ iowrite32(0, priv->regs + TSI721_I2C_INT_ENABLE);
+
+ /* Disable SRIO MAC interrupts */
+ iowrite32(0, priv->regs + TSI721_RIO_EM_INT_ENABLE);
+ iowrite32(0, priv->regs + TSI721_RIO_EM_DEV_INT_EN);
+}
+
+/**
+ * tsi721_setup_mport - Setup Tsi721 as RapidIO subsystem master port
+ * @priv: pointer to tsi721 private data
+ *
+ * Configures Tsi721 as RapidIO master port.
+ */
+static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ int err = 0;
+ struct rio_ops *ops;
+
+ struct rio_mport *mport;
+
+ ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL);
+ if (!ops) {
+ dev_dbg(&pdev->dev, "Unable to allocate memory for rio_ops\n");
+ return -ENOMEM;
+ }
+
+ ops->lcread = tsi721_lcread;
+ ops->lcwrite = tsi721_lcwrite;
+ ops->cread = tsi721_cread_dma;
+ ops->cwrite = tsi721_cwrite_dma;
+ ops->dsend = tsi721_dsend;
+ ops->open_inb_mbox = tsi721_open_inb_mbox;
+ ops->close_inb_mbox = tsi721_close_inb_mbox;
+ ops->open_outb_mbox = tsi721_open_outb_mbox;
+ ops->close_outb_mbox = tsi721_close_outb_mbox;
+ ops->add_outb_message = tsi721_add_outb_message;
+ ops->add_inb_buffer = tsi721_add_inb_buffer;
+ ops->get_inb_message = tsi721_get_inb_message;
+
+ mport = kzalloc(sizeof(struct rio_mport), GFP_KERNEL);
+ if (!mport) {
+ kfree(ops);
+ dev_dbg(&pdev->dev, "Unable to allocate memory for mport\n");
+ return -ENOMEM;
+ }
+
+ mport->ops = ops;
+ mport->index = 0;
+ mport->sys_size = 0; /* small system */
+ mport->phy_type = RIO_PHY_SERIAL;
+ mport->priv = (void *)priv;
+ mport->phys_efptr = 0x100;
+
+ INIT_LIST_HEAD(&mport->dbells);
+
+ rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
+ rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 0);
+ rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0);
+ strcpy(mport->name, "Tsi721 mport");
+
+ /* Hook up interrupt handler */
+
+#ifdef CONFIG_PCI_MSI
+ if (!tsi721_enable_msix(priv))
+ priv->flags |= TSI721_USING_MSIX;
+ else if (!pci_enable_msi(pdev))
+ priv->flags |= TSI721_USING_MSI;
+ else
+ dev_info(&pdev->dev,
+ "MSI/MSI-X is not available. Using legacy INTx.\n");
+#endif /* CONFIG_PCI_MSI */
+
+ err = tsi721_request_irq(mport);
+
+ if (!err) {
+ tsi721_interrupts_init(priv);
+ ops->pwenable = tsi721_pw_enable;
+ } else
+ dev_err(&pdev->dev, "Unable to get assigned PCI IRQ "
+ "vector %02X err=0x%x\n", pdev->irq, err);
+
+ /* Enable SRIO link */
+ iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
+ TSI721_DEVCTL_SRBOOT_CMPL,
+ priv->regs + TSI721_DEVCTL);
+
+ rio_register_mport(mport);
+ priv->mport = mport;
+
+ if (mport->host_deviceid >= 0)
+ iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
+ RIO_PORT_GEN_DISCOVERED,
+ priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
+ else
+ iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
+
+ return 0;
+}
+
+static int __devinit tsi721_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct tsi721_device *priv;
+ int i;
+ int err;
+ u32 regval;
+
+ priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate memory for device\n");
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to enable PCI device\n");
+ goto err_clean;
+ }
+
+ priv->pdev = pdev;
+
+#ifdef DEBUG
+ for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
+ dev_dbg(&pdev->dev, "res[%d] @ 0x%llx (0x%lx, 0x%lx)\n",
+ i, (unsigned long long)pci_resource_start(pdev, i),
+ (unsigned long)pci_resource_len(pdev, i),
+ pci_resource_flags(pdev, i));
+ }
+#endif
+ /*
+ * Verify BAR configuration
+ */
+
+ /* BAR_0 (registers) must be 512KB+ in 32-bit address space */
+ if (!(pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) ||
+ pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM_64 ||
+ pci_resource_len(pdev, BAR_0) < TSI721_REG_SPACE_SIZE) {
+ dev_err(&pdev->dev,
+ "Missing or misconfigured CSR BAR0, aborting.\n");
+ err = -ENODEV;
+ goto err_disable_pdev;
+ }
+
+ /* BAR_1 (outbound doorbells) must be 16MB+ in 32-bit address space */
+ if (!(pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM) ||
+ pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM_64 ||
+ pci_resource_len(pdev, BAR_1) < TSI721_DB_WIN_SIZE) {
+ dev_err(&pdev->dev,
+ "Missing or misconfigured Doorbell BAR1, aborting.\n");
+ err = -ENODEV;
+ goto err_disable_pdev;
+ }
+
+ /*
+ * BAR_2 and BAR_4 (outbound translation) must be in 64-bit PCIe address
+ * space.
+ * NOTE: BAR_2 and BAR_4 are not used by this version of driver.
+ * It may be a good idea to keep them disabled using HW configuration
+ * to save PCI memory space.
+ */
+ if ((pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM) &&
+ (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64)) {
+ dev_info(&pdev->dev, "Outbound BAR2 is not used but enabled.\n");
+ }
+
+ if ((pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM) &&
+ (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64)) {
+ dev_info(&pdev->dev, "Outbound BAR4 is not used but enabled.\n");
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot obtain PCI resources, "
+ "aborting.\n");
+ goto err_disable_pdev;
+ }
+
+ pci_set_master(pdev);
+
+ priv->regs = pci_ioremap_bar(pdev, BAR_0);
+ if (!priv->regs) {
+ dev_err(&pdev->dev,
+ "Unable to map device registers space, aborting\n");
+ err = -ENOMEM;
+ goto err_free_res;
+ }
+
+ priv->odb_base = pci_ioremap_bar(pdev, BAR_1);
+ if (!priv->odb_base) {
+ dev_err(&pdev->dev,
+ "Unable to map outbound doorbells space, aborting\n");
+ err = -ENOMEM;
+ goto err_unmap_bars;
+ }
+
+ /* Configure DMA attributes. */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ dev_info(&pdev->dev, "Unable to set DMA mask\n");
+ goto err_unmap_bars;
+ }
+
+ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
+ dev_info(&pdev->dev, "Unable to set consistent DMA mask\n");
+ } else {
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (err)
+ dev_info(&pdev->dev, "Unable to set consistent DMA mask\n");
+ }
+
+ /* Clear "no snoop" and "relaxed ordering" bits. */
+ pci_read_config_dword(pdev, 0x40 + PCI_EXP_DEVCTL, &regval);
+ regval &= ~(PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN);
+ pci_write_config_dword(pdev, 0x40 + PCI_EXP_DEVCTL, regval);
+
+ /*
+ * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
+ */
+ pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0x01);
+ pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXTBL,
+ TSI721_MSIXTBL_OFFSET);
+ pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXPBA,
+ TSI721_MSIXPBA_OFFSET);
+ pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0);
+ /* End of FIXUP */
+
+ tsi721_disable_ints(priv);
+
+ tsi721_init_pc2sr_mapping(priv);
+ tsi721_init_sr2pc_mapping(priv);
+
+ if (tsi721_bdma_init(priv)) {
+ dev_err(&pdev->dev, "BDMA initialization failed, aborting\n");
+ err = -ENOMEM;
+ goto err_unmap_bars;
+ }
+
+ err = tsi721_doorbell_init(priv);
+ if (err)
+ goto err_free_bdma;
+
+ tsi721_port_write_init(priv);
+
+ err = tsi721_messages_init(priv);
+ if (err)
+ goto err_free_consistent;
+
+ err = tsi721_setup_mport(priv);
+ if (err)
+ goto err_free_consistent;
+
+ return 0;
+
+err_free_consistent:
+ tsi721_doorbell_free(priv);
+err_free_bdma:
+ tsi721_bdma_free(priv);
+err_unmap_bars:
+ if (priv->regs)
+ iounmap(priv->regs);
+ if (priv->odb_base)
+ iounmap(priv->odb_base);
+err_free_res:
+ pci_release_regions(pdev);
+ pci_clear_master(pdev);
+err_disable_pdev:
+ pci_disable_device(pdev);
+err_clean:
+ kfree(priv);
+err_exit:
+ return err;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(tsi721_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_TSI721) },
+ { 0, } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, tsi721_pci_tbl);
+
+static struct pci_driver tsi721_driver = {
+ .name = "tsi721",
+ .id_table = tsi721_pci_tbl,
+ .probe = tsi721_probe,
+};
+
+static int __init tsi721_init(void)
+{
+ return pci_register_driver(&tsi721_driver);
+}
+
+static void __exit tsi721_exit(void)
+{
+ pci_unregister_driver(&tsi721_driver);
+}
+
+device_initcall(tsi721_init);
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
new file mode 100644
index 000000000000..58be4deb1402
--- /dev/null
+++ b/drivers/rapidio/devices/tsi721.h
@@ -0,0 +1,766 @@
+/*
+ * Tsi721 PCIExpress-to-SRIO bridge definitions
+ *
+ * Copyright 2011, Integrated Device Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * 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.
+ */
+
+#ifndef __TSI721_H
+#define __TSI721_H
+
+#define DRV_NAME "tsi721"
+
+#define DEFAULT_HOPCOUNT 0xff
+#define DEFAULT_DESTID 0xff
+
+/* PCI device ID */
+#define PCI_DEVICE_ID_TSI721 0x80ab
+
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_2 2
+#define BAR_4 4
+
+#define TSI721_PC2SR_BARS 2
+#define TSI721_PC2SR_WINS 8
+#define TSI721_PC2SR_ZONES 8
+#define TSI721_MAINT_WIN 0 /* Window for outbound maintenance requests */
+#define IDB_QUEUE 0 /* Inbound Doorbell Queue to use */
+#define IDB_QSIZE 512 /* Inbound Doorbell Queue size */
+
+/* Memory space sizes */
+#define TSI721_REG_SPACE_SIZE (512 * 1024) /* 512K */
+#define TSI721_DB_WIN_SIZE (16 * 1024 * 1024) /* 16MB */
+
+#define RIO_TT_CODE_8 0x00000000
+#define RIO_TT_CODE_16 0x00000001
+
+#define TSI721_DMA_MAXCH 8
+#define TSI721_DMA_MINSTSSZ 32
+#define TSI721_DMA_STSBLKSZ 8
+
+#define TSI721_SRIO_MAXCH 8
+
+#define DBELL_SID(buf) (((u8)buf[2] << 8) | (u8)buf[3])
+#define DBELL_TID(buf) (((u8)buf[4] << 8) | (u8)buf[5])
+#define DBELL_INF(buf) (((u8)buf[0] << 8) | (u8)buf[1])
+
+#define TSI721_RIO_PW_MSG_SIZE 16 /* Tsi721 saves only 16 bytes of PW msg */
+
+/* Register definitions */
+
+/*
+ * Registers in PCIe configuration space
+ */
+
+#define TSI721_PCIECFG_MSIXTBL 0x0a4
+#define TSI721_MSIXTBL_OFFSET 0x2c000
+#define TSI721_PCIECFG_MSIXPBA 0x0a8
+#define TSI721_MSIXPBA_OFFSET 0x2a000
+#define TSI721_PCIECFG_EPCTL 0x400
+
+/*
+ * Event Management Registers
+ */
+
+#define TSI721_RIO_EM_INT_STAT 0x10910
+#define TSI721_RIO_EM_INT_STAT_PW_RX 0x00010000
+
+#define TSI721_RIO_EM_INT_ENABLE 0x10914
+#define TSI721_RIO_EM_INT_ENABLE_PW_RX 0x00010000
+
+#define TSI721_RIO_EM_DEV_INT_EN 0x10930
+#define TSI721_RIO_EM_DEV_INT_EN_INT 0x00000001
+
+/*
+ * Port-Write Block Registers
+ */
+
+#define TSI721_RIO_PW_CTL 0x10a04
+#define TSI721_RIO_PW_CTL_PW_TIMER 0xf0000000
+#define TSI721_RIO_PW_CTL_PWT_DIS (0 << 28)
+#define TSI721_RIO_PW_CTL_PWT_103 (1 << 28)
+#define TSI721_RIO_PW_CTL_PWT_205 (1 << 29)
+#define TSI721_RIO_PW_CTL_PWT_410 (1 << 30)
+#define TSI721_RIO_PW_CTL_PWT_820 (1 << 31)
+#define TSI721_RIO_PW_CTL_PWC_MODE 0x01000000
+#define TSI721_RIO_PW_CTL_PWC_CONT 0x00000000
+#define TSI721_RIO_PW_CTL_PWC_REL 0x01000000
+
+#define TSI721_RIO_PW_RX_STAT 0x10a10
+#define TSI721_RIO_PW_RX_STAT_WR_SIZE 0x0000f000
+#define TSI_RIO_PW_RX_STAT_WDPTR 0x00000100
+#define TSI721_RIO_PW_RX_STAT_PW_SHORT 0x00000008
+#define TSI721_RIO_PW_RX_STAT_PW_TRUNC 0x00000004
+#define TSI721_RIO_PW_RX_STAT_PW_DISC 0x00000002
+#define TSI721_RIO_PW_RX_STAT_PW_VAL 0x00000001
+
+#define TSI721_RIO_PW_RX_CAPT(x) (0x10a20 + (x)*4)
+
+/*
+ * Inbound Doorbells
+ */
+
+#define TSI721_IDB_ENTRY_SIZE 64
+
+#define TSI721_IDQ_CTL(x) (0x20000 + (x) * 1000)
+#define TSI721_IDQ_SUSPEND 0x00000002
+#define TSI721_IDQ_INIT 0x00000001
+
+#define TSI721_IDQ_STS(x) (0x20004 + (x) * 1000)
+#define TSI721_IDQ_RUN 0x00200000
+
+#define TSI721_IDQ_MASK(x) (0x20008 + (x) * 1000)
+#define TSI721_IDQ_MASK_MASK 0xffff0000
+#define TSI721_IDQ_MASK_PATT 0x0000ffff
+
+#define TSI721_IDQ_RP(x) (0x2000c + (x) * 1000)
+#define TSI721_IDQ_RP_PTR 0x0007ffff
+
+#define TSI721_IDQ_WP(x) (0x20010 + (x) * 1000)
+#define TSI721_IDQ_WP_PTR 0x0007ffff
+
+#define TSI721_IDQ_BASEL(x) (0x20014 + (x) * 1000)
+#define TSI721_IDQ_BASEL_ADDR 0xffffffc0
+#define TSI721_IDQ_BASEU(x) (0x20018 + (x) * 1000)
+#define TSI721_IDQ_SIZE(x) (0x2001c + (x) * 1000)
+#define TSI721_IDQ_SIZE_VAL(size) (__fls(size) - 4)
+#define TSI721_IDQ_SIZE_MIN 512
+#define TSI721_IDQ_SIZE_MAX (512 * 1024)
+
+#define TSI721_SR_CHINT(x) (0x20040 + (x) * 1000)
+#define TSI721_SR_CHINTE(x) (0x20044 + (x) * 1000)
+#define TSI721_SR_CHINTSET(x) (0x20048 + (x) * 1000)
+#define TSI721_SR_CHINT_ODBOK 0x00000020
+#define TSI721_SR_CHINT_IDBQRCV 0x00000010
+#define TSI721_SR_CHINT_SUSP 0x00000008
+#define TSI721_SR_CHINT_ODBTO 0x00000004
+#define TSI721_SR_CHINT_ODBRTRY 0x00000002
+#define TSI721_SR_CHINT_ODBERR 0x00000001
+#define TSI721_SR_CHINT_ALL 0x0000003f
+
+#define TSI721_IBWIN_NUM 8
+
+#define TSI721_IBWINLB(x) (0x29000 + (x) * 20)
+#define TSI721_IBWINLB_BA 0xfffff000
+#define TSI721_IBWINLB_WEN 0x00000001
+
+#define TSI721_SR2PC_GEN_INTE 0x29800
+#define TSI721_SR2PC_PWE 0x29804
+#define TSI721_SR2PC_GEN_INT 0x29808
+
+#define TSI721_DEV_INTE 0x29840
+#define TSI721_DEV_INT 0x29844
+#define TSI721_DEV_INTSET 0x29848
+#define TSI721_DEV_INT_SMSG_CH 0x00000800
+#define TSI721_DEV_INT_SMSG_NCH 0x00000400
+#define TSI721_DEV_INT_SR2PC_CH 0x00000200
+#define TSI721_DEV_INT_SRIO 0x00000020
+
+#define TSI721_DEV_CHAN_INTE 0x2984c
+#define TSI721_DEV_CHAN_INT 0x29850
+
+#define TSI721_INT_SR2PC_CHAN_M 0xff000000
+#define TSI721_INT_SR2PC_CHAN(x) (1 << (24 + (x)))
+#define TSI721_INT_IMSG_CHAN_M 0x00ff0000
+#define TSI721_INT_IMSG_CHAN(x) (1 << (16 + (x)))
+#define TSI721_INT_OMSG_CHAN_M 0x0000ff00
+#define TSI721_INT_OMSG_CHAN(x) (1 << (8 + (x)))
+
+/*
+ * PC2SR block registers
+ */
+#define TSI721_OBWIN_NUM TSI721_PC2SR_WINS
+
+#define TSI721_OBWINLB(x) (0x40000 + (x) * 20)
+#define TSI721_OBWINLB_BA 0xffff8000
+#define TSI721_OBWINLB_WEN 0x00000001
+
+#define TSI721_OBWINUB(x) (0x40004 + (x) * 20)
+
+#define TSI721_OBWINSZ(x) (0x40008 + (x) * 20)
+#define TSI721_OBWINSZ_SIZE 0x00001f00
+#define TSI721_OBWIN_SIZE(size) (__fls(size) - 15)
+
+#define TSI721_ZONE_SEL 0x41300
+#define TSI721_ZONE_SEL_RD_WRB 0x00020000
+#define TSI721_ZONE_SEL_GO 0x00010000
+#define TSI721_ZONE_SEL_WIN 0x00000038
+#define TSI721_ZONE_SEL_ZONE 0x00000007
+
+#define TSI721_LUT_DATA0 0x41304
+#define TSI721_LUT_DATA0_ADD 0xfffff000
+#define TSI721_LUT_DATA0_RDTYPE 0x00000f00
+#define TSI721_LUT_DATA0_NREAD 0x00000100
+#define TSI721_LUT_DATA0_MNTRD 0x00000200
+#define TSI721_LUT_DATA0_RDCRF 0x00000020
+#define TSI721_LUT_DATA0_WRCRF 0x00000010
+#define TSI721_LUT_DATA0_WRTYPE 0x0000000f
+#define TSI721_LUT_DATA0_NWR 0x00000001
+#define TSI721_LUT_DATA0_MNTWR 0x00000002
+#define TSI721_LUT_DATA0_NWR_R 0x00000004
+
+#define TSI721_LUT_DATA1 0x41308
+
+#define TSI721_LUT_DATA2 0x4130c
+#define TSI721_LUT_DATA2_HC 0xff000000
+#define TSI721_LUT_DATA2_ADD65 0x000c0000
+#define TSI721_LUT_DATA2_TT 0x00030000
+#define TSI721_LUT_DATA2_DSTID 0x0000ffff
+
+#define TSI721_PC2SR_INTE 0x41310
+
+#define TSI721_DEVCTL 0x48004
+#define TSI721_DEVCTL_SRBOOT_CMPL 0x00000004
+
+#define TSI721_I2C_INT_ENABLE 0x49120
+
+/*
+ * Block DMA Engine Registers
+ * x = 0..7
+ */
+
+#define TSI721_DMAC_DWRCNT(x) (0x51000 + (x) * 0x1000)
+#define TSI721_DMAC_DRDCNT(x) (0x51004 + (x) * 0x1000)
+
+#define TSI721_DMAC_CTL(x) (0x51008 + (x) * 0x1000)
+#define TSI721_DMAC_CTL_SUSP 0x00000002
+#define TSI721_DMAC_CTL_INIT 0x00000001
+
+#define TSI721_DMAC_INT(x) (0x5100c + (x) * 0x1000)
+#define TSI721_DMAC_INT_STFULL 0x00000010
+#define TSI721_DMAC_INT_DONE 0x00000008
+#define TSI721_DMAC_INT_SUSP 0x00000004
+#define TSI721_DMAC_INT_ERR 0x00000002
+#define TSI721_DMAC_INT_IOFDONE 0x00000001
+#define TSI721_DMAC_INT_ALL 0x0000001f
+
+#define TSI721_DMAC_INTSET(x) (0x51010 + (x) * 0x1000)
+
+#define TSI721_DMAC_STS(x) (0x51014 + (x) * 0x1000)
+#define TSI721_DMAC_STS_ABORT 0x00400000
+#define TSI721_DMAC_STS_RUN 0x00200000
+#define TSI721_DMAC_STS_CS 0x001f0000
+
+#define TSI721_DMAC_INTE(x) (0x51018 + (x) * 0x1000)
+
+#define TSI721_DMAC_DPTRL(x) (0x51024 + (x) * 0x1000)
+#define TSI721_DMAC_DPTRL_MASK 0xffffffe0
+
+#define TSI721_DMAC_DPTRH(x) (0x51028 + (x) * 0x1000)
+
+#define TSI721_DMAC_DSBL(x) (0x5102c + (x) * 0x1000)
+#define TSI721_DMAC_DSBL_MASK 0xffffffc0
+
+#define TSI721_DMAC_DSBH(x) (0x51030 + (x) * 0x1000)
+
+#define TSI721_DMAC_DSSZ(x) (0x51034 + (x) * 0x1000)
+#define TSI721_DMAC_DSSZ_SIZE_M 0x0000000f
+#define TSI721_DMAC_DSSZ_SIZE(size) (__fls(size) - 4)
+
+
+#define TSI721_DMAC_DSRP(x) (0x51038 + (x) * 0x1000)
+#define TSI721_DMAC_DSRP_MASK 0x0007ffff
+
+#define TSI721_DMAC_DSWP(x) (0x5103c + (x) * 0x1000)
+#define TSI721_DMAC_DSWP_MASK 0x0007ffff
+
+#define TSI721_BDMA_INTE 0x5f000
+
+/*
+ * Messaging definitions
+ */
+#define TSI721_MSG_BUFFER_SIZE RIO_MAX_MSG_SIZE
+#define TSI721_MSG_MAX_SIZE RIO_MAX_MSG_SIZE
+#define TSI721_IMSG_MAXCH 8
+#define TSI721_IMSG_CHNUM TSI721_IMSG_MAXCH
+#define TSI721_IMSGD_MIN_RING_SIZE 32
+#define TSI721_IMSGD_RING_SIZE 512
+
+#define TSI721_OMSG_CHNUM 4 /* One channel per MBOX */
+#define TSI721_OMSGD_MIN_RING_SIZE 32
+#define TSI721_OMSGD_RING_SIZE 512
+
+/*
+ * Outbound Messaging Engine Registers
+ * x = 0..7
+ */
+
+#define TSI721_OBDMAC_DWRCNT(x) (0x61000 + (x) * 0x1000)
+
+#define TSI721_OBDMAC_DRDCNT(x) (0x61004 + (x) * 0x1000)
+
+#define TSI721_OBDMAC_CTL(x) (0x61008 + (x) * 0x1000)
+#define TSI721_OBDMAC_CTL_MASK 0x00000007
+#define TSI721_OBDMAC_CTL_RETRY_THR 0x00000004
+#define TSI721_OBDMAC_CTL_SUSPEND 0x00000002
+#define TSI721_OBDMAC_CTL_INIT 0x00000001
+
+#define TSI721_OBDMAC_INT(x) (0x6100c + (x) * 0x1000)
+#define TSI721_OBDMAC_INTSET(x) (0x61010 + (x) * 0x1000)
+#define TSI721_OBDMAC_INTE(x) (0x61018 + (x) * 0x1000)
+#define TSI721_OBDMAC_INT_MASK 0x0000001F
+#define TSI721_OBDMAC_INT_ST_FULL 0x00000010
+#define TSI721_OBDMAC_INT_DONE 0x00000008
+#define TSI721_OBDMAC_INT_SUSPENDED 0x00000004
+#define TSI721_OBDMAC_INT_ERROR 0x00000002
+#define TSI721_OBDMAC_INT_IOF_DONE 0x00000001
+#define TSI721_OBDMAC_INT_ALL TSI721_OBDMAC_INT_MASK
+
+#define TSI721_OBDMAC_STS(x) (0x61014 + (x) * 0x1000)
+#define TSI721_OBDMAC_STS_MASK 0x007f0000
+#define TSI721_OBDMAC_STS_ABORT 0x00400000
+#define TSI721_OBDMAC_STS_RUN 0x00200000
+#define TSI721_OBDMAC_STS_CS 0x001f0000
+
+#define TSI721_OBDMAC_PWE(x) (0x6101c + (x) * 0x1000)
+#define TSI721_OBDMAC_PWE_MASK 0x00000002
+#define TSI721_OBDMAC_PWE_ERROR_EN 0x00000002
+
+#define TSI721_OBDMAC_DPTRL(x) (0x61020 + (x) * 0x1000)
+#define TSI721_OBDMAC_DPTRL_MASK 0xfffffff0
+
+#define TSI721_OBDMAC_DPTRH(x) (0x61024 + (x) * 0x1000)
+#define TSI721_OBDMAC_DPTRH_MASK 0xffffffff
+
+#define TSI721_OBDMAC_DSBL(x) (0x61040 + (x) * 0x1000)
+#define TSI721_OBDMAC_DSBL_MASK 0xffffffc0
+
+#define TSI721_OBDMAC_DSBH(x) (0x61044 + (x) * 0x1000)
+#define TSI721_OBDMAC_DSBH_MASK 0xffffffff
+
+#define TSI721_OBDMAC_DSSZ(x) (0x61048 + (x) * 0x1000)
+#define TSI721_OBDMAC_DSSZ_MASK 0x0000000f
+
+#define TSI721_OBDMAC_DSRP(x) (0x6104c + (x) * 0x1000)
+#define TSI721_OBDMAC_DSRP_MASK 0x0007ffff
+
+#define TSI721_OBDMAC_DSWP(x) (0x61050 + (x) * 0x1000)
+#define TSI721_OBDMAC_DSWP_MASK 0x0007ffff
+
+#define TSI721_RQRPTO 0x60010
+#define TSI721_RQRPTO_MASK 0x00ffffff
+#define TSI721_RQRPTO_VAL 400 /* Response TO value */
+
+/*
+ * Inbound Messaging Engine Registers
+ * x = 0..7
+ */
+
+#define TSI721_IB_DEVID_GLOBAL 0xffff
+#define TSI721_IBDMAC_FQBL(x) (0x61200 + (x) * 0x1000)
+#define TSI721_IBDMAC_FQBL_MASK 0xffffffc0
+
+#define TSI721_IBDMAC_FQBH(x) (0x61204 + (x) * 0x1000)
+#define TSI721_IBDMAC_FQBH_MASK 0xffffffff
+
+#define TSI721_IBDMAC_FQSZ_ENTRY_INX TSI721_IMSGD_RING_SIZE
+#define TSI721_IBDMAC_FQSZ(x) (0x61208 + (x) * 0x1000)
+#define TSI721_IBDMAC_FQSZ_MASK 0x0000000f
+
+#define TSI721_IBDMAC_FQRP(x) (0x6120c + (x) * 0x1000)
+#define TSI721_IBDMAC_FQRP_MASK 0x0007ffff
+
+#define TSI721_IBDMAC_FQWP(x) (0x61210 + (x) * 0x1000)
+#define TSI721_IBDMAC_FQWP_MASK 0x0007ffff
+
+#define TSI721_IBDMAC_FQTH(x) (0x61214 + (x) * 0x1000)
+#define TSI721_IBDMAC_FQTH_MASK 0x0007ffff
+
+#define TSI721_IB_DEVID 0x60020
+#define TSI721_IB_DEVID_MASK 0x0000ffff
+
+#define TSI721_IBDMAC_CTL(x) (0x61240 + (x) * 0x1000)
+#define TSI721_IBDMAC_CTL_MASK 0x00000003
+#define TSI721_IBDMAC_CTL_SUSPEND 0x00000002
+#define TSI721_IBDMAC_CTL_INIT 0x00000001
+
+#define TSI721_IBDMAC_STS(x) (0x61244 + (x) * 0x1000)
+#define TSI721_IBDMAC_STS_MASK 0x007f0000
+#define TSI721_IBSMAC_STS_ABORT 0x00400000
+#define TSI721_IBSMAC_STS_RUN 0x00200000
+#define TSI721_IBSMAC_STS_CS 0x001f0000
+
+#define TSI721_IBDMAC_INT(x) (0x61248 + (x) * 0x1000)
+#define TSI721_IBDMAC_INTSET(x) (0x6124c + (x) * 0x1000)
+#define TSI721_IBDMAC_INTE(x) (0x61250 + (x) * 0x1000)
+#define TSI721_IBDMAC_INT_MASK 0x0000100f
+#define TSI721_IBDMAC_INT_SRTO 0x00001000
+#define TSI721_IBDMAC_INT_SUSPENDED 0x00000008
+#define TSI721_IBDMAC_INT_PC_ERROR 0x00000004
+#define TSI721_IBDMAC_INT_FQ_LOW 0x00000002
+#define TSI721_IBDMAC_INT_DQ_RCV 0x00000001
+#define TSI721_IBDMAC_INT_ALL TSI721_IBDMAC_INT_MASK
+
+#define TSI721_IBDMAC_PWE(x) (0x61254 + (x) * 0x1000)
+#define TSI721_IBDMAC_PWE_MASK 0x00001700
+#define TSI721_IBDMAC_PWE_SRTO 0x00001000
+#define TSI721_IBDMAC_PWE_ILL_FMT 0x00000400
+#define TSI721_IBDMAC_PWE_ILL_DEC 0x00000200
+#define TSI721_IBDMAC_PWE_IMP_SP 0x00000100
+
+#define TSI721_IBDMAC_DQBL(x) (0x61300 + (x) * 0x1000)
+#define TSI721_IBDMAC_DQBL_MASK 0xffffffc0
+#define TSI721_IBDMAC_DQBL_ADDR 0xffffffc0
+
+#define TSI721_IBDMAC_DQBH(x) (0x61304 + (x) * 0x1000)
+#define TSI721_IBDMAC_DQBH_MASK 0xffffffff
+
+#define TSI721_IBDMAC_DQRP(x) (0x61308 + (x) * 0x1000)
+#define TSI721_IBDMAC_DQRP_MASK 0x0007ffff
+
+#define TSI721_IBDMAC_DQWR(x) (0x6130c + (x) * 0x1000)
+#define TSI721_IBDMAC_DQWR_MASK 0x0007ffff
+
+#define TSI721_IBDMAC_DQSZ(x) (0x61314 + (x) * 0x1000)
+#define TSI721_IBDMAC_DQSZ_MASK 0x0000000f
+
+/*
+ * Messaging Engine Interrupts
+ */
+
+#define TSI721_SMSG_PWE 0x6a004
+
+#define TSI721_SMSG_INTE 0x6a000
+#define TSI721_SMSG_INT 0x6a008
+#define TSI721_SMSG_INTSET 0x6a010
+#define TSI721_SMSG_INT_MASK 0x0086ffff
+#define TSI721_SMSG_INT_UNS_RSP 0x00800000
+#define TSI721_SMSG_INT_ECC_NCOR 0x00040000
+#define TSI721_SMSG_INT_ECC_COR 0x00020000
+#define TSI721_SMSG_INT_ECC_NCOR_CH 0x0000ff00
+#define TSI721_SMSG_INT_ECC_COR_CH 0x000000ff
+
+#define TSI721_SMSG_ECC_LOG 0x6a014
+#define TSI721_SMSG_ECC_LOG_MASK 0x00070007
+#define TSI721_SMSG_ECC_LOG_ECC_NCOR_M 0x00070000
+#define TSI721_SMSG_ECC_LOG_ECC_COR_M 0x00000007
+
+#define TSI721_RETRY_GEN_CNT 0x6a100
+#define TSI721_RETRY_GEN_CNT_MASK 0xffffffff
+
+#define TSI721_RETRY_RX_CNT 0x6a104
+#define TSI721_RETRY_RX_CNT_MASK 0xffffffff
+
+#define TSI721_SMSG_ECC_COR_LOG(x) (0x6a300 + (x) * 4)
+#define TSI721_SMSG_ECC_COR_LOG_MASK 0x000000ff
+
+#define TSI721_SMSG_ECC_NCOR(x) (0x6a340 + (x) * 4)
+#define TSI721_SMSG_ECC_NCOR_MASK 0x000000ff
+
+/*
+ * Block DMA Descriptors
+ */
+
+struct tsi721_dma_desc {
+ __le32 type_id;
+
+#define TSI721_DMAD_DEVID 0x0000ffff
+#define TSI721_DMAD_CRF 0x00010000
+#define TSI721_DMAD_PRIO 0x00060000
+#define TSI721_DMAD_RTYPE 0x00780000
+#define TSI721_DMAD_IOF 0x08000000
+#define TSI721_DMAD_DTYPE 0xe0000000
+
+ __le32 bcount;
+
+#define TSI721_DMAD_BCOUNT1 0x03ffffff /* if DTYPE == 1 */
+#define TSI721_DMAD_BCOUNT2 0x0000000f /* if DTYPE == 2 */
+#define TSI721_DMAD_TT 0x0c000000
+#define TSI721_DMAD_RADDR0 0xc0000000
+
+ union {
+ __le32 raddr_lo; /* if DTYPE == (1 || 2) */
+ __le32 next_lo; /* if DTYPE == 3 */
+ };
+
+#define TSI721_DMAD_CFGOFF 0x00ffffff
+#define TSI721_DMAD_HOPCNT 0xff000000
+
+ union {
+ __le32 raddr_hi; /* if DTYPE == (1 || 2) */
+ __le32 next_hi; /* if DTYPE == 3 */
+ };
+
+ union {
+ struct { /* if DTYPE == 1 */
+ __le32 bufptr_lo;
+ __le32 bufptr_hi;
+ __le32 s_dist;
+ __le32 s_size;
+ } t1;
+ __le32 data[4]; /* if DTYPE == 2 */
+ u32 reserved[4]; /* if DTYPE == 3 */
+ };
+} __aligned(32);
+
+/*
+ * Inbound Messaging Descriptor
+ */
+struct tsi721_imsg_desc {
+ __le32 type_id;
+
+#define TSI721_IMD_DEVID 0x0000ffff
+#define TSI721_IMD_CRF 0x00010000
+#define TSI721_IMD_PRIO 0x00060000
+#define TSI721_IMD_TT 0x00180000
+#define TSI721_IMD_DTYPE 0xe0000000
+
+ __le32 msg_info;
+
+#define TSI721_IMD_BCOUNT 0x00000ff8
+#define TSI721_IMD_SSIZE 0x0000f000
+#define TSI721_IMD_LETER 0x00030000
+#define TSI721_IMD_XMBOX 0x003c0000
+#define TSI721_IMD_MBOX 0x00c00000
+#define TSI721_IMD_CS 0x78000000
+#define TSI721_IMD_HO 0x80000000
+
+ __le32 bufptr_lo;
+ __le32 bufptr_hi;
+ u32 reserved[12];
+
+} __aligned(64);
+
+/*
+ * Outbound Messaging Descriptor
+ */
+struct tsi721_omsg_desc {
+ __le32 type_id;
+
+#define TSI721_OMD_DEVID 0x0000ffff
+#define TSI721_OMD_CRF 0x00010000
+#define TSI721_OMD_PRIO 0x00060000
+#define TSI721_OMD_IOF 0x08000000
+#define TSI721_OMD_DTYPE 0xe0000000
+#define TSI721_OMD_RSRVD 0x17f80000
+
+ __le32 msg_info;
+
+#define TSI721_OMD_BCOUNT 0x00000ff8
+#define TSI721_OMD_SSIZE 0x0000f000
+#define TSI721_OMD_LETER 0x00030000
+#define TSI721_OMD_XMBOX 0x003c0000
+#define TSI721_OMD_MBOX 0x00c00000
+#define TSI721_OMD_TT 0x0c000000
+
+ union {
+ __le32 bufptr_lo; /* if DTYPE == 4 */
+ __le32 next_lo; /* if DTYPE == 5 */
+ };
+
+ union {
+ __le32 bufptr_hi; /* if DTYPE == 4 */
+ __le32 next_hi; /* if DTYPE == 5 */
+ };
+
+} __aligned(16);
+
+struct tsi721_dma_sts {
+ __le64 desc_sts[8];
+} __aligned(64);
+
+struct tsi721_desc_sts_fifo {
+ union {
+ __le64 da64;
+ struct {
+ __le32 lo;
+ __le32 hi;
+ } da32;
+ } stat[8];
+} __aligned(64);
+
+/* Descriptor types for BDMA and Messaging blocks */
+enum dma_dtype {
+ DTYPE1 = 1, /* Data Transfer DMA Descriptor */
+ DTYPE2 = 2, /* Immediate Data Transfer DMA Descriptor */
+ DTYPE3 = 3, /* Block Pointer DMA Descriptor */
+ DTYPE4 = 4, /* Outbound Msg DMA Descriptor */
+ DTYPE5 = 5, /* OB Messaging Block Pointer Descriptor */
+ DTYPE6 = 6 /* Inbound Messaging Descriptor */
+};
+
+enum dma_rtype {
+ NREAD = 0,
+ LAST_NWRITE_R = 1,
+ ALL_NWRITE = 2,
+ ALL_NWRITE_R = 3,
+ MAINT_RD = 4,
+ MAINT_WR = 5
+};
+
+/*
+ * mport Driver Definitions
+ */
+#define TSI721_DMA_CHNUM TSI721_DMA_MAXCH
+
+#define TSI721_DMACH_MAINT 0 /* DMA channel for maint requests */
+#define TSI721_DMACH_MAINT_NBD 32 /* Number of BDs for maint requests */
+
+#define MSG_DMA_ENTRY_INX_TO_SIZE(x) ((0x10 << (x)) & 0xFFFF0)
+
+enum tsi721_smsg_int_flag {
+ SMSG_INT_NONE = 0x00000000,
+ SMSG_INT_ECC_COR_CH = 0x000000ff,
+ SMSG_INT_ECC_NCOR_CH = 0x0000ff00,
+ SMSG_INT_ECC_COR = 0x00020000,
+ SMSG_INT_ECC_NCOR = 0x00040000,
+ SMSG_INT_UNS_RSP = 0x00800000,
+ SMSG_INT_ALL = 0x0006ffff
+};
+
+/* Structures */
+
+struct tsi721_bdma_chan {
+ int bd_num; /* number of buffer descriptors */
+ void *bd_base; /* start of DMA descriptors */
+ dma_addr_t bd_phys;
+ void *sts_base; /* start of DMA BD status FIFO */
+ dma_addr_t sts_phys;
+ int sts_size;
+};
+
+struct tsi721_imsg_ring {
+ u32 size;
+ /* VA/PA of data buffers for incoming messages */
+ void *buf_base;
+ dma_addr_t buf_phys;
+ /* VA/PA of circular free buffer list */
+ void *imfq_base;
+ dma_addr_t imfq_phys;
+ /* VA/PA of Inbound message descriptors */
+ void *imd_base;
+ dma_addr_t imd_phys;
+ /* Inbound Queue buffer pointers */
+ void *imq_base[TSI721_IMSGD_RING_SIZE];
+
+ u32 rx_slot;
+ void *dev_id;
+ u32 fq_wrptr;
+ u32 desc_rdptr;
+ spinlock_t lock;
+};
+
+struct tsi721_omsg_ring {
+ u32 size;
+ /* VA/PA of OB Msg descriptors */
+ void *omd_base;
+ dma_addr_t omd_phys;
+ /* VA/PA of OB Msg data buffers */
+ void *omq_base[TSI721_OMSGD_RING_SIZE];
+ dma_addr_t omq_phys[TSI721_OMSGD_RING_SIZE];
+ /* VA/PA of OB Msg descriptor status FIFO */
+ void *sts_base;
+ dma_addr_t sts_phys;
+ u32 sts_size; /* # of allocated status entries */
+ u32 sts_rdptr;
+
+ u32 tx_slot;
+ void *dev_id;
+ u32 wr_count;
+ spinlock_t lock;
+};
+
+enum tsi721_flags {
+ TSI721_USING_MSI = (1 << 0),
+ TSI721_USING_MSIX = (1 << 1),
+ TSI721_IMSGID_SET = (1 << 2),
+};
+
+#ifdef CONFIG_PCI_MSI
+/*
+ * MSI-X Table Entries (0 ... 69)
+ */
+#define TSI721_MSIX_DMACH_DONE(x) (0 + (x))
+#define TSI721_MSIX_DMACH_INT(x) (8 + (x))
+#define TSI721_MSIX_BDMA_INT 16
+#define TSI721_MSIX_OMSG_DONE(x) (17 + (x))
+#define TSI721_MSIX_OMSG_INT(x) (25 + (x))
+#define TSI721_MSIX_IMSG_DQ_RCV(x) (33 + (x))
+#define TSI721_MSIX_IMSG_INT(x) (41 + (x))
+#define TSI721_MSIX_MSG_INT 49
+#define TSI721_MSIX_SR2PC_IDBQ_RCV(x) (50 + (x))
+#define TSI721_MSIX_SR2PC_CH_INT(x) (58 + (x))
+#define TSI721_MSIX_SR2PC_INT 66
+#define TSI721_MSIX_PC2SR_INT 67
+#define TSI721_MSIX_SRIO_MAC_INT 68
+#define TSI721_MSIX_I2C_INT 69
+
+/* MSI-X vector and init table entry indexes */
+enum tsi721_msix_vect {
+ TSI721_VECT_IDB,
+ TSI721_VECT_PWRX, /* PW_RX is part of SRIO MAC Interrupt reporting */
+ TSI721_VECT_OMB0_DONE,
+ TSI721_VECT_OMB1_DONE,
+ TSI721_VECT_OMB2_DONE,
+ TSI721_VECT_OMB3_DONE,
+ TSI721_VECT_OMB0_INT,
+ TSI721_VECT_OMB1_INT,
+ TSI721_VECT_OMB2_INT,
+ TSI721_VECT_OMB3_INT,
+ TSI721_VECT_IMB0_RCV,
+ TSI721_VECT_IMB1_RCV,
+ TSI721_VECT_IMB2_RCV,
+ TSI721_VECT_IMB3_RCV,
+ TSI721_VECT_IMB0_INT,
+ TSI721_VECT_IMB1_INT,
+ TSI721_VECT_IMB2_INT,
+ TSI721_VECT_IMB3_INT,
+ TSI721_VECT_MAX
+};
+
+#define IRQ_DEVICE_NAME_MAX 64
+
+struct msix_irq {
+ u16 vector;
+ char irq_name[IRQ_DEVICE_NAME_MAX];
+};
+#endif /* CONFIG_PCI_MSI */
+
+struct tsi721_device {
+ struct pci_dev *pdev;
+ struct rio_mport *mport;
+ u32 flags;
+ void __iomem *regs;
+#ifdef CONFIG_PCI_MSI
+ struct msix_irq msix[TSI721_VECT_MAX];
+#endif
+ /* Doorbells */
+ void __iomem *odb_base;
+ void *idb_base;
+ dma_addr_t idb_dma;
+ struct work_struct idb_work;
+ u32 db_discard_count;
+
+ /* Inbound Port-Write */
+ struct work_struct pw_work;
+ struct kfifo pw_fifo;
+ spinlock_t pw_fifo_lock;
+ u32 pw_discard_count;
+
+ /* BDMA Engine */
+ struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM];
+
+ /* Inbound Messaging */
+ int imsg_init[TSI721_IMSG_CHNUM];
+ struct tsi721_imsg_ring imsg_ring[TSI721_IMSG_CHNUM];
+
+ /* Outbound Messaging */
+ int omsg_init[TSI721_OMSG_CHNUM];
+ struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM];
+};
+
+#endif
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index ebe77dd87daf..2bebd791a092 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -516,7 +516,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
return rdev;
cleanup:
- if (rio_is_switch(rdev))
+ if (rswitch)
kfree(rswitch->route_table);
kfree(rdev);
@@ -923,7 +923,7 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
* rio_enum_complete- Tests if enumeration of a network is complete
* @port: Master port to send transaction
*
- * Tests the Component Tag CSR for non-zero value (enumeration
+ * Tests the PGCCSR discovered bit for non-zero value (enumeration
* complete flag). Return %1 if enumeration is complete or %0 if
* enumeration is incomplete.
*/
@@ -933,7 +933,7 @@ static int rio_enum_complete(struct rio_mport *port)
rio_local_read_config_32(port, port->phys_efptr + RIO_PORT_GEN_CTL_CSR,
&regval);
- return (regval & RIO_PORT_GEN_MASTER) ? 1 : 0;
+ return (regval & RIO_PORT_GEN_DISCOVERED) ? 1 : 0;
}
/**
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index d63fddb0fbb0..e821b2159b4b 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -412,7 +412,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
if (info->desc.id == res->start)
break;
}
- if ((i < 0) || (i > PM8607_ID_RG_MAX)) {
+ if (i == ARRAY_SIZE(pm8607_regulator_info)) {
dev_err(&pdev->dev, "Failed to find regulator %llu\n",
(unsigned long long)res->start);
return -EINVAL;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c7fd2c0e3f2b..9713b1b860cb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -64,6 +64,16 @@ config REGULATOR_USERSPACE_CONSUMER
If unsure, say no.
+config REGULATOR_GPIO
+ tristate "GPIO regulator support"
+ depends on GENERIC_GPIO
+ help
+ This driver provides support for regulators that can be
+ controlled via gpios.
+ It is capable of supporting current and voltage regulators
+ and the platform has to provide a mapping of GPIO-states
+ to target volts/amps.
+
config REGULATOR_BQ24022
tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC"
help
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 040d5aa63535..93a6318f5328 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
index cd4104542f0d..5abeb3ac3e8d 100644
--- a/drivers/regulator/aat2870-regulator.c
+++ b/drivers/regulator/aat2870-regulator.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 9e4c123c4028..67fa2a3297ad 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1425,7 +1425,7 @@ int regulator_enable(struct regulator *regulator)
ret = _regulator_enable(rdev);
mutex_unlock(&rdev->mutex);
- if (ret != 0)
+ if (ret != 0 && rdev->supply)
regulator_disable(rdev->supply);
return ret;
@@ -2971,6 +2971,43 @@ void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data)
}
EXPORT_SYMBOL_GPL(regulator_get_init_drvdata);
+#ifdef CONFIG_DEBUG_FS
+static ssize_t supply_map_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ ssize_t len, ret = 0;
+ struct regulator_map *map;
+
+ if (!buf)
+ return -ENOMEM;
+
+ list_for_each_entry(map, &regulator_map_list, list) {
+ len = snprintf(buf + ret, PAGE_SIZE - ret,
+ "%s -> %s.%s\n",
+ rdev_get_name(map->regulator), map->dev_name,
+ map->supply);
+ if (len >= 0)
+ ret += len;
+ if (ret > PAGE_SIZE) {
+ ret = PAGE_SIZE;
+ break;
+ }
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations supply_map_fops = {
+ .read = supply_map_read_file,
+ .llseek = default_llseek,
+};
+#endif
+
static int __init regulator_init(void)
{
int ret;
@@ -2983,6 +3020,10 @@ static int __init regulator_init(void)
pr_warn("regulator: Failed to create debugfs directory\n");
debugfs_root = NULL;
}
+
+ if (IS_ERR(debugfs_create_file("supply_map", 0444, debugfs_root,
+ NULL, &supply_map_fops)))
+ pr_warn("regulator: Failed to create supplies debugfs\n");
#endif
regulator_dummy_init();
diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c
index 2bb8f451cc06..2d014a144365 100644
--- a/drivers/regulator/db8500-prcmu.c
+++ b/drivers/regulator/db8500-prcmu.c
@@ -13,7 +13,7 @@
#include <linux/err.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/db8500-prcmu.h>
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
new file mode 100644
index 000000000000..f0acf52498bd
--- /dev/null
+++ b/drivers/regulator/gpio-regulator.c
@@ -0,0 +1,358 @@
+/*
+ * gpio-regulator.c
+ *
+ * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on fixed.c
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Copyright (c) 2009 Nokia Corporation
+ * Roger Quadros <ext-roger.quadros@nokia.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 is useful for systems with mixed controllable and
+ * non-controllable regulators, as well as for allowing testing on
+ * systems with no controllable regulators.
+ */
+
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/gpio-regulator.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+struct gpio_regulator_data {
+ struct regulator_desc desc;
+ struct regulator_dev *dev;
+
+ int enable_gpio;
+ bool enable_high;
+ bool is_enabled;
+ unsigned startup_delay;
+
+ struct gpio *gpios;
+ int nr_gpios;
+
+ struct gpio_regulator_state *states;
+ int nr_states;
+
+ int state;
+};
+
+static int gpio_regulator_is_enabled(struct regulator_dev *dev)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+
+ return data->is_enabled;
+}
+
+static int gpio_regulator_enable(struct regulator_dev *dev)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+
+ if (gpio_is_valid(data->enable_gpio)) {
+ gpio_set_value_cansleep(data->enable_gpio, data->enable_high);
+ data->is_enabled = true;
+ }
+
+ return 0;
+}
+
+static int gpio_regulator_disable(struct regulator_dev *dev)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+
+ if (gpio_is_valid(data->enable_gpio)) {
+ gpio_set_value_cansleep(data->enable_gpio, !data->enable_high);
+ data->is_enabled = false;
+ }
+
+ return 0;
+}
+
+static int gpio_regulator_enable_time(struct regulator_dev *dev)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+
+ return data->startup_delay;
+}
+
+static int gpio_regulator_get_value(struct regulator_dev *dev)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+ int ptr;
+
+ for (ptr = 0; ptr < data->nr_states; ptr++)
+ if (data->states[ptr].gpios == data->state)
+ return data->states[ptr].value;
+
+ return -EINVAL;
+}
+
+static int gpio_regulator_set_value(struct regulator_dev *dev,
+ int min, int max)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+ int ptr, target, state;
+
+ target = -1;
+ for (ptr = 0; ptr < data->nr_states; ptr++)
+ if (data->states[ptr].value >= min &&
+ data->states[ptr].value <= max)
+ target = data->states[ptr].gpios;
+
+ if (target < 0)
+ return -EINVAL;
+
+ for (ptr = 0; ptr < data->nr_gpios; ptr++) {
+ state = (target & (1 << ptr)) >> ptr;
+ gpio_set_value(data->gpios[ptr].gpio, state);
+ }
+ data->state = target;
+
+ return 0;
+}
+
+static int gpio_regulator_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV,
+ unsigned *selector)
+{
+ return gpio_regulator_set_value(dev, min_uV, max_uV);
+}
+
+static int gpio_regulator_list_voltage(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+
+ if (selector >= data->nr_states)
+ return -EINVAL;
+
+ return data->states[selector].value;
+}
+
+static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
+ int min_uA, int max_uA)
+{
+ return gpio_regulator_set_value(dev, min_uA, max_uA);
+}
+
+static struct regulator_ops gpio_regulator_voltage_ops = {
+ .is_enabled = gpio_regulator_is_enabled,
+ .enable = gpio_regulator_enable,
+ .disable = gpio_regulator_disable,
+ .enable_time = gpio_regulator_enable_time,
+ .get_voltage = gpio_regulator_get_value,
+ .set_voltage = gpio_regulator_set_voltage,
+ .list_voltage = gpio_regulator_list_voltage,
+};
+
+static struct regulator_ops gpio_regulator_current_ops = {
+ .is_enabled = gpio_regulator_is_enabled,
+ .enable = gpio_regulator_enable,
+ .disable = gpio_regulator_disable,
+ .enable_time = gpio_regulator_enable_time,
+ .get_current_limit = gpio_regulator_get_value,
+ .set_current_limit = gpio_regulator_set_current_limit,
+};
+
+static int __devinit gpio_regulator_probe(struct platform_device *pdev)
+{
+ struct gpio_regulator_config *config = pdev->dev.platform_data;
+ struct gpio_regulator_data *drvdata;
+ int ptr, ret, state;
+
+ drvdata = kzalloc(sizeof(struct gpio_regulator_data), GFP_KERNEL);
+ if (drvdata == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate device data\n");
+ return -ENOMEM;
+ }
+
+ drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
+ if (drvdata->desc.name == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate supply name\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ drvdata->gpios = kmemdup(config->gpios,
+ config->nr_gpios * sizeof(struct gpio),
+ GFP_KERNEL);
+ if (drvdata->gpios == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate gpio data\n");
+ ret = -ENOMEM;
+ goto err_name;
+ }
+
+ drvdata->states = kmemdup(config->states,
+ config->nr_states *
+ sizeof(struct gpio_regulator_state),
+ GFP_KERNEL);
+ if (drvdata->states == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate state data\n");
+ ret = -ENOMEM;
+ goto err_memgpio;
+ }
+ drvdata->nr_states = config->nr_states;
+
+ drvdata->desc.owner = THIS_MODULE;
+
+ /* handle regulator type*/
+ switch (config->type) {
+ case REGULATOR_VOLTAGE:
+ drvdata->desc.type = REGULATOR_VOLTAGE;
+ drvdata->desc.ops = &gpio_regulator_voltage_ops;
+ drvdata->desc.n_voltages = config->nr_states;
+ break;
+ case REGULATOR_CURRENT:
+ drvdata->desc.type = REGULATOR_CURRENT;
+ drvdata->desc.ops = &gpio_regulator_current_ops;
+ break;
+ default:
+ dev_err(&pdev->dev, "No regulator type set\n");
+ ret = -EINVAL;
+ goto err_memgpio;
+ break;
+ }
+
+ drvdata->enable_gpio = config->enable_gpio;
+ drvdata->startup_delay = config->startup_delay;
+
+ if (gpio_is_valid(config->enable_gpio)) {
+ drvdata->enable_high = config->enable_high;
+
+ ret = gpio_request(config->enable_gpio, config->supply_name);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not obtain regulator enable GPIO %d: %d\n",
+ config->enable_gpio, ret);
+ goto err_memstate;
+ }
+
+ /* set output direction without changing state
+ * to prevent glitch
+ */
+ if (config->enabled_at_boot) {
+ drvdata->is_enabled = true;
+ ret = gpio_direction_output(config->enable_gpio,
+ config->enable_high);
+ } else {
+ drvdata->is_enabled = false;
+ ret = gpio_direction_output(config->enable_gpio,
+ !config->enable_high);
+ }
+
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not configure regulator enable GPIO %d direction: %d\n",
+ config->enable_gpio, ret);
+ goto err_enablegpio;
+ }
+ } else {
+ /* Regulator without GPIO control is considered
+ * always enabled
+ */
+ drvdata->is_enabled = true;
+ }
+
+ drvdata->nr_gpios = config->nr_gpios;
+ ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not obtain regulator setting GPIOs: %d\n", ret);
+ goto err_enablegpio;
+ }
+
+ /* build initial state from gpio init data. */
+ state = 0;
+ for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) {
+ if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH)
+ state |= (1 << ptr);
+ }
+ drvdata->state = state;
+
+ drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev,
+ config->init_data, drvdata);
+ if (IS_ERR(drvdata->dev)) {
+ ret = PTR_ERR(drvdata->dev);
+ dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
+ goto err_stategpio;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+ return 0;
+
+err_stategpio:
+ gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
+err_enablegpio:
+ if (gpio_is_valid(config->enable_gpio))
+ gpio_free(config->enable_gpio);
+err_memstate:
+ kfree(drvdata->states);
+err_memgpio:
+ kfree(drvdata->gpios);
+err_name:
+ kfree(drvdata->desc.name);
+err:
+ kfree(drvdata);
+ return ret;
+}
+
+static int __devexit gpio_regulator_remove(struct platform_device *pdev)
+{
+ struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev);
+
+ regulator_unregister(drvdata->dev);
+
+ gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
+
+ kfree(drvdata->states);
+ kfree(drvdata->gpios);
+
+ if (gpio_is_valid(drvdata->enable_gpio))
+ gpio_free(drvdata->enable_gpio);
+
+ kfree(drvdata->desc.name);
+ kfree(drvdata);
+
+ return 0;
+}
+
+static struct platform_driver gpio_regulator_driver = {
+ .probe = gpio_regulator_probe,
+ .remove = __devexit_p(gpio_regulator_remove),
+ .driver = {
+ .name = "gpio-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gpio_regulator_init(void)
+{
+ return platform_driver_register(&gpio_regulator_driver);
+}
+subsys_initcall(gpio_regulator_init);
+
+static void __exit gpio_regulator_exit(void)
+{
+ platform_driver_unregister(&gpio_regulator_driver);
+}
+module_exit(gpio_regulator_exit);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("gpio voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-regulator");
diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c
index 30eb9e54f7ec..1062cf9f02dc 100644
--- a/drivers/regulator/max8649.c
+++ b/drivers/regulator/max8649.c
@@ -221,7 +221,7 @@ static int max8649_enable_time(struct regulator_dev *rdev)
ret = (ret & MAX8649_RAMP_MASK) >> 5;
rate = (32 * 1000) >> ret; /* uV/uS */
- return (voltage / rate);
+ return DIV_ROUND_UP(voltage, rate);
}
static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode)
diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c
index 486ed8141fcd..3883d85c5b88 100644
--- a/drivers/regulator/max8952.c
+++ b/drivers/regulator/max8952.c
@@ -26,7 +26,6 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/max8952.h>
-#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -47,7 +46,6 @@ enum {
struct max8952_data {
struct i2c_client *client;
struct device *dev;
- struct mutex mutex;
struct max8952_platform_data *pdata;
struct regulator_dev *rdev;
@@ -208,7 +206,6 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client,
max8952->client = client;
max8952->dev = &client->dev;
max8952->pdata = pdata;
- mutex_init(&max8952->mutex);
max8952->rdev = regulator_register(&regulator, max8952->dev,
&pdata->reg_data, max8952);
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index 730f43ad415b..cb2841feeefd 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -336,9 +336,9 @@ static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv;
struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
- struct mc13783_regulator_platform_data *pdata =
+ struct mc13xxx_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
- struct mc13783_regulator_init_data *init_data;
+ struct mc13xxx_regulator_init_data *init_data;
int i, ret;
dev_dbg(&pdev->dev, "%s id %d\n", __func__, pdev->id);
@@ -381,7 +381,7 @@ err:
static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
- struct mc13783_regulator_platform_data *pdata =
+ struct mc13xxx_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
int i;
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index 701a5900f83f..9fb4c7b81753 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -63,6 +63,13 @@
#define TPS65023_REG_CTRL_LDO2_EN BIT(2)
#define TPS65023_REG_CTRL_LDO1_EN BIT(1)
+/* REG_CTRL2 bitfields */
+#define TPS65023_REG_CTRL2_GO BIT(7)
+#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6)
+#define TPS65023_REG_CTRL2_DCDC2 BIT(2)
+#define TPS65023_REG_CTRL2_DCDC1 BIT(1)
+#define TPS65023_REG_CTRL2_DCDC3 BIT(0)
+
/* LDO_CTRL bitfields */
#define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4)
#define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4))
@@ -85,7 +92,7 @@
#define TPS65023_MAX_REG_ID TPS65023_LDO_2
/* Supported voltage values for regulators */
-static const u16 VDCDC1_VSEL_table[] = {
+static const u16 VCORE_VSEL_table[] = {
800, 825, 850, 875,
900, 925, 950, 975,
1000, 1025, 1050, 1075,
@@ -96,20 +103,29 @@ static const u16 VDCDC1_VSEL_table[] = {
1500, 1525, 1550, 1600,
};
-static const u16 LDO1_VSEL_table[] = {
+/* Supported voltage values for LDO regulators for tps65020 */
+static const u16 TPS65020_LDO1_VSEL_table[] = {
+ 1000, 1050, 1100, 1300,
+ 1800, 2500, 3000, 3300,
+};
+
+static const u16 TPS65020_LDO2_VSEL_table[] = {
+ 1000, 1050, 1100, 1300,
+ 1800, 2500, 3000, 3300,
+};
+
+/* Supported voltage values for LDO regulators
+ * for tps65021 and tps65023 */
+static const u16 TPS65023_LDO1_VSEL_table[] = {
1000, 1100, 1300, 1800,
2200, 2600, 2800, 3150,
};
-static const u16 LDO2_VSEL_table[] = {
+static const u16 TPS65023_LDO2_VSEL_table[] = {
1050, 1200, 1300, 1800,
2500, 2800, 3000, 3300,
};
-static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDC1_VSEL_table),
- 0, 0, ARRAY_SIZE(LDO1_VSEL_table),
- ARRAY_SIZE(LDO2_VSEL_table)};
-
/* Regulator specific details */
struct tps_info {
const char *name;
@@ -127,6 +143,13 @@ struct tps_pmic {
struct regulator_dev *rdev[TPS65023_NUM_REGULATOR];
const struct tps_info *info[TPS65023_NUM_REGULATOR];
struct regmap *regmap;
+ u8 core_regulator;
+};
+
+/* Struct passed as driver data */
+struct tps_driver_data {
+ const struct tps_info *info;
+ u8 core_regulator;
};
static int tps_65023_set_bits(struct tps_pmic *tps, u8 reg, u8 mask)
@@ -253,7 +276,7 @@ static int tps65023_dcdc_get_voltage(struct regulator_dev *dev)
if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
return -EINVAL;
- if (dcdc == TPS65023_DCDC_1) {
+ if (dcdc == tps->core_regulator) {
data = tps_65023_reg_read(tps, TPS65023_REG_DEF_CORE);
if (data < 0)
return data;
@@ -270,10 +293,10 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev,
struct tps_pmic *tps = rdev_get_drvdata(dev);
int dcdc = rdev_get_id(dev);
int vsel;
+ int ret;
- if (dcdc != TPS65023_DCDC_1)
+ if (dcdc != tps->core_regulator)
return -EINVAL;
-
if (min_uV < tps->info[dcdc]->min_uV
|| min_uV > tps->info[dcdc]->max_uV)
return -EINVAL;
@@ -292,11 +315,21 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev,
*selector = vsel;
- /* write to the register in case we found a match */
if (vsel == tps->info[dcdc]->table_len)
- return -EINVAL;
- else
- return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel);
+ goto failed;
+
+ ret = tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel);
+
+ /* Tell the chip that we have changed the value in DEFCORE
+ * and its time to update the core voltage
+ */
+ tps_65023_set_bits(tps, TPS65023_REG_CON_CTRL2,
+ TPS65023_REG_CTRL2_GO);
+
+ return ret;
+
+failed:
+ return -EINVAL;
}
static int tps65023_ldo_get_voltage(struct regulator_dev *dev)
@@ -362,7 +395,7 @@ static int tps65023_dcdc_list_voltage(struct regulator_dev *dev,
if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
return -EINVAL;
- if (dcdc == TPS65023_DCDC_1) {
+ if (dcdc == tps->core_regulator) {
if (selector >= tps->info[dcdc]->table_len)
return -EINVAL;
else
@@ -414,7 +447,8 @@ static struct regmap_config tps65023_regmap_config = {
static int __devinit tps_65023_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct tps_info *info = (void *)id->driver_data;
+ const struct tps_driver_data *drv_data = (void *)id->driver_data;
+ const struct tps_info *info = drv_data->info;
struct regulator_init_data *init_data;
struct regulator_dev *rdev;
struct tps_pmic *tps;
@@ -446,6 +480,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client,
/* common for all regulators */
tps->client = client;
+ tps->core_regulator = drv_data->core_regulator;
for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) {
/* Store regulator specific information */
@@ -453,7 +488,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client,
tps->desc[i].name = info->name;
tps->desc[i].id = i;
- tps->desc[i].n_voltages = num_voltages[i];
+ tps->desc[i].n_voltages = info->table_len;
tps->desc[i].ops = (i > TPS65023_DCDC_3 ?
&tps65023_ldo_ops : &tps65023_dcdc_ops);
tps->desc[i].type = REGULATOR_VOLTAGE;
@@ -475,6 +510,14 @@ static int __devinit tps_65023_probe(struct i2c_client *client,
i2c_set_clientdata(client, tps);
+ /* Enable setting output voltage by I2C */
+ tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2,
+ TPS65023_REG_CTRL2_CORE_ADJ);
+
+ /* Enable setting output voltage by I2C */
+ tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2,
+ TPS65023_REG_CTRL2_CORE_ADJ);
+
return 0;
fail:
@@ -507,13 +550,86 @@ static int __devexit tps_65023_remove(struct i2c_client *client)
return 0;
}
+static const struct tps_info tps65020_regs[] = {
+ {
+ .name = "VDCDC1",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .fixed = 1,
+ },
+ {
+ .name = "VDCDC2",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .fixed = 1,
+ },
+ {
+ .name = "VDCDC3",
+ .min_uV = 800000,
+ .max_uV = 1600000,
+ .table_len = ARRAY_SIZE(VCORE_VSEL_table),
+ .table = VCORE_VSEL_table,
+ },
+
+ {
+ .name = "LDO1",
+ .min_uV = 1000000,
+ .max_uV = 3150000,
+ .table_len = ARRAY_SIZE(TPS65020_LDO1_VSEL_table),
+ .table = TPS65020_LDO1_VSEL_table,
+ },
+ {
+ .name = "LDO2",
+ .min_uV = 1050000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(TPS65020_LDO2_VSEL_table),
+ .table = TPS65020_LDO2_VSEL_table,
+ },
+};
+
+static const struct tps_info tps65021_regs[] = {
+ {
+ .name = "VDCDC1",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .fixed = 1,
+ },
+ {
+ .name = "VDCDC2",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .fixed = 1,
+ },
+ {
+ .name = "VDCDC3",
+ .min_uV = 800000,
+ .max_uV = 1600000,
+ .table_len = ARRAY_SIZE(VCORE_VSEL_table),
+ .table = VCORE_VSEL_table,
+ },
+ {
+ .name = "LDO1",
+ .min_uV = 1000000,
+ .max_uV = 3150000,
+ .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table),
+ .table = TPS65023_LDO1_VSEL_table,
+ },
+ {
+ .name = "LDO2",
+ .min_uV = 1050000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table),
+ .table = TPS65023_LDO2_VSEL_table,
+ },
+};
+
static const struct tps_info tps65023_regs[] = {
{
.name = "VDCDC1",
.min_uV = 800000,
.max_uV = 1600000,
- .table_len = ARRAY_SIZE(VDCDC1_VSEL_table),
- .table = VDCDC1_VSEL_table,
+ .table_len = ARRAY_SIZE(VCORE_VSEL_table),
+ .table = VCORE_VSEL_table,
},
{
.name = "VDCDC2",
@@ -531,23 +647,40 @@ static const struct tps_info tps65023_regs[] = {
.name = "LDO1",
.min_uV = 1000000,
.max_uV = 3150000,
- .table_len = ARRAY_SIZE(LDO1_VSEL_table),
- .table = LDO1_VSEL_table,
+ .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table),
+ .table = TPS65023_LDO1_VSEL_table,
},
{
.name = "LDO2",
.min_uV = 1050000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(LDO2_VSEL_table),
- .table = LDO2_VSEL_table,
+ .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table),
+ .table = TPS65023_LDO2_VSEL_table,
},
};
+static struct tps_driver_data tps65020_drv_data = {
+ .info = tps65020_regs,
+ .core_regulator = TPS65023_DCDC_3,
+};
+
+static struct tps_driver_data tps65021_drv_data = {
+ .info = tps65021_regs,
+ .core_regulator = TPS65023_DCDC_3,
+};
+
+static struct tps_driver_data tps65023_drv_data = {
+ .info = tps65023_regs,
+ .core_regulator = TPS65023_DCDC_1,
+};
+
static const struct i2c_device_id tps_65023_id[] = {
{.name = "tps65023",
- .driver_data = (unsigned long) tps65023_regs,},
+ .driver_data = (unsigned long) &tps65023_drv_data},
{.name = "tps65021",
- .driver_data = (unsigned long) tps65023_regs,},
+ .driver_data = (unsigned long) &tps65021_drv_data,},
+ {.name = "tps65020",
+ .driver_data = (unsigned long) &tps65020_drv_data},
{ },
};
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index bfffabc21eda..bdef70365f52 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -90,12 +90,6 @@ static const u16 LDO2_VSEL_table[] = {
3000, 3100, 3200, 3300,
};
-static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDCx_VSEL_table),
- ARRAY_SIZE(VDCDCx_VSEL_table),
- ARRAY_SIZE(VDCDCx_VSEL_table),
- ARRAY_SIZE(LDO1_VSEL_table),
- ARRAY_SIZE(LDO2_VSEL_table)};
-
struct tps_info {
const char *name;
unsigned min_uV;
@@ -598,7 +592,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev)
tps->desc[i].name = info->name;
tps->desc[i].id = i;
- tps->desc[i].n_voltages = num_voltages[i];
+ tps->desc[i].n_voltages = info->table_len;
tps->desc[i].ops = (i > TPS6507X_DCDC_3 ?
&tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops);
tps->desc[i].type = REGULATOR_VOLTAGE;
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index bb04a75a4c98..dbcf09d5080c 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -332,6 +332,36 @@ static inline int tps6586x_regulator_preinit(struct device *parent,
1 << ri->enable_bit[1]);
}
+static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev)
+{
+ struct device *parent = pdev->dev.parent;
+ struct regulator_init_data *p = pdev->dev.platform_data;
+ struct tps6586x_settings *setting = p->driver_data;
+ uint8_t reg;
+
+ if (setting == NULL)
+ return 0;
+
+ if (!(setting->slew_rate & TPS6586X_SLEW_RATE_SET))
+ return 0;
+
+ /* only SM0 and SM1 can have the slew rate settings */
+ switch (pdev->id) {
+ case TPS6586X_ID_SM_0:
+ reg = TPS6586X_SM0SL;
+ break;
+ case TPS6586X_ID_SM_1:
+ reg = TPS6586X_SM1SL;
+ break;
+ default:
+ dev_warn(&pdev->dev, "Only SM0/SM1 can set slew rate\n");
+ return -EINVAL;
+ }
+
+ return tps6586x_write(parent, reg,
+ setting->slew_rate & TPS6586X_SLEW_RATE_MASK);
+}
+
static inline struct tps6586x_regulator *find_regulator_info(int id)
{
struct tps6586x_regulator *ri;
@@ -374,7 +404,7 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rdev);
- return 0;
+ return tps6586x_regulator_set_slew_rate(pdev);
}
static int __devexit tps6586x_regulator_remove(struct platform_device *pdev)
diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c
index 3a9313e00fac..39d4a1749e71 100644
--- a/drivers/regulator/tps65912-regulator.c
+++ b/drivers/regulator/tps65912-regulator.c
@@ -43,8 +43,6 @@
#define TPS65912_REG_LDO9 12
#define TPS65912_REG_LDO10 13
-#define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10
-
/* Number of step-down converters available */
#define TPS65912_NUM_DCDC 4
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5a538fc1cc85..53eb4e55b289 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -8,7 +8,7 @@ config RTC_LIB
menuconfig RTC_CLASS
bool "Real Time Clock"
default n
- depends on !S390
+ depends on !S390 && !UML
select RTC_LIB
help
Generic RTC class support. If you say yes here, you will
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 01a7df5317c1..e8326f26fa2f 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -21,16 +21,13 @@
#include "rtc-core.h"
-static DEFINE_IDR(rtc_idr);
-static DEFINE_MUTEX(idr_lock);
+static DEFINE_IDA(rtc_ida);
struct class *rtc_class;
static void rtc_device_release(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
- mutex_lock(&idr_lock);
- idr_remove(&rtc_idr, rtc->id);
- mutex_unlock(&idr_lock);
+ ida_simple_remove(&rtc_ida, rtc->id);
kfree(rtc);
}
@@ -146,25 +143,16 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
struct rtc_wkalrm alrm;
int id, err;
- if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
- err = -ENOMEM;
+ id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ err = id;
goto exit;
}
-
- mutex_lock(&idr_lock);
- err = idr_get_new(&rtc_idr, NULL, &id);
- mutex_unlock(&idr_lock);
-
- if (err < 0)
- goto exit;
-
- id = id & MAX_ID_MASK;
-
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
err = -ENOMEM;
- goto exit_idr;
+ goto exit_ida;
}
rtc->id = id;
@@ -222,10 +210,8 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
exit_kfree:
kfree(rtc);
-exit_idr:
- mutex_lock(&idr_lock);
- idr_remove(&rtc_idr, id);
- mutex_unlock(&idr_lock);
+exit_ida:
+ ida_simple_remove(&rtc_ida, id);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
@@ -276,7 +262,7 @@ static void __exit rtc_exit(void)
{
rtc_dev_exit();
class_destroy(rtc_class);
- idr_destroy(&rtc_idr);
+ ida_destroy(&rtc_ida);
}
subsys_initcall(rtc_init);
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index b2005b44e4f7..62b0763b7b9a 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -34,6 +34,7 @@ enum ds_type {
ds_1388,
ds_3231,
m41t00,
+ mcp7941x,
rx_8025,
// rs5c372 too? different address...
};
@@ -43,6 +44,7 @@ enum ds_type {
#define DS1307_REG_SECS 0x00 /* 00-59 */
# define DS1307_BIT_CH 0x80
# define DS1340_BIT_nEOSC 0x80
+# define MCP7941X_BIT_ST 0x80
#define DS1307_REG_MIN 0x01 /* 00-59 */
#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */
@@ -50,6 +52,7 @@ enum ds_type {
# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
#define DS1307_REG_WDAY 0x03 /* 01-07 */
+# define MCP7941X_BIT_VBATEN 0x08
#define DS1307_REG_MDAY 0x04 /* 01-31 */
#define DS1307_REG_MONTH 0x05 /* 01-12 */
# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
@@ -137,6 +140,8 @@ static const struct chip_desc chips[] = {
},
[m41t00] = {
},
+[mcp7941x] = {
+},
[rx_8025] = {
}, };
@@ -149,6 +154,7 @@ static const struct i2c_device_id ds1307_id[] = {
{ "ds1340", ds_1340 },
{ "ds3231", ds_3231 },
{ "m41t00", m41t00 },
+ { "mcp7941x", mcp7941x },
{ "pt7c4338", ds_1307 },
{ "rx8025", rx_8025 },
{ }
@@ -365,6 +371,10 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
| DS1340_BIT_CENTURY;
break;
+ case mcp7941x:
+ buf[DS1307_REG_SECS] |= MCP7941X_BIT_ST;
+ buf[DS1307_REG_WDAY] |= MCP7941X_BIT_VBATEN;
+ break;
default:
break;
}
@@ -809,6 +819,23 @@ read_rtc:
dev_warn(&client->dev, "SET TIME!\n");
}
break;
+ case mcp7941x:
+ /* make sure that the backup battery is enabled */
+ if (!(ds1307->regs[DS1307_REG_WDAY] & MCP7941X_BIT_VBATEN)) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_WDAY,
+ ds1307->regs[DS1307_REG_WDAY]
+ | MCP7941X_BIT_VBATEN);
+ }
+
+ /* clock halted? turn it on, so clock can tick. */
+ if (!(tmp & MCP7941X_BIT_ST)) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS,
+ MCP7941X_BIT_ST);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+
+ break;
case rx_8025:
case ds_1337:
case ds_1339:
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index a1a278bc340d..9d0c3b478d55 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -309,7 +309,7 @@ static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
return IRQ_HANDLED;
}
-static int __devinit mc13xxx_rtc_probe(struct platform_device *pdev)
+static int __init mc13xxx_rtc_probe(struct platform_device *pdev)
{
int ret;
struct mc13xxx_rtc *priv;
@@ -378,7 +378,7 @@ err_reset_irq_request:
return ret;
}
-static int __devexit mc13xxx_rtc_remove(struct platform_device *pdev)
+static int __exit mc13xxx_rtc_remove(struct platform_device *pdev)
{
struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
@@ -410,7 +410,7 @@ const struct platform_device_id mc13xxx_rtc_idtable[] = {
static struct platform_driver mc13xxx_rtc_driver = {
.id_table = mc13xxx_rtc_idtable,
- .remove = __devexit_p(mc13xxx_rtc_remove),
+ .remove = __exit_p(mc13xxx_rtc_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index a1d3ddba99cc..65894f05a801 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -11,7 +11,6 @@
#define KMSG_COMPONENT "dasd"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/kernel_stat.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -1594,7 +1593,6 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
unsigned long long now;
int expires;
- kstat_cpu(smp_processor_id()).irqs[IOINT_DAS]++;
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
@@ -2061,13 +2059,14 @@ void dasd_add_request_tail(struct dasd_ccw_req *cqr)
/*
* Wakeup helper for the 'sleep_on' functions.
*/
-static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
+void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
{
spin_lock_irq(get_ccwdev_lock(cqr->startdev->cdev));
cqr->callback_data = DASD_SLEEPON_END_TAG;
spin_unlock_irq(get_ccwdev_lock(cqr->startdev->cdev));
wake_up(&generic_waitq);
}
+EXPORT_SYMBOL_GPL(dasd_wakeup_cb);
static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
{
@@ -2167,7 +2166,9 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
} else
wait_event(generic_waitq, !(device->stopped));
- cqr->callback = dasd_wakeup_cb;
+ if (!cqr->callback)
+ cqr->callback = dasd_wakeup_cb;
+
cqr->callback_data = DASD_SLEEPON_START_TAG;
dasd_add_request_tail(cqr);
if (interruptible) {
@@ -2263,7 +2264,11 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = DASD_SLEEPON_START_TAG;
cqr->status = DASD_CQR_QUEUED;
- list_add(&cqr->devlist, &device->ccw_queue);
+ /*
+ * add new request as second
+ * first the terminated cqr needs to be finished
+ */
+ list_add(&cqr->devlist, device->ccw_queue.next);
/* let the bh start the request to keep them in order */
dasd_schedule_device_bh(device);
@@ -3284,6 +3289,9 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
if (IS_ERR(device))
return PTR_ERR(device);
+ /* mark device as suspended */
+ set_bit(DASD_FLAG_SUSPENDED, &device->flags);
+
if (device->discipline->freeze)
rc = device->discipline->freeze(device);
@@ -3358,6 +3366,7 @@ int dasd_generic_restore_device(struct ccw_device *cdev)
if (device->block)
dasd_schedule_block_bh(device->block);
+ clear_bit(DASD_FLAG_SUSPENDED, &device->flags);
dasd_put_device(device);
return 0;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 6e835c9fdfcb..6ab29680586a 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -844,6 +844,30 @@ static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
}
+/*
+ * Wakeup helper for read_conf
+ * if the cqr is not done and needs some error recovery
+ * the buffer has to be re-initialized with the EBCDIC "V1.0"
+ * to show support for virtual device SNEQ
+ */
+static void read_conf_cb(struct dasd_ccw_req *cqr, void *data)
+{
+ struct ccw1 *ccw;
+ __u8 *rcd_buffer;
+
+ if (cqr->status != DASD_CQR_DONE) {
+ ccw = cqr->cpaddr;
+ rcd_buffer = (__u8 *)((addr_t) ccw->cda);
+ memset(rcd_buffer, 0, sizeof(*rcd_buffer));
+
+ rcd_buffer[0] = 0xE5;
+ rcd_buffer[1] = 0xF1;
+ rcd_buffer[2] = 0x4B;
+ rcd_buffer[3] = 0xF0;
+ }
+ dasd_wakeup_cb(cqr, data);
+}
+
static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
struct dasd_ccw_req *cqr,
__u8 *rcd_buffer,
@@ -863,6 +887,7 @@ static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
cqr->retries = 5;
+ cqr->callback = read_conf_cb;
rc = dasd_sleep_on_immediatly(cqr);
return rc;
}
@@ -900,6 +925,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
goto out_error;
}
dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
+ cqr->callback = read_conf_cb;
ret = dasd_sleep_on(cqr);
/*
* on success we update the user input parms
@@ -1075,6 +1101,12 @@ static void do_path_verification_work(struct work_struct *work)
data = container_of(work, struct path_verification_work_data, worker);
device = data->device;
+ /* delay path verification until device was resumed */
+ if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
+ schedule_work(work);
+ return;
+ }
+
opm = 0;
npm = 0;
ppm = 0;
@@ -2021,9 +2053,13 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
if ((scsw_dstat(&irb->scsw) & mask) == mask) {
- /* for alias only and not in offline processing*/
+ /*
+ * for alias only, not in offline processing
+ * and only if not suspended
+ */
if (!device->block && private->lcu &&
- !test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ !test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
+ !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
/*
* the state change could be caused by an alias
* reassignment remove device from alias handling
@@ -2350,7 +2386,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
new_track = 1;
end_idaw = 0;
len_to_track_end = 0;
- idaw_dst = 0;
+ idaw_dst = NULL;
idaw_len = 0;
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
@@ -2412,7 +2448,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
if (end_idaw) {
idaws = idal_create_words(idaws, idaw_dst,
idaw_len);
- idaw_dst = 0;
+ idaw_dst = NULL;
idaw_len = 0;
end_idaw = 0;
}
@@ -3998,6 +4034,7 @@ static struct ccw_driver dasd_eckd_driver = {
.thaw = dasd_generic_restore_device,
.restore = dasd_generic_restore_device,
.uc_handler = dasd_generic_uc_handler,
+ .int_class = IOINT_DAS,
};
/*
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 4b71b1164868..a62a75358eb9 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -79,6 +79,7 @@ static struct ccw_driver dasd_fba_driver = {
.freeze = dasd_generic_pm_freeze,
.thaw = dasd_generic_restore_device,
.restore = dasd_generic_restore_device,
+ .int_class = IOINT_DAS,
};
static void
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 1dd12bd85a69..afe8c33422ed 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -516,6 +516,7 @@ struct dasd_block {
*/
#define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */
#define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */
+#define DASD_FLAG_SUSPENDED 9 /* The device was suspended */
void dasd_put_device_wake(struct dasd_device *);
@@ -643,6 +644,7 @@ struct dasd_ccw_req *
dasd_smalloc_request(int , int, int, struct dasd_device *);
void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *);
void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *);
+void dasd_wakeup_cb(struct dasd_ccw_req *, void *);
static inline int
dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device)
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 9b43ae94beba..a5a55da2a1ac 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -27,7 +27,7 @@
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
static int dcssblk_release(struct gendisk *disk, fmode_t mode);
-static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
+static void dcssblk_make_request(struct request_queue *q, struct bio *bio);
static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
void **kaddr, unsigned long *pfn);
@@ -814,7 +814,7 @@ out:
return rc;
}
-static int
+static void
dcssblk_make_request(struct request_queue *q, struct bio *bio)
{
struct dcssblk_dev_info *dev_info;
@@ -871,10 +871,9 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
bytes_done += bvec->bv_len;
}
bio_endio(bio, 0);
- return 0;
+ return;
fail:
bio_io_error(bio);
- return 0;
}
static int
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 1f6a4d894e73..98f3e4ade924 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -181,7 +181,7 @@ static unsigned long xpram_highest_page_index(void)
/*
* Block device make request function.
*/
-static int xpram_make_request(struct request_queue *q, struct bio *bio)
+static void xpram_make_request(struct request_queue *q, struct bio *bio)
{
xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data;
struct bio_vec *bvec;
@@ -221,10 +221,9 @@ static int xpram_make_request(struct request_queue *q, struct bio *bio)
}
set_bit(BIO_UPTODATE, &bio->bi_flags);
bio_endio(bio, 0);
- return 0;
+ return;
fail:
bio_io_error(bio);
- return 0;
}
static int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 694464c65fcd..934458ad55e5 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -9,7 +9,6 @@
* Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu>
*/
-#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
@@ -362,7 +361,6 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,
int cstat, dstat;
int count;
- kstat_cpu(smp_processor_id()).irqs[IOINT_C15]++;
raw = dev_get_drvdata(&cdev->dev);
req = (struct raw3215_req *) intparm;
cstat = irb->scsw.cmd.cstat;
@@ -776,6 +774,7 @@ static struct ccw_driver raw3215_ccw_driver = {
.freeze = &raw3215_pm_stop,
.thaw = &raw3215_pm_start,
.restore = &raw3215_pm_start,
+ .int_class = IOINT_C15,
};
#ifdef CONFIG_TN3215_CONSOLE
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 810ac38631c3..e5cb9248a442 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -7,7 +7,6 @@
* Copyright IBM Corp. 2003, 2009
*/
-#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -330,7 +329,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
struct raw3270_request *rq;
int rc;
- kstat_cpu(smp_processor_id()).irqs[IOINT_C70]++;
rp = dev_get_drvdata(&cdev->dev);
if (!rp)
return;
@@ -1398,6 +1396,7 @@ static struct ccw_driver raw3270_ccw_driver = {
.freeze = &raw3270_pm_stop,
.thaw = &raw3270_pm_start,
.restore = &raw3270_pm_start,
+ .int_class = IOINT_C70,
};
static int
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 837e010299a8..0b54a91f8dcd 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -61,8 +61,8 @@ static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
rc = sclp_service_call(cmd, sccb);
if (rc)
goto out;
- __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
- PSW_MASK_WAIT | PSW_DEFAULT_KEY);
+ __load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
+ PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT);
local_irq_disable();
out:
/* Contents of the sccb might have changed. */
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index a90a02c28d6a..87fc0ac11e67 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -30,7 +30,8 @@ static void do_machine_quiesce(void)
psw_t quiesce_psw;
smp_send_stop();
- quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
+ quiesce_psw.mask =
+ PSW_MASK_BASE | PSW_MASK_EA | PSW_MASK_BA | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 9eff2df70ddb..934ef33eb9a4 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -1330,6 +1330,7 @@ static struct ccw_driver tape_34xx_driver = {
.set_online = tape_34xx_online,
.set_offline = tape_generic_offline,
.freeze = tape_generic_pm_suspend,
+ .int_class = IOINT_TAP,
};
static int
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index a7d570728882..49c6aab7ad78 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -1762,6 +1762,7 @@ static struct ccw_driver tape_3590_driver = {
.set_offline = tape_generic_offline,
.set_online = tape_3590_online,
.freeze = tape_generic_pm_suspend,
+ .int_class = IOINT_TAP,
};
/*
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 7978a0adeaf3..b3a3e8e8656e 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -14,7 +14,6 @@
#define KMSG_COMPONENT "tape"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/init.h> // for kernel parameters
#include <linux/kmod.h> // for requesting modules
@@ -1115,7 +1114,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
struct tape_request *request;
int rc;
- kstat_cpu(smp_processor_id()).irqs[IOINT_TAP]++;
device = dev_get_drvdata(&cdev->dev);
if (device == NULL) {
return;
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index f6b00c3df425..d291a54acfad 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -11,7 +11,6 @@
#define KMSG_COMPONENT "vmur"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/kernel_stat.h>
#include <linux/cdev.h>
#include <linux/slab.h>
@@ -74,6 +73,7 @@ static struct ccw_driver ur_driver = {
.set_online = ur_set_online,
.set_offline = ur_set_offline,
.freeze = ur_pm_suspend,
+ .int_class = IOINT_VMR,
};
static DEFINE_MUTEX(vmur_mutex);
@@ -305,7 +305,6 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
{
struct urdev *urd;
- kstat_cpu(smp_processor_id()).irqs[IOINT_VMR]++;
TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
irb->scsw.cmd.count);
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 3b94044027c2..43068fbd0baa 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
+#include <linux/module.h>
#include <asm/asm-offsets.h>
#include <asm/ipl.h>
#include <asm/sclp.h>
@@ -142,22 +143,6 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
return memcpy_hsa(dest, src, count, TO_KERNEL);
}
-static int memcpy_real_user(void __user *dest, unsigned long src, size_t count)
-{
- static char buf[4096];
- int offs = 0, size;
-
- while (offs < count) {
- size = min(sizeof(buf), count - offs);
- if (memcpy_real(buf, (void *) src + offs, size))
- return -EFAULT;
- if (copy_to_user(dest + offs, buf, size))
- return -EFAULT;
- offs += size;
- }
- return 0;
-}
-
static int __init init_cpu_info(enum arch_id arch)
{
struct save_area *sa;
@@ -346,8 +331,8 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
/* Copy from real mem */
size = count - mem_offs - hdr_count;
- rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
- size);
+ rc = copy_to_user_real(buf + hdr_count + mem_offs,
+ (void *) mem_start + mem_offs, size);
if (rc)
goto fail;
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 5c567414c4bb..4f1989d27b1f 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -29,31 +29,20 @@
/* a device matches a driver if all its slave devices match the same
* entry of the driver */
-static int
-ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
+static int ccwgroup_bus_match(struct device *dev, struct device_driver * drv)
{
- struct ccwgroup_device *gdev;
- struct ccwgroup_driver *gdrv;
-
- gdev = to_ccwgroupdev(dev);
- gdrv = to_ccwgroupdrv(drv);
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(drv);
if (gdev->creator_id == gdrv->driver_id)
return 1;
return 0;
}
-static int
-ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
-{
- /* TODO */
- return 0;
-}
static struct bus_type ccwgroup_bus_type;
-static void
-__ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
+static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
{
int i;
char str[8];
@@ -63,7 +52,6 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
sysfs_remove_link(&gdev->dev.kobj, str);
sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
}
-
}
/*
@@ -87,6 +75,87 @@ static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
}
}
+static int ccwgroup_set_online(struct ccwgroup_device *gdev)
+{
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+ int ret = 0;
+
+ if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
+ return -EAGAIN;
+ if (gdev->state == CCWGROUP_ONLINE)
+ goto out;
+ if (gdrv->set_online)
+ ret = gdrv->set_online(gdev);
+ if (ret)
+ goto out;
+
+ gdev->state = CCWGROUP_ONLINE;
+out:
+ atomic_set(&gdev->onoff, 0);
+ return ret;
+}
+
+static int ccwgroup_set_offline(struct ccwgroup_device *gdev)
+{
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+ int ret = 0;
+
+ if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
+ return -EAGAIN;
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+ if (gdrv->set_offline)
+ ret = gdrv->set_offline(gdev);
+ if (ret)
+ goto out;
+
+ gdev->state = CCWGROUP_OFFLINE;
+out:
+ atomic_set(&gdev->onoff, 0);
+ return ret;
+}
+
+static ssize_t ccwgroup_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
+ unsigned long value;
+ int ret;
+
+ if (!dev->driver)
+ return -EINVAL;
+ if (!try_module_get(gdrv->driver.owner))
+ return -EINVAL;
+
+ ret = strict_strtoul(buf, 0, &value);
+ if (ret)
+ goto out;
+
+ if (value == 1)
+ ret = ccwgroup_set_online(gdev);
+ else if (value == 0)
+ ret = ccwgroup_set_offline(gdev);
+ else
+ ret = -EINVAL;
+out:
+ module_put(gdrv->driver.owner);
+ return (ret == 0) ? count : ret;
+}
+
+static ssize_t ccwgroup_online_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ int online;
+
+ online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", online);
+}
+
/*
* Provide an 'ungroup' attribute so the user can remove group devices no
* longer needed or accidentially created. Saves memory :)
@@ -104,14 +173,13 @@ static void ccwgroup_ungroup_callback(struct device *dev)
mutex_unlock(&gdev->reg_mutex);
}
-static ssize_t
-ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t ccwgroup_ungroup_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct ccwgroup_device *gdev;
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
int rc;
- gdev = to_ccwgroupdev(dev);
-
/* Prevent concurrent online/offline processing and ungrouping. */
if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
return -EAGAIN;
@@ -132,24 +200,35 @@ out:
}
return count;
}
-
static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
+static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
-static void
-ccwgroup_release (struct device *dev)
+static struct attribute *ccwgroup_attrs[] = {
+ &dev_attr_online.attr,
+ &dev_attr_ungroup.attr,
+ NULL,
+};
+static struct attribute_group ccwgroup_attr_group = {
+ .attrs = ccwgroup_attrs,
+};
+static const struct attribute_group *ccwgroup_attr_groups[] = {
+ &ccwgroup_attr_group,
+ NULL,
+};
+
+static void ccwgroup_release(struct device *dev)
{
kfree(to_ccwgroupdev(dev));
}
-static int
-__ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
+static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
{
char str[8];
int i, rc;
for (i = 0; i < gdev->count; i++) {
- rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
- "group_device");
+ rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
+ &gdev->dev.kobj, "group_device");
if (rc) {
for (--i; i >= 0; i--)
sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
@@ -159,8 +238,8 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
}
for (i = 0; i < gdev->count; i++) {
sprintf(str, "cdev%d", i);
- rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
- str);
+ rc = sysfs_create_link(&gdev->dev.kobj,
+ &gdev->cdev[i]->dev.kobj, str);
if (rc) {
for (--i; i >= 0; i--) {
sprintf(str, "cdev%d", i);
@@ -293,26 +372,17 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
}
dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
-
+ gdev->dev.groups = ccwgroup_attr_groups;
rc = device_add(&gdev->dev);
if (rc)
goto error;
- get_device(&gdev->dev);
- rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
-
+ rc = __ccwgroup_create_symlinks(gdev);
if (rc) {
- device_unregister(&gdev->dev);
+ device_del(&gdev->dev);
goto error;
}
-
- rc = __ccwgroup_create_symlinks(gdev);
- if (!rc) {
- mutex_unlock(&gdev->reg_mutex);
- put_device(&gdev->dev);
- return 0;
- }
- device_remove_file(&gdev->dev, &dev_attr_ungroup);
- device_unregister(&gdev->dev);
+ mutex_unlock(&gdev->reg_mutex);
+ return 0;
error:
for (i = 0; i < num_devices; i++)
if (gdev->cdev[i]) {
@@ -330,7 +400,15 @@ error:
EXPORT_SYMBOL(ccwgroup_create_from_string);
static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
- void *data);
+ void *data)
+{
+ struct device *dev = data;
+
+ if (action == BUS_NOTIFY_UNBIND_DRIVER)
+ device_schedule_callback(dev, ccwgroup_ungroup_callback);
+
+ return NOTIFY_OK;
+}
static struct notifier_block ccwgroup_nb = {
.notifier_call = ccwgroup_notifier
@@ -362,138 +440,21 @@ module_exit(cleanup_ccwgroup);
/************************** driver stuff ******************************/
-static int
-ccwgroup_set_online(struct ccwgroup_device *gdev)
+static int ccwgroup_probe(struct device *dev)
{
- struct ccwgroup_driver *gdrv;
- int ret;
-
- if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
- return -EAGAIN;
- if (gdev->state == CCWGROUP_ONLINE) {
- ret = 0;
- goto out;
- }
- if (!gdev->dev.driver) {
- ret = -EINVAL;
- goto out;
- }
- gdrv = to_ccwgroupdrv (gdev->dev.driver);
- if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0))
- goto out;
-
- gdev->state = CCWGROUP_ONLINE;
- out:
- atomic_set(&gdev->onoff, 0);
- return ret;
-}
-
-static int
-ccwgroup_set_offline(struct ccwgroup_device *gdev)
-{
- struct ccwgroup_driver *gdrv;
- int ret;
-
- if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
- return -EAGAIN;
- if (gdev->state == CCWGROUP_OFFLINE) {
- ret = 0;
- goto out;
- }
- if (!gdev->dev.driver) {
- ret = -EINVAL;
- goto out;
- }
- gdrv = to_ccwgroupdrv (gdev->dev.driver);
- if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0))
- goto out;
-
- gdev->state = CCWGROUP_OFFLINE;
- out:
- atomic_set(&gdev->onoff, 0);
- return ret;
-}
-
-static ssize_t
-ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ccwgroup_device *gdev;
- struct ccwgroup_driver *gdrv;
- unsigned long value;
- int ret;
-
- if (!dev->driver)
- return -ENODEV;
-
- gdev = to_ccwgroupdev(dev);
- gdrv = to_ccwgroupdrv(dev->driver);
-
- if (!try_module_get(gdrv->driver.owner))
- return -EINVAL;
-
- ret = strict_strtoul(buf, 0, &value);
- if (ret)
- goto out;
-
- if (value == 1)
- ret = ccwgroup_set_online(gdev);
- else if (value == 0)
- ret = ccwgroup_set_offline(gdev);
- else
- ret = -EINVAL;
-out:
- module_put(gdrv->driver.owner);
- return (ret == 0) ? count : ret;
-}
-
-static ssize_t
-ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf)
-{
- int online;
-
- online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
-
- return sprintf(buf, online ? "1\n" : "0\n");
-}
-
-static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
-
-static int
-ccwgroup_probe (struct device *dev)
-{
- struct ccwgroup_device *gdev;
- struct ccwgroup_driver *gdrv;
-
- int ret;
-
- gdev = to_ccwgroupdev(dev);
- gdrv = to_ccwgroupdrv(dev->driver);
-
- if ((ret = device_create_file(dev, &dev_attr_online)))
- return ret;
-
- ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
- if (ret)
- device_remove_file(dev, &dev_attr_online);
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
- return ret;
+ return gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
}
-static int
-ccwgroup_remove (struct device *dev)
+static int ccwgroup_remove(struct device *dev)
{
- struct ccwgroup_device *gdev;
- struct ccwgroup_driver *gdrv;
-
- device_remove_file(dev, &dev_attr_online);
- device_remove_file(dev, &dev_attr_ungroup);
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
if (!dev->driver)
return 0;
-
- gdev = to_ccwgroupdev(dev);
- gdrv = to_ccwgroupdrv(dev->driver);
-
if (gdrv->remove)
gdrv->remove(gdev);
@@ -502,15 +463,11 @@ ccwgroup_remove (struct device *dev)
static void ccwgroup_shutdown(struct device *dev)
{
- struct ccwgroup_device *gdev;
- struct ccwgroup_driver *gdrv;
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
if (!dev->driver)
return;
-
- gdev = to_ccwgroupdev(dev);
- gdrv = to_ccwgroupdrv(dev->driver);
-
if (gdrv->shutdown)
gdrv->shutdown(gdev);
}
@@ -586,26 +543,12 @@ static const struct dev_pm_ops ccwgroup_pm_ops = {
static struct bus_type ccwgroup_bus_type = {
.name = "ccwgroup",
.match = ccwgroup_bus_match,
- .uevent = ccwgroup_uevent,
.probe = ccwgroup_probe,
.remove = ccwgroup_remove,
.shutdown = ccwgroup_shutdown,
.pm = &ccwgroup_pm_ops,
};
-
-static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
- void *data)
-{
- struct device *dev = data;
-
- if (action == BUS_NOTIFY_UNBIND_DRIVER)
- device_schedule_callback(dev, ccwgroup_ungroup_callback);
-
- return NOTIFY_OK;
-}
-
-
/**
* ccwgroup_driver_register() - register a ccw group driver
* @cdriver: driver to be registered
@@ -619,9 +562,9 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
return driver_register(&cdriver->driver);
}
+EXPORT_SYMBOL(ccwgroup_driver_register);
-static int
-__ccwgroup_match_all(struct device *dev, void *data)
+static int __ccwgroup_match_all(struct device *dev, void *data)
{
return 1;
}
@@ -652,6 +595,7 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
put_driver(&cdriver->driver);
driver_unregister(&cdriver->driver);
}
+EXPORT_SYMBOL(ccwgroup_driver_unregister);
/**
* ccwgroup_probe_ccwdev() - probe function for slave devices
@@ -666,6 +610,7 @@ int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
{
return 0;
}
+EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
/**
* ccwgroup_remove_ccwdev() - remove function for slave devices
@@ -702,9 +647,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
/* Release ccwgroup device reference for local processing. */
put_device(&gdev->dev);
}
-
-MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(ccwgroup_driver_register);
-EXPORT_SYMBOL(ccwgroup_driver_unregister);
-EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
index d15f8b4d78bd..5156264d0c74 100644
--- a/drivers/s390/cio/ccwreq.c
+++ b/drivers/s390/cio/ccwreq.c
@@ -1,10 +1,13 @@
/*
* Handling of internal CCW device requests.
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2011
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/types.h>
#include <linux/err.h>
#include <asm/ccwdev.h>
@@ -323,7 +326,21 @@ void ccw_request_timeout(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
- int rc;
+ int rc = -ENODEV, chp;
+
+ if (cio_update_schib(sch))
+ goto err;
+
+ for (chp = 0; chp < 8; chp++) {
+ if ((0x80 >> chp) & sch->schib.pmcw.lpum)
+ pr_warning("%s: No interrupt was received within %lus "
+ "(CS=%02x, DS=%02x, CHPID=%x.%02x)\n",
+ dev_name(&cdev->dev), req->timeout / HZ,
+ scsw_cstat(&sch->schib.scsw),
+ scsw_dstat(&sch->schib.scsw),
+ sch->schid.cssid,
+ sch->schib.pmcw.chpid[chp]);
+ }
if (!ccwreq_next_path(cdev)) {
/* set the final return code for this request */
@@ -342,7 +359,7 @@ err:
* ccw_request_notoper - notoper handler for I/O request procedure
* @cdev: ccw device
*
- * Handle timeout during I/O request procedure.
+ * Handle notoper during I/O request procedure.
*/
void ccw_request_notoper(struct ccw_device *cdev)
{
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index e950f1ad4dd1..0c87b0fc7714 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -1,7 +1,7 @@
/*
* Driver for s390 chsc subchannels
*
- * Copyright IBM Corp. 2008, 2009
+ * Copyright IBM Corp. 2008, 2011
*
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
+#include <linux/kernel_stat.h>
#include <asm/compat.h>
#include <asm/cio.h>
@@ -56,6 +57,8 @@ static void chsc_subchannel_irq(struct subchannel *sch)
CHSC_LOG(4, "irb");
CHSC_LOG_HEX(4, irb, sizeof(*irb));
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CSC]++;
+
/* Copy irb to provided request and set done. */
if (!request) {
CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n",
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index eb3140ee821e..dc67c397449e 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -622,6 +622,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (!sch) {
/* Clear pending interrupt condition. */
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
tsch(tpi_info->schid, irb);
continue;
}
@@ -634,7 +635,10 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
/* Call interrupt handler if there is one. */
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
- }
+ else
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
+ } else
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
spin_unlock(sch->lock);
/*
* Are more interrupts pending?
@@ -667,18 +671,23 @@ static int cio_tpi(void)
tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id;
if (tpi(NULL) != 1)
return 0;
+ kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++;
if (tpi_info->adapter_IO) {
do_adapter_IO(tpi_info->isc);
return 1;
}
irb = (struct irb *)&S390_lowcore.irb;
/* Store interrupt response block to lowcore. */
- if (tsch(tpi_info->schid, irb) != 0)
+ if (tsch(tpi_info->schid, irb) != 0) {
/* Not status pending or not operational. */
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
return 1;
+ }
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
- if (!sch)
+ if (!sch) {
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
return 1;
+ }
irq_context = in_interrupt();
if (!irq_context)
local_bh_disable();
@@ -687,6 +696,8 @@ static int cio_tpi(void)
memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
+ else
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
spin_unlock(sch->lock);
irq_exit();
if (!irq_context)
@@ -1058,7 +1069,7 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
{
struct subchannel_id schid;
- s390_reset_system();
+ s390_reset_system(NULL, NULL);
if (reipl_find_schid(devid, &schid) != 0)
panic("IPL Device not found\n");
do_reipl_asm(*((__u32*)&schid));
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 80ebdddf7747..33bb4d891e16 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -133,6 +133,8 @@ struct channel_subsystem {
extern struct channel_subsystem *channel_subsystems[];
+void channel_subsystem_reinit(void);
+
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 8e04c00cf0ad..d734f4a0ecac 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -21,6 +21,7 @@
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
+#include <linux/kernel_stat.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -747,6 +748,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
struct ccw_device *cdev)
{
cdev->private->cdev = cdev;
+ cdev->private->int_class = IOINT_CIO;
atomic_set(&cdev->private->onoff, 0);
cdev->dev.parent = &sch->dev;
cdev->dev.release = ccw_device_release;
@@ -1010,6 +1012,8 @@ static void io_subchannel_irq(struct subchannel *sch)
CIO_TRACE_EVENT(6, dev_name(&sch->dev));
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
+ else
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
}
void io_subchannel_init_config(struct subchannel *sch)
@@ -1621,6 +1625,7 @@ ccw_device_probe_console(void)
memset(&console_private, 0, sizeof(struct ccw_device_private));
console_cdev.private = &console_private;
console_private.cdev = &console_cdev;
+ console_private.int_class = IOINT_CIO;
ret = ccw_device_console_enable(&console_cdev, sch);
if (ret) {
cio_release_console();
@@ -1702,11 +1707,18 @@ ccw_device_probe (struct device *dev)
int ret;
cdev->drv = cdrv; /* to let the driver call _set_online */
+ /* Note: we interpret class 0 in this context as an uninitialized
+ * field since it translates to a non-I/O interrupt class. */
+ if (cdrv->int_class != 0)
+ cdev->private->int_class = cdrv->int_class;
+ else
+ cdev->private->int_class = IOINT_CIO;
ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
if (ret) {
cdev->drv = NULL;
+ cdev->private->int_class = IOINT_CIO;
return ret;
}
@@ -1740,6 +1752,7 @@ ccw_device_remove (struct device *dev)
}
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
+ cdev->private->int_class = IOINT_CIO;
return 0;
}
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 0b7245c72d5e..179824b3082f 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -5,6 +5,7 @@
#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/notifier.h>
+#include <linux/kernel_stat.h>
#include "io_sch.h"
/*
@@ -56,7 +57,17 @@ extern fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS];
static inline void
dev_fsm_event(struct ccw_device *cdev, enum dev_event dev_event)
{
- dev_jumptable[cdev->private->state][dev_event](cdev, dev_event);
+ int state = cdev->private->state;
+
+ if (dev_event == DEV_EVENT_INTERRUPT) {
+ if (state == DEV_STATE_ONLINE)
+ kstat_cpu(smp_processor_id()).
+ irqs[cdev->private->int_class]++;
+ else if (state != DEV_STATE_CMFCHANGE &&
+ state != DEV_STATE_CMFUPDATE)
+ kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++;
+ }
+ dev_jumptable[state][dev_event](cdev, dev_event);
}
/*
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index ba31ad88f4f7..2ebb492a5c17 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <asm/schid.h>
#include <asm/ccwdev.h>
+#include <asm/irq.h>
#include "css.h"
#include "orb.h"
@@ -157,6 +158,7 @@ struct ccw_device_private {
struct list_head cmb_list; /* list of measured devices */
u64 cmb_start_time; /* clock value of cmb reset */
void *cmb_wait; /* deferred cmb enable/disable */
+ enum interruption_class int_class;
};
static inline int rsch(struct subchannel_id schid)
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 3dd86441da3d..b962ffbc0803 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -18,14 +18,6 @@
#define QDIO_BUSY_BIT_RETRIES 1000 /* = 10s retry time */
#define QDIO_INPUT_THRESHOLD (500 << 12) /* 500 microseconds */
-/*
- * if an asynchronous HiperSockets queue runs full, the 10 seconds timer wait
- * till next initiative to give transmitted skbs back to the stack is too long.
- * Therefore polling is started in case of multicast queue is filled more
- * than 50 percent.
- */
-#define QDIO_IQDIO_POLL_LVL 65 /* HS multicast queue */
-
enum qdio_irq_states {
QDIO_IRQ_STATE_INACTIVE,
QDIO_IRQ_STATE_ESTABLISHED,
@@ -290,6 +282,9 @@ struct qdio_q {
/* error condition during a data transfer */
unsigned int qdio_error;
+ /* last scan of the queue */
+ u64 timestamp;
+
struct tasklet_struct tasklet;
struct qdio_queue_perf_stat q_stats;
@@ -423,31 +418,7 @@ static inline int multicast_outbound(struct qdio_q *q)
#define queue_irqs_disabled(q) \
(test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) != 0)
-#define TIQDIO_SHARED_IND 63
-
-/* device state change indicators */
-struct indicator_t {
- u32 ind; /* u32 because of compare-and-swap performance */
- atomic_t count; /* use count, 0 or 1 for non-shared indicators */
-};
-
-extern struct indicator_t *q_indicators;
-
-static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq)
-{
- return irq->nr_input_qs > 1;
-}
-
-static inline int references_shared_dsci(struct qdio_irq *irq)
-{
- return irq->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
-}
-
-static inline int shared_ind(struct qdio_q *q)
-{
- struct qdio_irq *i = q->irq_ptr;
- return references_shared_dsci(i) || has_multiple_inq_on_dsci(i);
-}
+extern u64 last_ai_time;
/* prototypes for thin interrupt */
void qdio_setup_thinint(struct qdio_irq *irq_ptr);
@@ -460,7 +431,8 @@ int tiqdio_allocate_memory(void);
void tiqdio_free_memory(void);
int tiqdio_register_thinints(void);
void tiqdio_unregister_thinints(void);
-
+void clear_nonshared_ind(struct qdio_irq *);
+int test_nonshared_ind(struct qdio_irq *);
/* prototypes for setup */
void qdio_inbound_processing(unsigned long data);
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index aaf7f935bfd3..ed68245f9741 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -54,15 +54,17 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q)
return 0;
- seq_printf(m, "DSCI: %d nr_used: %d\n",
- *(u32 *)q->irq_ptr->dsci, atomic_read(&q->nr_buf_used));
- seq_printf(m, "ftc: %d last_move: %d\n",
+ seq_printf(m, "Timestamp: %Lx Last AI: %Lx\n",
+ q->timestamp, last_ai_time);
+ seq_printf(m, "nr_used: %d ftc: %d last_move: %d\n",
+ atomic_read(&q->nr_buf_used),
q->first_to_check, q->last_move);
if (q->is_input_q) {
seq_printf(m, "polling: %d ack start: %d ack count: %d\n",
q->u.in.polling, q->u.in.ack_start,
q->u.in.ack_count);
- seq_printf(m, "IRQs disabled: %u\n",
+ seq_printf(m, "DSCI: %d IRQs disabled: %u\n",
+ *(u32 *)q->irq_ptr->dsci,
test_bit(QDIO_QUEUE_IRQS_DISABLED,
&q->u.in.queue_irq_state));
}
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 6547ff469410..3ef8d071c64a 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -15,7 +15,6 @@
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/io.h>
-#include <linux/kernel_stat.h>
#include <linux/atomic.h>
#include <asm/debug.h>
#include <asm/qdio.h>
@@ -105,9 +104,12 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
/* all done or next buffer state different */
if (ccq == 0 || ccq == 32)
return 0;
- /* not all buffers processed */
- if (ccq == 96 || ccq == 97)
+ /* no buffer processed */
+ if (ccq == 97)
return 1;
+ /* not all buffers processed */
+ if (ccq == 96)
+ return 2;
/* notify devices immediately */
DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
return -EIO;
@@ -127,10 +129,8 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int start, int count, int auto_ack)
{
+ int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
unsigned int ccq = 0;
- int tmp_count = count, tmp_start = start;
- int nr = q->nr;
- int rc;
BUG_ON(!q->irq_ptr->sch_token);
qperf_inc(q, eqbs);
@@ -141,30 +141,34 @@ again:
ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
auto_ack);
rc = qdio_check_ccq(q, ccq);
-
- /* At least one buffer was processed, return and extract the remaining
- * buffers later.
- */
- if ((ccq == 96) && (count != tmp_count)) {
- qperf_inc(q, eqbs_partial);
- return (count - tmp_count);
- }
+ if (!rc)
+ return count - tmp_count;
if (rc == 1) {
DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
goto again;
}
- if (rc < 0) {
- DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev,
- QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
- q->nr, q->first_to_kick, count,
- q->irq_ptr->int_parm);
- return 0;
+ if (rc == 2) {
+ BUG_ON(tmp_count == count);
+ qperf_inc(q, eqbs_partial);
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
+ tmp_count);
+ /*
+ * Retry once, if that fails bail out and process the
+ * extracted buffers before trying again.
+ */
+ if (!retried++)
+ goto again;
+ else
+ return count - tmp_count;
}
- return count - tmp_count;
+
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
+ 0, -1, -1, q->irq_ptr->int_parm);
+ return 0;
}
/**
@@ -197,22 +201,22 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
rc = qdio_check_ccq(q, ccq);
- if (rc == 1) {
+ if (!rc) {
+ WARN_ON(tmp_count);
+ return count - tmp_count;
+ }
+
+ if (rc == 1 || rc == 2) {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
qperf_inc(q, sqbs_partial);
goto again;
}
- if (rc < 0) {
- DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev,
- QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
- q->nr, q->first_to_kick, count,
- q->irq_ptr->int_parm);
- return 0;
- }
- WARN_ON(tmp_count);
- return count - tmp_count;
+
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
+ 0, -1, -1, q->irq_ptr->int_parm);
+ return 0;
}
/* returns number of examined buffers and their common state in *state */
@@ -277,7 +281,7 @@ static inline int set_buf_state(struct qdio_q *q, int bufnr,
}
/* set slsb states to initial state */
-void qdio_init_buf_states(struct qdio_irq *irq_ptr)
+static void qdio_init_buf_states(struct qdio_irq *irq_ptr)
{
struct qdio_q *q;
int i;
@@ -446,7 +450,7 @@ static void process_buffer_error(struct qdio_q *q, int count)
qperf_inc(q, target_full);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x",
q->first_to_check);
- return;
+ goto set;
}
DBF_ERROR("%4x BUF ERROR", SCH_NO(q));
@@ -456,6 +460,7 @@ static void process_buffer_error(struct qdio_q *q, int count)
q->sbal[q->first_to_check]->element[14].sflags,
q->sbal[q->first_to_check]->element[15].sflags);
+set:
/*
* Interrupts may be avoided as long as the error is present
* so change the buffer state immediately to avoid starvation.
@@ -513,6 +518,8 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
int count, stop;
unsigned char state = 0;
+ q->timestamp = get_clock_fast();
+
/*
* Don't check 128 buffers, as otherwise qdio_inbound_q_moved
* would return 0.
@@ -782,6 +789,8 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
int count, stop;
unsigned char state = 0;
+ q->timestamp = get_clock_fast();
+
if (need_siga_sync(q))
if (((queue_type(q) != QDIO_IQDIO_QFMT) &&
!pci_out_supported(q)) ||
@@ -912,21 +921,13 @@ static void __qdio_outbound_processing(struct qdio_q *q)
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
goto sched;
- /* bail out for HiperSockets unicast queues */
- if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q))
- return;
-
- if ((queue_type(q) == QDIO_IQDIO_QFMT) &&
- (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL)
- goto sched;
-
if (q->u.out.pci_out_enabled)
return;
/*
* Now we know that queue type is either qeth without pci enabled
- * or HiperSockets multicast. Make sure buffer switch from PRIMED to
- * EMPTY is noticed and outbound_handler is called after some time.
+ * or HiperSockets. Make sure buffer switch from PRIMED to EMPTY
+ * is noticed and outbound_handler is called after some time.
*/
if (qdio_outbound_q_done(q))
del_timer(&q->u.out.timer);
@@ -1128,7 +1129,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
- kstat_cpu(smp_processor_id()).irqs[IOINT_QDI]++;
if (irq_ptr->perf_stat_enabled)
irq_ptr->perf_stat.qdio_int++;
@@ -1719,9 +1719,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
WARN_ON(queue_irqs_enabled(q));
- if (!shared_ind(q))
- xchg(q->irq_ptr->dsci, 0);
-
+ clear_nonshared_ind(irq_ptr);
qdio_stop_polling(q);
clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state);
@@ -1729,7 +1727,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
* We need to check again to not lose initiative after
* resetting the ACK state.
*/
- if (!shared_ind(q) && *q->irq_ptr->dsci)
+ if (test_nonshared_ind(irq_ptr))
goto rescan;
if (!qdio_inbound_q_done(q))
goto rescan;
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index a3e3949d7b69..011eadea3ee4 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -26,17 +26,24 @@
*/
#define TIQDIO_NR_NONSHARED_IND 63
#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
+#define TIQDIO_SHARED_IND 63
+
+/* device state change indicators */
+struct indicator_t {
+ u32 ind; /* u32 because of compare-and-swap performance */
+ atomic_t count; /* use count, 0 or 1 for non-shared indicators */
+};
/* list of thin interrupt input queues */
static LIST_HEAD(tiq_list);
-DEFINE_MUTEX(tiq_list_lock);
+static DEFINE_MUTEX(tiq_list_lock);
/* adapter local summary indicator */
static u8 *tiqdio_alsi;
-struct indicator_t *q_indicators;
+static struct indicator_t *q_indicators;
-static u64 last_ai_time;
+u64 last_ai_time;
/* returns addr for the device state change indicator */
static u32 *get_indicator(void)
@@ -90,6 +97,43 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
synchronize_rcu();
}
+static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
+{
+ return irq_ptr->nr_input_qs > 1;
+}
+
+static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
+{
+ return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+}
+
+static inline int shared_ind(struct qdio_irq *irq_ptr)
+{
+ return references_shared_dsci(irq_ptr) ||
+ has_multiple_inq_on_dsci(irq_ptr);
+}
+
+void clear_nonshared_ind(struct qdio_irq *irq_ptr)
+{
+ if (!is_thinint_irq(irq_ptr))
+ return;
+ if (shared_ind(irq_ptr))
+ return;
+ xchg(irq_ptr->dsci, 0);
+}
+
+int test_nonshared_ind(struct qdio_irq *irq_ptr)
+{
+ if (!is_thinint_irq(irq_ptr))
+ return 0;
+ if (shared_ind(irq_ptr))
+ return 0;
+ if (*irq_ptr->dsci)
+ return 1;
+ else
+ return 0;
+}
+
static inline u32 clear_shared_ind(void)
{
if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
@@ -119,7 +163,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
q->irq_ptr->int_parm);
} else {
- if (!shared_ind(q))
+ if (!shared_ind(q->irq_ptr))
xchg(q->irq_ptr->dsci, 0);
/*
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index aec60d55b10d..3c2c923d5c0a 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -33,7 +33,7 @@
* The pointer to our (page) of device descriptions.
*/
static void *kvm_devices;
-struct work_struct hotplug_work;
+static struct work_struct hotplug_work;
struct kvm_device {
struct virtio_device vdev;
@@ -334,10 +334,10 @@ static void scan_devices(void)
*/
static int match_desc(struct device *dev, void *data)
{
- if ((ulong)to_kvmdev(dev_to_virtio(dev))->desc == (ulong)data)
- return 1;
+ struct virtio_device *vdev = dev_to_virtio(dev);
+ struct kvm_device *kdev = to_kvmdev(vdev);
- return 0;
+ return kdev->desc == data;
}
/*
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index f1fa2483ae6b..b41fae37d3af 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -63,7 +63,6 @@
#define KMSG_COMPONENT "claw"
-#include <linux/kernel_stat.h>
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
#include <asm/debug.h>
@@ -291,6 +290,7 @@ static struct ccw_driver claw_ccw_driver = {
.ids = claw_ids,
.probe = ccwgroup_probe_ccwdev,
.remove = ccwgroup_remove_ccwdev,
+ .int_class = IOINT_CLW,
};
static ssize_t
@@ -645,7 +645,6 @@ claw_irq_handler(struct ccw_device *cdev,
struct claw_env *p_env;
struct chbk *p_ch_r=NULL;
- kstat_cpu(smp_processor_id()).irqs[IOINT_CLW]++;
CLAW_DBF_TEXT(4, trace, "clawirq");
/* Bypass all 'unsolicited interrupts' */
privptr = dev_get_drvdata(&cdev->dev);
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 426787efc492..5cb93a8e3403 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -24,7 +24,6 @@
#define KMSG_COMPONENT "ctcm"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -1203,7 +1202,6 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
int cstat;
int dstat;
- kstat_cpu(smp_processor_id()).irqs[IOINT_CTC]++;
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
"Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev));
@@ -1769,6 +1767,7 @@ static struct ccw_driver ctcm_ccw_driver = {
.ids = ctcm_ids,
.probe = ccwgroup_probe_ccwdev,
.remove = ccwgroup_remove_ccwdev,
+ .int_class = IOINT_CTC,
};
static struct ccwgroup_driver ctcm_group_driver = {
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
index 8305319b2a84..650aec1839e9 100644
--- a/drivers/s390/net/ctcm_sysfs.c
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -159,7 +159,7 @@ static ssize_t ctcm_proto_store(struct device *dev,
return count;
}
-const char *ctcm_type[] = {
+static const char *ctcm_type[] = {
"not a channel",
"CTC/A",
"FICON channel",
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index fb246b944b16..c28713da1ec5 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -26,7 +26,6 @@
#define KMSG_COMPONENT "lcs"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
@@ -1399,7 +1398,6 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
int rc, index;
int cstat, dstat;
- kstat_cpu(smp_processor_id()).irqs[IOINT_LCS]++;
if (lcs_check_irb_error(cdev, irb))
return;
@@ -1972,7 +1970,7 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char
static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store);
-const char *lcs_type[] = {
+static const char *lcs_type[] = {
"not a channel",
"2216 parallel",
"2216 channel",
@@ -2399,6 +2397,7 @@ static struct ccw_driver lcs_ccw_driver = {
.ids = lcs_ids,
.probe = ccwgroup_probe_ccwdev,
.remove = ccwgroup_remove_ccwdev,
+ .int_class = IOINT_LCS,
};
/**
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index ce735204d317..e4c1176ee25b 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1415,7 +1415,7 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card)
return 0;
}
-int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
+static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
{
int rc = 0;
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index e0119377ffe3..e76107b2ade3 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -3337,6 +3337,13 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= PCI_D0;
pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+ /*
+ * The P600 requires a small delay when changing states.
+ * Otherwise we may think the board did not reset and we bail.
+ * This for kdump only and is particular to the P600.
+ */
+ msleep(500);
}
return 0;
}
diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c
index 33b2ed451e09..e0ada3773786 100644
--- a/drivers/sh/intc/chip.c
+++ b/drivers/sh/intc/chip.c
@@ -202,11 +202,16 @@ static int intc_set_type(struct irq_data *data, unsigned int type)
if (!value)
return -EINVAL;
+ value &= ~SENSE_VALID_FLAG;
+
ihp = intc_find_irq(d->sense, d->nr_sense, irq);
if (ihp) {
+ /* PINT has 2-bit sense registers, should fail on EDGE_BOTH */
+ if (value >= (1 << _INTC_WIDTH(ihp->handle)))
+ return -EINVAL;
+
addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
- intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle,
- value & ~SENSE_VALID_FLAG);
+ intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
}
return 0;
diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c
index e32304b66cf1..56bf9336b92b 100644
--- a/drivers/sh/intc/userimask.c
+++ b/drivers/sh/intc/userimask.c
@@ -13,6 +13,7 @@
#include <linux/sysdev.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/stat.h>
#include <asm/sizes.h>
#include "internals.h"
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
index 75934e3ea34e..e67fe170d8d5 100644
--- a/drivers/sh/pfc.c
+++ b/drivers/sh/pfc.c
@@ -217,7 +217,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
if (!r_width)
break;
- for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) {
+ for (n = 0; n < (r_width / f_width) * (1 << f_width); n++) {
if (config_reg->enum_ids[n] == enum_id) {
*crp = config_reg;
*indexp = n;
@@ -577,6 +577,32 @@ static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
}
+static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ pinmux_enum_t enum_id;
+ pinmux_enum_t *enum_ids;
+ int i, k, pos;
+
+ pos = 0;
+ enum_id = 0;
+ while (1) {
+ pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id);
+ if (pos <= 0 || !enum_id)
+ break;
+
+ for (i = 0; i < gpioc->gpio_irq_size; i++) {
+ enum_ids = gpioc->gpio_irq[i].enum_ids;
+ for (k = 0; enum_ids[k]; k++) {
+ if (enum_ids[k] == enum_id)
+ return gpioc->gpio_irq[i].irq;
+ }
+ }
+ }
+
+ return -ENOSYS;
+}
+
int register_pinmux(struct pinmux_info *pip)
{
struct gpio_chip *chip = &pip->chip;
@@ -592,6 +618,7 @@ int register_pinmux(struct pinmux_info *pip)
chip->get = sh_gpio_get;
chip->direction_output = sh_gpio_direction_output;
chip->set = sh_gpio_set;
+ chip->to_irq = sh_gpio_to_irq;
WARN_ON(pip->first_gpio != 0); /* needs testing */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 52e2900d9d8e..a1fd73df5416 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -88,7 +88,7 @@ config SPI_BFIN_SPORT
config SPI_AU1550
tristate "Au1550/Au12x0 SPI Controller"
- depends on (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL
+ depends on MIPS_ALCHEMY && EXPERIMENTAL
select SPI_BITBANG
help
If you say yes to this option, support will be included for the
diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c
index 4813a63ce6fb..881c1967741d 100644
--- a/drivers/spi/spi-altera.c
+++ b/drivers/spi/spi-altera.c
@@ -320,18 +320,7 @@ static struct platform_driver altera_spi_driver = {
.of_match_table = altera_spi_match,
},
};
-
-static int __init altera_spi_init(void)
-{
- return platform_driver_register(&altera_spi_driver);
-}
-module_init(altera_spi_init);
-
-static void __exit altera_spi_exit(void)
-{
- platform_driver_unregister(&altera_spi_driver);
-}
-module_exit(altera_spi_exit);
+module_platform_driver(altera_spi_driver);
MODULE_DESCRIPTION("Altera SPI driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index 03019bf5a5e9..024b48aed5ca 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -273,18 +273,7 @@ static struct platform_driver ath79_spi_driver = {
.owner = THIS_MODULE,
},
};
-
-static __init int ath79_spi_init(void)
-{
- return platform_driver_register(&ath79_spi_driver);
-}
-module_init(ath79_spi_init);
-
-static __exit void ath79_spi_exit(void)
-{
- platform_driver_unregister(&ath79_spi_driver);
-}
-module_exit(ath79_spi_exit);
+module_platform_driver(ath79_spi_driver);
MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index d3bff424286f..79665e2e6ec5 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1074,18 +1074,7 @@ static struct platform_driver atmel_spi_driver = {
.resume = atmel_spi_resume,
.remove = __exit_p(atmel_spi_remove),
};
-
-static int __init atmel_spi_init(void)
-{
- return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe);
-}
-module_init(atmel_spi_init);
-
-static void __exit atmel_spi_exit(void)
-{
- platform_driver_unregister(&atmel_spi_driver);
-}
-module_exit(atmel_spi_exit);
+module_platform_driver(atmel_spi_driver);
MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver");
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c
index e557ff617b11..248a2cc671a9 100644
--- a/drivers/spi/spi-bfin-sport.c
+++ b/drivers/spi/spi-bfin-sport.c
@@ -938,15 +938,4 @@ static struct platform_driver bfin_sport_spi_driver = {
.suspend = bfin_sport_spi_suspend,
.resume = bfin_sport_spi_resume,
};
-
-static int __init bfin_sport_spi_init(void)
-{
- return platform_driver_register(&bfin_sport_spi_driver);
-}
-module_init(bfin_sport_spi_init);
-
-static void __exit bfin_sport_spi_exit(void)
-{
- platform_driver_unregister(&bfin_sport_spi_driver);
-}
-module_exit(bfin_sport_spi_exit);
+module_platform_driver(bfin_sport_spi_driver);
diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c
index b8d25f2b7038..3b83ff8b1e2b 100644
--- a/drivers/spi/spi-bfin5xx.c
+++ b/drivers/spi/spi-bfin5xx.c
@@ -1098,7 +1098,7 @@ static int bfin_spi_setup(struct spi_device *spi)
if (chip->pio_interrupt && !drv_data->irq_requested) {
ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler,
- IRQF_DISABLED, "BFIN_SPI", drv_data);
+ 0, "BFIN_SPI", drv_data);
if (ret) {
dev_err(&spi->dev, "Unable to register spi IRQ\n");
goto error;
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index ae2cd1c1fda8..6eee64a5d240 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -487,7 +487,7 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev)
goto fail2;
}
- status = request_irq(mcfqspi->irq, mcfqspi_irq_handler, IRQF_DISABLED,
+ status = request_irq(mcfqspi->irq, mcfqspi_irq_handler, 0,
pdev->name, mcfqspi);
if (status) {
dev_dbg(&pdev->dev, "request_irq failed\n");
@@ -621,20 +621,10 @@ static struct platform_driver mcfqspi_driver = {
.driver.name = DRIVER_NAME,
.driver.owner = THIS_MODULE,
.driver.pm = MCFQSPI_DEV_PM_OPS,
+ .probe = mcfqspi_probe,
.remove = __devexit_p(mcfqspi_remove),
};
-
-static int __init mcfqspi_init(void)
-{
- return platform_driver_probe(&mcfqspi_driver, mcfqspi_probe);
-}
-module_init(mcfqspi_init);
-
-static void __exit mcfqspi_exit(void)
-{
- platform_driver_unregister(&mcfqspi_driver);
-}
-module_exit(mcfqspi_exit);
+module_platform_driver(mcfqspi_driver);
MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
MODULE_DESCRIPTION("Coldfire QSPI Controller Driver");
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 1f0ed8005c91..31bfba805cf4 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -799,7 +799,7 @@ rx_dma_failed:
* It will invoke spi_bitbang_start to create work queue so that client driver
* can register transfer method to work queue.
*/
-static int davinci_spi_probe(struct platform_device *pdev)
+static int __devinit davinci_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct davinci_spi *dspi;
@@ -984,7 +984,7 @@ err:
* It will also call spi_bitbang_stop to destroy the work queue which was
* created by spi_bitbang_start.
*/
-static int __exit davinci_spi_remove(struct platform_device *pdev)
+static int __devexit davinci_spi_remove(struct platform_device *pdev)
{
struct davinci_spi *dspi;
struct spi_master *master;
@@ -1011,20 +1011,10 @@ static struct platform_driver davinci_spi_driver = {
.name = "spi_davinci",
.owner = THIS_MODULE,
},
- .remove = __exit_p(davinci_spi_remove),
+ .probe = davinci_spi_probe,
+ .remove = __devexit_p(davinci_spi_remove),
};
-
-static int __init davinci_spi_init(void)
-{
- return platform_driver_probe(&davinci_spi_driver, davinci_spi_probe);
-}
-module_init(davinci_spi_init);
-
-static void __exit davinci_spi_exit(void)
-{
- platform_driver_unregister(&davinci_spi_driver);
-}
-module_exit(davinci_spi_exit);
+module_platform_driver(davinci_spi_driver);
MODULE_DESCRIPTION("TI DaVinci SPI Master Controller Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index 130e55537db6..e743a45ee92c 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -116,13 +116,13 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
/* 1. setup DMA related registers */
if (cs_change) {
spi_enable_chip(dws, 0);
- dw_writew(dws, dmardlr, 0xf);
- dw_writew(dws, dmatdlr, 0x10);
+ dw_writew(dws, DW_SPI_DMARDLR, 0xf);
+ dw_writew(dws, DW_SPI_DMATDLR, 0x10);
if (dws->tx_dma)
dma_ctrl |= 0x2;
if (dws->rx_dma)
dma_ctrl |= 0x1;
- dw_writew(dws, dmacr, dma_ctrl);
+ dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
spi_enable_chip(dws, 1);
}
@@ -200,7 +200,8 @@ static struct dw_spi_dma_ops mid_dma_ops = {
int dw_spi_mid_init(struct dw_spi *dws)
{
- u32 *clk_reg, clk_cdiv;
+ void __iomem *clk_reg;
+ u32 clk_cdiv;
clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16);
if (!clk_reg)
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 34eb66501dbf..fac399c3022c 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -127,24 +127,14 @@ static int __devexit dw_spi_mmio_remove(struct platform_device *pdev)
}
static struct platform_driver dw_spi_mmio_driver = {
+ .probe = dw_spi_mmio_probe,
.remove = __devexit_p(dw_spi_mmio_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
-
-static int __init dw_spi_mmio_init(void)
-{
- return platform_driver_probe(&dw_spi_mmio_driver, dw_spi_mmio_probe);
-}
-module_init(dw_spi_mmio_init);
-
-static void __exit dw_spi_mmio_exit(void)
-{
- platform_driver_unregister(&dw_spi_mmio_driver);
-}
-module_exit(dw_spi_mmio_exit);
+module_platform_driver(dw_spi_mmio_driver);
MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>");
MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 857cd30b44bb..296d94f4cf72 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -88,35 +88,35 @@ static ssize_t spi_show_regs(struct file *file, char __user *user_buf,
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
"=================================\n");
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "CTRL0: \t\t0x%08x\n", dw_readl(dws, ctrl0));
+ "CTRL0: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL0));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "CTRL1: \t\t0x%08x\n", dw_readl(dws, ctrl1));
+ "CTRL1: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL1));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "SSIENR: \t0x%08x\n", dw_readl(dws, ssienr));
+ "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "SER: \t\t0x%08x\n", dw_readl(dws, ser));
+ "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "BAUDR: \t\t0x%08x\n", dw_readl(dws, baudr));
+ "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "TXFTLR: \t0x%08x\n", dw_readl(dws, txfltr));
+ "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFLTR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "RXFTLR: \t0x%08x\n", dw_readl(dws, rxfltr));
+ "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFLTR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "TXFLR: \t\t0x%08x\n", dw_readl(dws, txflr));
+ "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "RXFLR: \t\t0x%08x\n", dw_readl(dws, rxflr));
+ "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "SR: \t\t0x%08x\n", dw_readl(dws, sr));
+ "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "IMR: \t\t0x%08x\n", dw_readl(dws, imr));
+ "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "ISR: \t\t0x%08x\n", dw_readl(dws, isr));
+ "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "DMACR: \t\t0x%08x\n", dw_readl(dws, dmacr));
+ "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "DMATDLR: \t0x%08x\n", dw_readl(dws, dmatdlr));
+ "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
- "DMARDLR: \t0x%08x\n", dw_readl(dws, dmardlr));
+ "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR));
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
"=================================\n");
@@ -166,7 +166,7 @@ static inline u32 tx_max(struct dw_spi *dws)
u32 tx_left, tx_room, rxtx_gap;
tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
- tx_room = dws->fifo_len - dw_readw(dws, txflr);
+ tx_room = dws->fifo_len - dw_readw(dws, DW_SPI_TXFLR);
/*
* Another concern is about the tx/rx mismatch, we
@@ -187,7 +187,7 @@ static inline u32 rx_max(struct dw_spi *dws)
{
u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
- return min(rx_left, (u32)dw_readw(dws, rxflr));
+ return min(rx_left, (u32)dw_readw(dws, DW_SPI_RXFLR));
}
static void dw_writer(struct dw_spi *dws)
@@ -203,7 +203,7 @@ static void dw_writer(struct dw_spi *dws)
else
txw = *(u16 *)(dws->tx);
}
- dw_writew(dws, dr, txw);
+ dw_writew(dws, DW_SPI_DR, txw);
dws->tx += dws->n_bytes;
}
}
@@ -214,7 +214,7 @@ static void dw_reader(struct dw_spi *dws)
u16 rxw;
while (max--) {
- rxw = dw_readw(dws, dr);
+ rxw = dw_readw(dws, DW_SPI_DR);
/* Care rx only if the transfer's original "rx" is not null */
if (dws->rx_end - dws->len) {
if (dws->n_bytes == 1)
@@ -322,13 +322,13 @@ EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
static irqreturn_t interrupt_transfer(struct dw_spi *dws)
{
- u16 irq_status = dw_readw(dws, isr);
+ u16 irq_status = dw_readw(dws, DW_SPI_ISR);
/* Error handling */
if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
- dw_readw(dws, txoicr);
- dw_readw(dws, rxoicr);
- dw_readw(dws, rxuicr);
+ dw_readw(dws, DW_SPI_TXOICR);
+ dw_readw(dws, DW_SPI_RXOICR);
+ dw_readw(dws, DW_SPI_RXUICR);
int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
return IRQ_HANDLED;
}
@@ -352,7 +352,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
struct dw_spi *dws = dev_id;
- u16 irq_status = dw_readw(dws, isr) & 0x3f;
+ u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f;
if (!irq_status)
return IRQ_NONE;
@@ -520,11 +520,11 @@ static void pump_transfers(unsigned long data)
* 2. clk_div is changed
* 3. control value changes
*/
- if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) {
+ if (dw_readw(dws, DW_SPI_CTRL0) != cr0 || cs_change || clk_div || imask) {
spi_enable_chip(dws, 0);
- if (dw_readw(dws, ctrl0) != cr0)
- dw_writew(dws, ctrl0, cr0);
+ if (dw_readw(dws, DW_SPI_CTRL0) != cr0)
+ dw_writew(dws, DW_SPI_CTRL0, cr0);
spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
spi_chip_sel(dws, spi->chip_select);
@@ -534,7 +534,7 @@ static void pump_transfers(unsigned long data)
if (imask)
spi_umask_intr(dws, imask);
if (txint_level)
- dw_writew(dws, txfltr, txint_level);
+ dw_writew(dws, DW_SPI_TXFLTR, txint_level);
spi_enable_chip(dws, 1);
if (cs_change)
@@ -790,13 +790,13 @@ static void spi_hw_init(struct dw_spi *dws)
if (!dws->fifo_len) {
u32 fifo;
for (fifo = 2; fifo <= 257; fifo++) {
- dw_writew(dws, txfltr, fifo);
- if (fifo != dw_readw(dws, txfltr))
+ dw_writew(dws, DW_SPI_TXFLTR, fifo);
+ if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
break;
}
dws->fifo_len = (fifo == 257) ? 0 : fifo;
- dw_writew(dws, txfltr, 0);
+ dw_writew(dws, DW_SPI_TXFLTR, 0);
}
}
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 8b7b07bf6c3f..9c57c078031e 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -4,6 +4,33 @@
#include <linux/io.h>
#include <linux/scatterlist.h>
+/* Register offsets */
+#define DW_SPI_CTRL0 0x00
+#define DW_SPI_CTRL1 0x04
+#define DW_SPI_SSIENR 0x08
+#define DW_SPI_MWCR 0x0c
+#define DW_SPI_SER 0x10
+#define DW_SPI_BAUDR 0x14
+#define DW_SPI_TXFLTR 0x18
+#define DW_SPI_RXFLTR 0x1c
+#define DW_SPI_TXFLR 0x20
+#define DW_SPI_RXFLR 0x24
+#define DW_SPI_SR 0x28
+#define DW_SPI_IMR 0x2c
+#define DW_SPI_ISR 0x30
+#define DW_SPI_RISR 0x34
+#define DW_SPI_TXOICR 0x38
+#define DW_SPI_RXOICR 0x3c
+#define DW_SPI_RXUICR 0x40
+#define DW_SPI_MSTICR 0x44
+#define DW_SPI_ICR 0x48
+#define DW_SPI_DMACR 0x4c
+#define DW_SPI_DMATDLR 0x50
+#define DW_SPI_DMARDLR 0x54
+#define DW_SPI_IDR 0x58
+#define DW_SPI_VERSION 0x5c
+#define DW_SPI_DR 0x60
+
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
@@ -55,35 +82,6 @@ enum dw_ssi_type {
SSI_NS_MICROWIRE,
};
-struct dw_spi_reg {
- u32 ctrl0;
- u32 ctrl1;
- u32 ssienr;
- u32 mwcr;
- u32 ser;
- u32 baudr;
- u32 txfltr;
- u32 rxfltr;
- u32 txflr;
- u32 rxflr;
- u32 sr;
- u32 imr;
- u32 isr;
- u32 risr;
- u32 txoicr;
- u32 rxoicr;
- u32 rxuicr;
- u32 msticr;
- u32 icr;
- u32 dmacr;
- u32 dmatdlr;
- u32 dmardlr;
- u32 idr;
- u32 version;
- u32 dr; /* Currently oper as 32 bits,
- though only low 16 bits matters */
-} __packed;
-
struct dw_spi;
struct dw_spi_dma_ops {
int (*dma_init)(struct dw_spi *dws);
@@ -161,23 +159,34 @@ struct dw_spi {
#endif
};
-#define dw_readl(dw, name) \
- __raw_readl(&(((struct dw_spi_reg *)dw->regs)->name))
-#define dw_writel(dw, name, val) \
- __raw_writel((val), &(((struct dw_spi_reg *)dw->regs)->name))
-#define dw_readw(dw, name) \
- __raw_readw(&(((struct dw_spi_reg *)dw->regs)->name))
-#define dw_writew(dw, name, val) \
- __raw_writew((val), &(((struct dw_spi_reg *)dw->regs)->name))
+static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
+{
+ return __raw_readl(dws->regs + offset);
+}
+
+static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
+{
+ __raw_writel(val, dws->regs + offset);
+}
+
+static inline u16 dw_readw(struct dw_spi *dws, u32 offset)
+{
+ return __raw_readw(dws->regs + offset);
+}
+
+static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val)
+{
+ __raw_writew(val, dws->regs + offset);
+}
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
{
- dw_writel(dws, ssienr, (enable ? 1 : 0));
+ dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
}
static inline void spi_set_clk(struct dw_spi *dws, u16 div)
{
- dw_writel(dws, baudr, div);
+ dw_writel(dws, DW_SPI_BAUDR, div);
}
static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
@@ -188,7 +197,7 @@ static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
if (dws->cs_control)
dws->cs_control(1);
- dw_writel(dws, ser, 1 << cs);
+ dw_writel(dws, DW_SPI_SER, 1 << cs);
}
/* Disable IRQ bits */
@@ -196,8 +205,8 @@ static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
- new_mask = dw_readl(dws, imr) & ~mask;
- dw_writel(dws, imr, new_mask);
+ new_mask = dw_readl(dws, DW_SPI_IMR) & ~mask;
+ dw_writel(dws, DW_SPI_IMR, new_mask);
}
/* Enable IRQ bits */
@@ -205,8 +214,8 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
- new_mask = dw_readl(dws, imr) | mask;
- dw_writel(dws, imr, new_mask);
+ new_mask = dw_readl(dws, DW_SPI_IMR) | mask;
+ dw_writel(dws, DW_SPI_IMR, new_mask);
}
/*
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index 1cf645479bfe..0a282e5fcc9c 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -24,6 +24,7 @@
#include <linux/dmaengine.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
@@ -1025,7 +1026,7 @@ static void ep93xx_spi_release_dma(struct ep93xx_spi *espi)
free_page((unsigned long)espi->zeropage);
}
-static int __init ep93xx_spi_probe(struct platform_device *pdev)
+static int __devinit ep93xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct ep93xx_spi_info *info;
@@ -1150,7 +1151,7 @@ fail_release_master:
return error;
}
-static int __exit ep93xx_spi_remove(struct platform_device *pdev)
+static int __devexit ep93xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct ep93xx_spi *espi = spi_master_get_devdata(master);
@@ -1196,20 +1197,10 @@ static struct platform_driver ep93xx_spi_driver = {
.name = "ep93xx-spi",
.owner = THIS_MODULE,
},
- .remove = __exit_p(ep93xx_spi_remove),
+ .probe = ep93xx_spi_probe,
+ .remove = __devexit_p(ep93xx_spi_remove),
};
-
-static int __init ep93xx_spi_init(void)
-{
- return platform_driver_probe(&ep93xx_spi_driver, ep93xx_spi_probe);
-}
-module_init(ep93xx_spi_init);
-
-static void __exit ep93xx_spi_exit(void)
-{
- platform_driver_unregister(&ep93xx_spi_driver);
-}
-module_exit(ep93xx_spi_exit);
+module_platform_driver(ep93xx_spi_driver);
MODULE_DESCRIPTION("EP93xx SPI Controller driver");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 54e499d5f92c..d770f03705c3 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -744,18 +744,7 @@ static struct platform_driver fsl_espi_driver = {
.probe = of_fsl_espi_probe,
.remove = __devexit_p(of_fsl_espi_remove),
};
-
-static int __init fsl_espi_init(void)
-{
- return platform_driver_register(&fsl_espi_driver);
-}
-module_init(fsl_espi_init);
-
-static void __exit fsl_espi_exit(void)
-{
- platform_driver_unregister(&fsl_espi_driver);
-}
-module_exit(fsl_espi_exit);
+module_platform_driver(fsl_espi_driver);
MODULE_AUTHOR("Mingkai Hu");
MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 0e88ab745490..635ff08b377f 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -311,7 +311,7 @@ done:
return value;
}
-static int __init spi_gpio_probe(struct platform_device *pdev)
+static int __devinit spi_gpio_probe(struct platform_device *pdev)
{
int status;
struct spi_master *master;
@@ -379,7 +379,7 @@ gpio_free:
return status;
}
-static int __exit spi_gpio_remove(struct platform_device *pdev)
+static int __devexit spi_gpio_remove(struct platform_device *pdev)
{
struct spi_gpio *spi_gpio;
struct spi_gpio_platform_data *pdata;
@@ -408,21 +408,10 @@ MODULE_ALIAS("platform:" DRIVER_NAME);
static struct platform_driver spi_gpio_driver = {
.driver.name = DRIVER_NAME,
.driver.owner = THIS_MODULE,
- .remove = __exit_p(spi_gpio_remove),
+ .probe = spi_gpio_probe,
+ .remove = __devexit_p(spi_gpio_remove),
};
-
-static int __init spi_gpio_init(void)
-{
- return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe);
-}
-module_init(spi_gpio_init);
-
-static void __exit spi_gpio_exit(void)
-{
- platform_driver_unregister(&spi_gpio_driver);
-}
-module_exit(spi_gpio_exit);
-
+module_platform_driver(spi_gpio_driver);
MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO ");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index fa594d604aca..c6e697f5e007 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -929,19 +929,7 @@ static struct platform_driver spi_imx_driver = {
.probe = spi_imx_probe,
.remove = __devexit_p(spi_imx_remove),
};
-
-static int __init spi_imx_init(void)
-{
- return platform_driver_register(&spi_imx_driver);
-}
-
-static void __exit spi_imx_exit(void)
-{
- platform_driver_unregister(&spi_imx_driver);
-}
-
-module_init(spi_imx_init);
-module_exit(spi_imx_exit);
+module_platform_driver(spi_imx_driver);
MODULE_DESCRIPTION("SPI Master Controller driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 6a5b4238fb6b..4c63f772780a 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -559,18 +559,7 @@ static struct platform_driver mpc512x_psc_spi_of_driver = {
.of_match_table = mpc512x_psc_spi_of_match,
},
};
-
-static int __init mpc512x_psc_spi_init(void)
-{
- return platform_driver_register(&mpc512x_psc_spi_of_driver);
-}
-module_init(mpc512x_psc_spi_init);
-
-static void __exit mpc512x_psc_spi_exit(void)
-{
- platform_driver_unregister(&mpc512x_psc_spi_of_driver);
-}
-module_exit(mpc512x_psc_spi_exit);
+module_platform_driver(mpc512x_psc_spi_of_driver);
MODULE_AUTHOR("John Rigby");
MODULE_DESCRIPTION("MPC512x PSC SPI Driver");
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index e30baf0852ac..66047156d90d 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -511,18 +511,7 @@ static struct platform_driver mpc52xx_psc_spi_of_driver = {
.of_match_table = mpc52xx_psc_spi_of_match,
},
};
-
-static int __init mpc52xx_psc_spi_init(void)
-{
- return platform_driver_register(&mpc52xx_psc_spi_of_driver);
-}
-module_init(mpc52xx_psc_spi_init);
-
-static void __exit mpc52xx_psc_spi_exit(void)
-{
- platform_driver_unregister(&mpc52xx_psc_spi_of_driver);
-}
-module_exit(mpc52xx_psc_spi_exit);
+module_platform_driver(mpc52xx_psc_spi_of_driver);
MODULE_AUTHOR("Dragos Carp");
MODULE_DESCRIPTION("MPC52xx PSC SPI Driver");
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index 015a974bed72..57633d963456 100644
--- a/drivers/spi/spi-mpc52xx.c
+++ b/drivers/spi/spi-mpc52xx.c
@@ -564,16 +564,4 @@ static struct platform_driver mpc52xx_spi_of_driver = {
.probe = mpc52xx_spi_probe,
.remove = __devexit_p(mpc52xx_spi_remove),
};
-
-static int __init mpc52xx_spi_init(void)
-{
- return platform_driver_register(&mpc52xx_spi_of_driver);
-}
-module_init(mpc52xx_spi_init);
-
-static void __exit mpc52xx_spi_exit(void)
-{
- platform_driver_unregister(&mpc52xx_spi_of_driver);
-}
-module_exit(mpc52xx_spi_exit);
-
+module_platform_driver(mpc52xx_spi_of_driver);
diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c
index c0a6ce81f9c0..e763254741c2 100644
--- a/drivers/spi/spi-nuc900.c
+++ b/drivers/spi/spi-nuc900.c
@@ -484,19 +484,7 @@ static struct platform_driver nuc900_spi_driver = {
.owner = THIS_MODULE,
},
};
-
-static int __init nuc900_spi_init(void)
-{
- return platform_driver_register(&nuc900_spi_driver);
-}
-
-static void __exit nuc900_spi_exit(void)
-{
- platform_driver_unregister(&nuc900_spi_driver);
-}
-
-module_init(nuc900_spi_init);
-module_exit(nuc900_spi_exit);
+module_platform_driver(nuc900_spi_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("nuc900 spi driver!");
diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c
index f1bde66cea19..897274e8715c 100644
--- a/drivers/spi/spi-oc-tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
@@ -406,18 +406,7 @@ static struct platform_driver tiny_spi_driver = {
.of_match_table = tiny_spi_match,
},
};
-
-static int __init tiny_spi_init(void)
-{
- return platform_driver_register(&tiny_spi_driver);
-}
-module_init(tiny_spi_init);
-
-static void __exit tiny_spi_exit(void)
-{
- platform_driver_unregister(&tiny_spi_driver);
-}
-module_exit(tiny_spi_exit);
+module_platform_driver(tiny_spi_driver);
MODULE_DESCRIPTION("OpenCores tiny SPI driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index fde3a2d4f120..322be7aea8b4 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1116,15 +1116,16 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev)
status = -ENODEV;
goto err1;
}
+
+ r->start += pdata->regs_offset;
+ r->end += pdata->regs_offset;
+ mcspi->phys = r->start;
if (!request_mem_region(r->start, resource_size(r),
dev_name(&pdev->dev))) {
status = -EBUSY;
goto err1;
}
- r->start += pdata->regs_offset;
- r->end += pdata->regs_offset;
- mcspi->phys = r->start;
mcspi->base = ioremap(r->start, resource_size(r));
if (!mcspi->base) {
dev_dbg(&pdev->dev, "can't ioremap MCSPI\n");
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 1ab2fa0d37fd..f103e470cb63 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -113,7 +113,6 @@
#define SSP_CR0_MASK_CSS_ST (0x1FUL << 16)
#define SSP_CR0_MASK_FRF_ST (0x3UL << 21)
-
/*
* SSP Control Register 0 - SSP_CR1
*/
@@ -283,7 +282,6 @@
#define SPI_POLLING_TIMEOUT 1000
-
/*
* The type of reading going on on this chip
*/
@@ -749,7 +747,6 @@ static void readwriter(struct pl022 *pl022)
*/
}
-
/**
* next_transfer - Move to the Next transfer in the current spi message
* @pl022: SSP driver private data structure
@@ -1016,14 +1013,14 @@ static int configure_dma(struct pl022 *pl022)
dmaengine_slave_config(txchan, &tx_conf);
/* Create sglists for the transfers */
- pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
+ pages = DIV_ROUND_UP(pl022->cur_transfer->len, PAGE_SIZE);
dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages);
- ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL);
+ ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_rx_sg;
- ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL);
+ ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_tx_sg;
@@ -1531,8 +1528,7 @@ static void pump_messages(struct work_struct *work)
/* Initial message state */
pl022->cur_msg->state = STATE_START;
pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next,
- struct spi_transfer,
- transfer_list);
+ struct spi_transfer, transfer_list);
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
@@ -1551,7 +1547,6 @@ static void pump_messages(struct work_struct *work)
do_interrupt_dma_transfer(pl022);
}
-
static int __init init_queue(struct pl022 *pl022)
{
INIT_LIST_HEAD(&pl022->queue);
@@ -1560,8 +1555,8 @@ static int __init init_queue(struct pl022 *pl022)
pl022->running = false;
pl022->busy = false;
- tasklet_init(&pl022->pump_transfers,
- pump_transfers, (unsigned long)pl022);
+ tasklet_init(&pl022->pump_transfers, pump_transfers,
+ (unsigned long)pl022);
INIT_WORK(&pl022->pump_messages, pump_messages);
pl022->workqueue = create_singlethread_workqueue(
@@ -1572,7 +1567,6 @@ static int __init init_queue(struct pl022 *pl022)
return 0;
}
-
static int start_queue(struct pl022 *pl022)
{
unsigned long flags;
@@ -1595,7 +1589,6 @@ static int start_queue(struct pl022 *pl022)
return 0;
}
-
static int stop_queue(struct pl022 *pl022)
{
unsigned long flags;
@@ -1791,71 +1784,70 @@ static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
return 0;
}
-static int calculate_effective_freq(struct pl022 *pl022,
- int freq,
- struct ssp_clock_params *clk_freq)
+static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
+{
+ return rate / (cpsdvsr * (1 + scr));
+}
+
+static int calculate_effective_freq(struct pl022 *pl022, int freq, struct
+ ssp_clock_params * clk_freq)
{
/* Lets calculate the frequency parameters */
- u16 cpsdvsr = 2;
- u16 scr = 0;
- bool freq_found = false;
- u32 rate;
- u32 max_tclk;
- u32 min_tclk;
+ u16 cpsdvsr = CPSDVR_MIN, scr = SCR_MIN;
+ u32 rate, max_tclk, min_tclk, best_freq = 0, best_cpsdvsr = 0,
+ best_scr = 0, tmp, found = 0;
rate = clk_get_rate(pl022->clk);
/* cpsdvscr = 2 & scr 0 */
- max_tclk = (rate / (CPSDVR_MIN * (1 + SCR_MIN)));
+ max_tclk = spi_rate(rate, CPSDVR_MIN, SCR_MIN);
/* cpsdvsr = 254 & scr = 255 */
- min_tclk = (rate / (CPSDVR_MAX * (1 + SCR_MAX)));
-
- if ((freq <= max_tclk) && (freq >= min_tclk)) {
- while (cpsdvsr <= CPSDVR_MAX && !freq_found) {
- while (scr <= SCR_MAX && !freq_found) {
- if ((rate /
- (cpsdvsr * (1 + scr))) > freq)
- scr += 1;
- else {
- /*
- * This bool is made true when
- * effective frequency >=
- * target frequency is found
- */
- freq_found = true;
- if ((rate /
- (cpsdvsr * (1 + scr))) != freq) {
- if (scr == SCR_MIN) {
- cpsdvsr -= 2;
- scr = SCR_MAX;
- } else
- scr -= 1;
- }
- }
- }
- if (!freq_found) {
- cpsdvsr += 2;
- scr = SCR_MIN;
- }
- }
- if (cpsdvsr != 0) {
- dev_dbg(&pl022->adev->dev,
- "SSP Effective Frequency is %u\n",
- (rate / (cpsdvsr * (1 + scr))));
- clk_freq->cpsdvsr = (u8) (cpsdvsr & 0xFF);
- clk_freq->scr = (u8) (scr & 0xFF);
- dev_dbg(&pl022->adev->dev,
- "SSP cpsdvsr = %d, scr = %d\n",
- clk_freq->cpsdvsr, clk_freq->scr);
- }
- } else {
+ min_tclk = spi_rate(rate, CPSDVR_MAX, SCR_MAX);
+
+ if (!((freq <= max_tclk) && (freq >= min_tclk))) {
dev_err(&pl022->adev->dev,
"controller data is incorrect: out of range frequency");
return -EINVAL;
}
+
+ /*
+ * best_freq will give closest possible available rate (<= requested
+ * freq) for all values of scr & cpsdvsr.
+ */
+ while ((cpsdvsr <= CPSDVR_MAX) && !found) {
+ while (scr <= SCR_MAX) {
+ tmp = spi_rate(rate, cpsdvsr, scr);
+
+ if (tmp > freq)
+ scr++;
+ /*
+ * If found exact value, update and break.
+ * If found more closer value, update and continue.
+ */
+ else if ((tmp == freq) || (tmp > best_freq)) {
+ best_freq = tmp;
+ best_cpsdvsr = cpsdvsr;
+ best_scr = scr;
+
+ if (tmp == freq)
+ break;
+ }
+ scr++;
+ }
+ cpsdvsr += 2;
+ scr = SCR_MIN;
+ }
+
+ clk_freq->cpsdvsr = (u8) (best_cpsdvsr & 0xFF);
+ clk_freq->scr = (u8) (best_scr & 0xFF);
+ dev_dbg(&pl022->adev->dev,
+ "SSP Target Frequency is: %u, Effective Frequency is %u\n",
+ freq, best_freq);
+ dev_dbg(&pl022->adev->dev, "SSP cpsdvsr = %d, scr = %d\n",
+ clk_freq->cpsdvsr, clk_freq->scr);
+
return 0;
}
-
/*
* A piece of default chip info unless the platform
* supplies it.
@@ -1873,7 +1865,6 @@ static const struct pl022_config_chip pl022_default_chip_info = {
.cs_control = null_cs_control,
};
-
/**
* pl022_setup - setup function registered to SPI master framework
* @spi: spi device which is requesting setup
@@ -1950,7 +1941,6 @@ static int pl022_setup(struct spi_device *spi)
goto err_config_params;
}
-
status = verify_controller_parameters(pl022, chip_info);
if (status) {
dev_err(&spi->dev, "controller data is incorrect");
@@ -2090,7 +2080,8 @@ static int pl022_setup(struct spi_device *spi)
}
SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
- SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
+ SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD,
+ 3);
/* Save controller_state */
spi_set_ctldata(spi, chip);
@@ -2116,7 +2107,6 @@ static void pl022_cleanup(struct spi_device *spi)
kfree(chip);
}
-
static int __devinit
pl022_probe(struct amba_device *adev, const struct amba_id *id)
{
@@ -2242,7 +2232,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
err_start_queue:
err_init_queue:
destroy_queue(pl022);
- pl022_dma_remove(pl022);
+ if (platform_info->enable_dma)
+ pl022_dma_remove(pl022);
+
free_irq(adev->irq[0], pl022);
err_no_irq:
clk_unprepare(pl022->clk);
@@ -2277,7 +2269,9 @@ pl022_remove(struct amba_device *adev)
if (destroy_queue(pl022) != 0)
dev_err(&adev->dev, "queue remove failed\n");
load_ssp_default_config(pl022);
- pl022_dma_remove(pl022);
+ if (pl022->master_info->enable_dma)
+ pl022_dma_remove(pl022);
+
free_irq(adev->irq[0], pl022);
clk_disable(pl022->clk);
clk_unprepare(pl022->clk);
@@ -2364,7 +2358,6 @@ static struct vendor_data vendor_arm = {
.loopback = true,
};
-
static struct vendor_data vendor_st = {
.fifodepth = 32,
.max_bpw = 32,
@@ -2419,9 +2412,9 @@ static struct amba_id pl022_ids[] = {
* and 32 locations deep TX/RX FIFO but no extended
* CR0/CR1 register
*/
- .id = 0x00080023,
- .mask = 0xffffffff,
- .data = &vendor_st_pl023,
+ .id = 0x00080023,
+ .mask = 0xffffffff,
+ .data = &vendor_st_pl023,
},
{
.id = 0x10080023,
@@ -2441,19 +2434,16 @@ static struct amba_driver pl022_driver = {
.remove = __devexit_p(pl022_remove),
};
-
static int __init pl022_init(void)
{
return amba_driver_register(&pl022_driver);
}
-
subsys_initcall(pl022_init);
static void __exit pl022_exit(void)
{
amba_driver_unregister(&pl022_driver);
}
-
module_exit(pl022_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index b267fd901e54..98ec53285fc7 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -514,7 +514,7 @@ static int __init spi_ppc4xx_of_probe(struct platform_device *op)
/* Request IRQ */
hw->irqnum = irq_of_parse_and_map(np, 0);
ret = request_irq(hw->irqnum, spi_ppc4xx_int,
- IRQF_DISABLED, "spi_ppc4xx_of", (void *)hw);
+ 0, "spi_ppc4xx_of", (void *)hw);
if (ret) {
dev_err(dev, "unable to allocate interrupt\n");
goto free_gpios;
@@ -594,18 +594,7 @@ static struct platform_driver spi_ppc4xx_of_driver = {
.of_match_table = spi_ppc4xx_of_match,
},
};
-
-static int __init spi_ppc4xx_init(void)
-{
- return platform_driver_register(&spi_ppc4xx_of_driver);
-}
-module_init(spi_ppc4xx_init);
-
-static void __exit spi_ppc4xx_exit(void)
-{
- platform_driver_unregister(&spi_ppc4xx_of_driver);
-}
-module_exit(spi_ppc4xx_exit);
+module_platform_driver(spi_ppc4xx_of_driver);
MODULE_AUTHOR("Gary Jennejohn & Stefan Roese");
MODULE_DESCRIPTION("Simple PPC4xx SPI Driver");
diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c
index 1996ac57ef91..b857a3e7af94 100644
--- a/drivers/spi/spi-s3c24xx.c
+++ b/drivers/spi/spi-s3c24xx.c
@@ -505,7 +505,7 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
}
}
-static int __init s3c24xx_spi_probe(struct platform_device *pdev)
+static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
{
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
@@ -661,7 +661,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
return err;
}
-static int __exit s3c24xx_spi_remove(struct platform_device *dev)
+static int __devexit s3c24xx_spi_remove(struct platform_device *dev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(dev);
@@ -719,26 +719,15 @@ static const struct dev_pm_ops s3c24xx_spi_pmops = {
MODULE_ALIAS("platform:s3c2410-spi");
static struct platform_driver s3c24xx_spi_driver = {
- .remove = __exit_p(s3c24xx_spi_remove),
+ .probe = s3c24xx_spi_probe,
+ .remove = __devexit_p(s3c24xx_spi_remove),
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.pm = S3C24XX_SPI_PMOPS,
},
};
-
-static int __init s3c24xx_spi_init(void)
-{
- return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
-}
-
-static void __exit s3c24xx_spi_exit(void)
-{
- platform_driver_unregister(&s3c24xx_spi_driver);
-}
-
-module_init(s3c24xx_spi_init);
-module_exit(s3c24xx_spi_exit);
+module_platform_driver(s3c24xx_spi_driver);
MODULE_DESCRIPTION("S3C24XX SPI Driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 595dacc7645f..019a7163572f 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -131,6 +131,12 @@
#define RXBUSY (1<<2)
#define TXBUSY (1<<3)
+struct s3c64xx_spi_dma_data {
+ unsigned ch;
+ enum dma_data_direction direction;
+ enum dma_ch dmach;
+};
+
/**
* struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
* @clk: Pointer to the spi clock.
@@ -164,13 +170,14 @@ struct s3c64xx_spi_driver_data {
struct work_struct work;
struct list_head queue;
spinlock_t lock;
- enum dma_ch rx_dmach;
- enum dma_ch tx_dmach;
unsigned long sfr_start;
struct completion xfer_completion;
unsigned state;
unsigned cur_mode, cur_bpw;
unsigned cur_speed;
+ struct s3c64xx_spi_dma_data rx_dma;
+ struct s3c64xx_spi_dma_data tx_dma;
+ struct samsung_dma_ops *ops;
};
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
@@ -226,6 +233,78 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
writel(val, regs + S3C64XX_SPI_CH_CFG);
}
+static void s3c64xx_spi_dmacb(void *data)
+{
+ struct s3c64xx_spi_driver_data *sdd;
+ struct s3c64xx_spi_dma_data *dma = data;
+ unsigned long flags;
+
+ if (dma->direction == DMA_FROM_DEVICE)
+ sdd = container_of(data,
+ struct s3c64xx_spi_driver_data, rx_dma);
+ else
+ sdd = container_of(data,
+ struct s3c64xx_spi_driver_data, tx_dma);
+
+ spin_lock_irqsave(&sdd->lock, flags);
+
+ if (dma->direction == DMA_FROM_DEVICE) {
+ sdd->state &= ~RXBUSY;
+ if (!(sdd->state & TXBUSY))
+ complete(&sdd->xfer_completion);
+ } else {
+ sdd->state &= ~TXBUSY;
+ if (!(sdd->state & RXBUSY))
+ complete(&sdd->xfer_completion);
+ }
+
+ spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
+ unsigned len, dma_addr_t buf)
+{
+ struct s3c64xx_spi_driver_data *sdd;
+ struct samsung_dma_prep_info info;
+
+ if (dma->direction == DMA_FROM_DEVICE)
+ sdd = container_of((void *)dma,
+ struct s3c64xx_spi_driver_data, rx_dma);
+ else
+ sdd = container_of((void *)dma,
+ struct s3c64xx_spi_driver_data, tx_dma);
+
+ info.cap = DMA_SLAVE;
+ info.len = len;
+ info.fp = s3c64xx_spi_dmacb;
+ info.fp_param = dma;
+ info.direction = dma->direction;
+ info.buf = buf;
+
+ sdd->ops->prepare(dma->ch, &info);
+ sdd->ops->trigger(dma->ch);
+}
+
+static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
+{
+ struct samsung_dma_info info;
+
+ sdd->ops = samsung_dma_get_ops();
+
+ info.cap = DMA_SLAVE;
+ info.client = &s3c64xx_spi_dma_client;
+ info.width = sdd->cur_bpw / 8;
+
+ info.direction = sdd->rx_dma.direction;
+ info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
+ sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &info);
+ info.direction = sdd->tx_dma.direction;
+ info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
+ sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &info);
+
+ return 1;
+}
+
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
@@ -258,10 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
- s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8);
- s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
- xfer->tx_dma, xfer->len);
- s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
+ prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma);
} else {
switch (sdd->cur_bpw) {
case 32:
@@ -293,10 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
- s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8);
- s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
- xfer->rx_dma, xfer->len);
- s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
+ prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma);
}
}
@@ -482,46 +555,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
}
}
-static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
- int size, enum s3c2410_dma_buffresult res)
-{
- struct s3c64xx_spi_driver_data *sdd = buf_id;
- unsigned long flags;
-
- spin_lock_irqsave(&sdd->lock, flags);
-
- if (res == S3C2410_RES_OK)
- sdd->state &= ~RXBUSY;
- else
- dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size);
-
- /* If the other done */
- if (!(sdd->state & TXBUSY))
- complete(&sdd->xfer_completion);
-
- spin_unlock_irqrestore(&sdd->lock, flags);
-}
-
-static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
- int size, enum s3c2410_dma_buffresult res)
-{
- struct s3c64xx_spi_driver_data *sdd = buf_id;
- unsigned long flags;
-
- spin_lock_irqsave(&sdd->lock, flags);
-
- if (res == S3C2410_RES_OK)
- sdd->state &= ~TXBUSY;
- else
- dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size);
-
- /* If the other done */
- if (!(sdd->state & RXBUSY))
- complete(&sdd->xfer_completion);
-
- spin_unlock_irqrestore(&sdd->lock, flags);
-}
-
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
@@ -696,12 +729,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
if (use_dma) {
if (xfer->tx_buf != NULL
&& (sdd->state & TXBUSY))
- s3c2410_dma_ctrl(sdd->tx_dmach,
- S3C2410_DMAOP_FLUSH);
+ sdd->ops->stop(sdd->tx_dma.ch);
if (xfer->rx_buf != NULL
&& (sdd->state & RXBUSY))
- s3c2410_dma_ctrl(sdd->rx_dmach,
- S3C2410_DMAOP_FLUSH);
+ sdd->ops->stop(sdd->rx_dma.ch);
}
goto out;
@@ -739,30 +770,6 @@ out:
msg->complete(msg->context);
}
-static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
-{
- if (s3c2410_dma_request(sdd->rx_dmach,
- &s3c64xx_spi_dma_client, NULL) < 0) {
- dev_err(&sdd->pdev->dev, "cannot get RxDMA\n");
- return 0;
- }
- s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb);
- s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW,
- sdd->sfr_start + S3C64XX_SPI_RX_DATA);
-
- if (s3c2410_dma_request(sdd->tx_dmach,
- &s3c64xx_spi_dma_client, NULL) < 0) {
- dev_err(&sdd->pdev->dev, "cannot get TxDMA\n");
- s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
- return 0;
- }
- s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb);
- s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM,
- sdd->sfr_start + S3C64XX_SPI_TX_DATA);
-
- return 1;
-}
-
static void s3c64xx_spi_work(struct work_struct *work)
{
struct s3c64xx_spi_driver_data *sdd = container_of(work,
@@ -799,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
spin_unlock_irqrestore(&sdd->lock, flags);
/* Free DMA channels */
- s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
- s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+ sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
+ sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
}
static int s3c64xx_spi_transfer(struct spi_device *spi,
@@ -1017,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cntrlr_info = sci;
sdd->pdev = pdev;
sdd->sfr_start = mem_res->start;
- sdd->tx_dmach = dmatx_res->start;
- sdd->rx_dmach = dmarx_res->start;
+ sdd->tx_dma.dmach = dmatx_res->start;
+ sdd->tx_dma.direction = DMA_TO_DEVICE;
+ sdd->rx_dma.dmach = dmarx_res->start;
+ sdd->rx_dma.direction = DMA_FROM_DEVICE;
sdd->cur_bpw = 8;
@@ -1106,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
pdev->id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
- sdd->rx_dmach, sdd->tx_dmach);
+ sdd->rx_dma.dmach, sdd->tx_dma.dmach);
return 0;
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index e00d94b22250..0f4834ae28cd 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -635,7 +635,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
goto err2;
}
- ret = request_irq(i, sh_msiof_spi_irq, IRQF_DISABLED,
+ ret = request_irq(i, sh_msiof_spi_irq, 0,
dev_name(&pdev->dev), p);
if (ret) {
dev_err(&pdev->dev, "unable to request irq\n");
@@ -730,18 +730,7 @@ static struct platform_driver sh_msiof_spi_drv = {
.pm = &sh_msiof_spi_dev_pm_ops,
},
};
-
-static int __init sh_msiof_spi_init(void)
-{
- return platform_driver_register(&sh_msiof_spi_drv);
-}
-module_init(sh_msiof_spi_init);
-
-static void __exit sh_msiof_spi_exit(void)
-{
- platform_driver_unregister(&sh_msiof_spi_drv);
-}
-module_exit(sh_msiof_spi_exit);
+module_platform_driver(sh_msiof_spi_drv);
MODULE_DESCRIPTION("SuperH MSIOF SPI Master Interface Driver");
MODULE_AUTHOR("Magnus Damm");
diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c
index e7779c09f6ef..8844bc342782 100644
--- a/drivers/spi/spi-sh-sci.c
+++ b/drivers/spi/spi-sh-sci.c
@@ -186,18 +186,7 @@ static struct platform_driver sh_sci_spi_drv = {
.owner = THIS_MODULE,
},
};
-
-static int __init sh_sci_spi_init(void)
-{
- return platform_driver_register(&sh_sci_spi_drv);
-}
-module_init(sh_sci_spi_init);
-
-static void __exit sh_sci_spi_exit(void)
-{
- platform_driver_unregister(&sh_sci_spi_drv);
-}
-module_exit(sh_sci_spi_exit);
+module_platform_driver(sh_sci_spi_drv);
MODULE_DESCRIPTION("SH SCI SPI Driver");
MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c
index 9eedd71ad898..70c8af9f7ccc 100644
--- a/drivers/spi/spi-sh.c
+++ b/drivers/spi/spi-sh.c
@@ -484,7 +484,7 @@ static int __devinit spi_sh_probe(struct platform_device *pdev)
goto error2;
}
- ret = request_irq(irq, spi_sh_irq, IRQF_DISABLED, "spi_sh", ss);
+ ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error3;
@@ -524,18 +524,7 @@ static struct platform_driver spi_sh_driver = {
.owner = THIS_MODULE,
},
};
-
-static int __init spi_sh_init(void)
-{
- return platform_driver_register(&spi_sh_driver);
-}
-module_init(spi_sh_init);
-
-static void __exit spi_sh_exit(void)
-{
- platform_driver_unregister(&spi_sh_driver);
-}
-module_exit(spi_sh_exit);
+module_platform_driver(spi_sh_driver);
MODULE_DESCRIPTION("SH SPI bus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-stmp.c b/drivers/spi/spi-stmp.c
index fadff76eb7e0..58e385285323 100644
--- a/drivers/spi/spi-stmp.c
+++ b/drivers/spi/spi-stmp.c
@@ -659,19 +659,8 @@ static struct platform_driver stmp_spi_driver = {
.suspend = stmp_spi_suspend,
.resume = stmp_spi_resume,
};
+module_platform_driver(stmp_spi_driver);
-static int __init stmp_spi_init(void)
-{
- return platform_driver_register(&stmp_spi_driver);
-}
-
-static void __exit stmp_spi_exit(void)
-{
- platform_driver_unregister(&stmp_spi_driver);
-}
-
-module_init(stmp_spi_init);
-module_exit(stmp_spi_exit);
module_param(pio, int, S_IRUGO);
module_param(clock, int, S_IRUGO);
MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>");
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c
index a5a6302dc8e0..ae6d78a3e912 100644
--- a/drivers/spi/spi-tegra.c
+++ b/drivers/spi/spi-tegra.c
@@ -18,6 +18,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -464,7 +465,7 @@ static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
return 0;
}
-static int __init spi_tegra_probe(struct platform_device *pdev)
+static int __devinit spi_tegra_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct spi_tegra_data *tspi;
@@ -612,19 +613,9 @@ static struct platform_driver spi_tegra_driver = {
.owner = THIS_MODULE,
.of_match_table = spi_tegra_of_match_table,
},
+ .probe = spi_tegra_probe,
.remove = __devexit_p(spi_tegra_remove),
};
-
-static int __init spi_tegra_init(void)
-{
- return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe);
-}
-module_init(spi_tegra_init);
-
-static void __exit spi_tegra_exit(void)
-{
- platform_driver_unregister(&spi_tegra_driver);
-}
-module_exit(spi_tegra_exit);
+module_platform_driver(spi_tegra_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c
index ee22795c7973..7963c60063d6 100644
--- a/drivers/spi/spi-ti-ssp.c
+++ b/drivers/spi/spi-ti-ssp.c
@@ -383,18 +383,7 @@ static struct platform_driver ti_ssp_spi_driver = {
.owner = THIS_MODULE,
},
};
-
-static int __init ti_ssp_spi_init(void)
-{
- return platform_driver_register(&ti_ssp_spi_driver);
-}
-module_init(ti_ssp_spi_init);
-
-static void __exit ti_ssp_spi_exit(void)
-{
- platform_driver_unregister(&ti_ssp_spi_driver);
-}
-module_exit(ti_ssp_spi_exit);
+module_platform_driver(ti_ssp_spi_driver);
MODULE_DESCRIPTION("SSP SPI Master");
MODULE_AUTHOR("Cyril Chemparathy");
diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
index 4d2c75df886c..4c5a663b9fa8 100644
--- a/drivers/spi/spi-xilinx.c
+++ b/drivers/spi/spi-xilinx.c
@@ -538,18 +538,7 @@ static struct platform_driver xilinx_spi_driver = {
.of_match_table = xilinx_spi_of_match,
},
};
-
-static int __init xilinx_spi_pltfm_init(void)
-{
- return platform_driver_register(&xilinx_spi_driver);
-}
-module_init(xilinx_spi_pltfm_init);
-
-static void __exit xilinx_spi_pltfm_exit(void)
-{
- platform_driver_unregister(&xilinx_spi_driver);
-}
-module_exit(xilinx_spi_pltfm_exit);
+module_platform_driver(xilinx_spi_driver);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("Xilinx SPI driver");
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index a32961305723..25cdff36a78a 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -30,14 +30,6 @@ source "drivers/staging/et131x/Kconfig"
source "drivers/staging/slicoss/Kconfig"
-source "drivers/staging/go7007/Kconfig"
-
-source "drivers/staging/cx25821/Kconfig"
-
-source "drivers/staging/tm6000/Kconfig"
-
-source "drivers/staging/cxd2099/Kconfig"
-
source "drivers/staging/usbip/Kconfig"
source "drivers/staging/winbond/Kconfig"
@@ -106,20 +98,12 @@ source "drivers/staging/wlags49_h25/Kconfig"
source "drivers/staging/sm7xx/Kconfig"
-source "drivers/staging/dt3155v4l/Kconfig"
-
source "drivers/staging/crystalhd/Kconfig"
source "drivers/staging/cxt1e1/Kconfig"
source "drivers/staging/xgifb/Kconfig"
-source "drivers/staging/lirc/Kconfig"
-
-source "drivers/staging/easycap/Kconfig"
-
-source "drivers/staging/solo6x10/Kconfig"
-
source "drivers/staging/tidspbridge/Kconfig"
source "drivers/staging/quickstart/Kconfig"
@@ -142,10 +126,10 @@ source "drivers/staging/ste_rmi4/Kconfig"
source "drivers/staging/gma500/Kconfig"
-source "drivers/staging/altera-stapl/Kconfig"
-
source "drivers/staging/mei/Kconfig"
source "drivers/staging/nvec/Kconfig"
+source "drivers/staging/media/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index d7a5a04d0a2f..a25f3f26c7ff 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -4,13 +4,9 @@
obj-$(CONFIG_STAGING) += staging.o
obj-y += serial/
+obj-y += media/
obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/
-obj-$(CONFIG_VIDEO_GO7007) += go7007/
-obj-$(CONFIG_VIDEO_CX25821) += cx25821/
-obj-$(CONFIG_VIDEO_TM6000) += tm6000/
-obj-$(CONFIG_DVB_CXD2099) += cxd2099/
-obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_W35UND) += winbond/
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
@@ -45,12 +41,9 @@ obj-$(CONFIG_ZCACHE) += zcache/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
-obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_CRYSTALHD) += crystalhd/
obj-$(CONFIG_CXT1E1) += cxt1e1/
obj-$(CONFIG_FB_XGI) += xgifb/
-obj-$(CONFIG_EASYCAP) += easycap/
-obj-$(CONFIG_SOLO6X10) += solo6x10/
obj-$(CONFIG_TIDSPBRIDGE) += tidspbridge/
obj-$(CONFIG_ACPI_QUICKSTART) += quickstart/
obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/
@@ -59,7 +52,6 @@ obj-$(CONFIG_BCM_WIMAX) += bcm/
obj-$(CONFIG_FT1000) += ft1000/
obj-$(CONFIG_SND_INTEL_SST) += intel_sst/
obj-$(CONFIG_SPEAKUP) += speakup/
-obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
obj-$(CONFIG_DRM_PSB) += gma500/
diff --git a/drivers/staging/altera-stapl/Makefile b/drivers/staging/altera-stapl/Makefile
deleted file mode 100644
index ddeede3c4b96..000000000000
--- a/drivers/staging/altera-stapl/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-altera-stapl-y := altera-lpt.o altera-jtag.o altera-comp.o altera.o
-
-obj-$(CONFIG_ALTERA_STAPL) += altera-stapl.o
diff --git a/drivers/staging/altera-stapl/altera.h b/drivers/staging/altera-stapl/altera.h
deleted file mode 100644
index 94c0c6181daf..000000000000
--- a/drivers/staging/altera-stapl/altera.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * altera.h
- *
- * altera FPGA driver
- *
- * Copyright (C) Altera Corporation 1998-2001
- * Copyright (C) 2010 NetUP Inc.
- * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * 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 _ALTERA_H_
-#define _ALTERA_H_
-
-struct altera_config {
- void *dev;
- u8 *action;
- int (*jtag_io) (void *dev, int tms, int tdi, int tdo);
-};
-
-#if defined(CONFIG_ALTERA_STAPL) || \
- (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE))
-
-extern int altera_init(struct altera_config *config, const struct firmware *fw);
-#else
-
-static inline int altera_init(struct altera_config *config,
- const struct firmware *fw)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return 0;
-}
-#endif /* CONFIG_ALTERA_STAPL */
-
-#endif /* _ALTERA_H_ */
diff --git a/drivers/staging/cx25821/README b/drivers/staging/cx25821/README
deleted file mode 100644
index a9ba50b9888b..000000000000
--- a/drivers/staging/cx25821/README
+++ /dev/null
@@ -1,6 +0,0 @@
-Todo:
- - checkpatch.pl cleanups
- - sparse cleanups
-
-Please send patches to linux-media@vger.kernel.org
-
diff --git a/drivers/staging/iio/trigger.h b/drivers/staging/iio/trigger.h
index 598fcb3599f9..5cc42a655c88 100644
--- a/drivers/staging/iio/trigger.h
+++ b/drivers/staging/iio/trigger.h
@@ -115,8 +115,7 @@ void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time);
irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private);
-struct iio_trigger *iio_allocate_trigger(const char *fmt, ...)
- __attribute__((format(printf, 1, 2)));
+__printf(1, 2) struct iio_trigger *iio_allocate_trigger(const char *fmt, ...);
void iio_free_trigger(struct iio_trigger *trig);
#endif /* _IIO_TRIGGER_H_ */
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
new file mode 100644
index 000000000000..7e5caa39ed3f
--- /dev/null
+++ b/drivers/staging/media/Kconfig
@@ -0,0 +1,37 @@
+menuconfig STAGING_MEDIA
+ bool "Media staging drivers"
+ default n
+ ---help---
+ This option allows you to select a number of media drivers that
+ don't have the "normal" Linux kernel quality level.
+ Most of them don't follow properly the V4L, DVB and/or RC API's,
+ so, they won't likely work fine with the existing applications.
+ That also means that, one fixed, their API's will change to match
+ the existing ones.
+
+ If you wish to work on these drivers, to help improve them, or
+ to report problems you have with them, please use the
+ linux-media@vger.kernel.org mailing list.
+
+ If in doubt, say N here.
+
+
+if STAGING_MEDIA
+
+# Please keep them in alphabetic order
+source "drivers/staging/media/as102/Kconfig"
+
+source "drivers/staging/media/cxd2099/Kconfig"
+
+source "drivers/staging/media/dt3155v4l/Kconfig"
+
+source "drivers/staging/media/easycap/Kconfig"
+
+source "drivers/staging/media/go7007/Kconfig"
+
+source "drivers/staging/media/solo6x10/Kconfig"
+
+# Keep LIRC at the end, as it has sub-menus
+source "drivers/staging/media/lirc/Kconfig"
+
+endif
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
new file mode 100644
index 000000000000..c69124cdb0d3
--- /dev/null
+++ b/drivers/staging/media/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_DVB_AS102) += as102/
+obj-$(CONFIG_DVB_CXD2099) += cxd2099/
+obj-$(CONFIG_EASYCAP) += easycap/
+obj-$(CONFIG_LIRC_STAGING) += lirc/
+obj-$(CONFIG_SOLO6X10) += solo6x10/
+obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
+obj-$(CONFIG_VIDEO_GO7007) += go7007/
diff --git a/drivers/staging/media/as102/Kconfig b/drivers/staging/media/as102/Kconfig
new file mode 100644
index 000000000000..5865029db0f6
--- /dev/null
+++ b/drivers/staging/media/as102/Kconfig
@@ -0,0 +1,7 @@
+config DVB_AS102
+ tristate "Abilis AS102 DVB receiver"
+ depends on DVB_CORE && USB && I2C && INPUT
+ help
+ Choose Y or M here if you have a device containing an AS102
+
+ To compile this driver as a module, choose M here
diff --git a/drivers/staging/media/as102/Makefile b/drivers/staging/media/as102/Makefile
new file mode 100644
index 000000000000..e7dbb6f814d5
--- /dev/null
+++ b/drivers/staging/media/as102/Makefile
@@ -0,0 +1,6 @@
+dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \
+ as102_fe.o as102_usb_drv.o as10x_cmd_cfg.o
+
+obj-$(CONFIG_DVB_AS102) += dvb-as102.o
+
+EXTRA_CFLAGS += -DCONFIG_AS102_USB -Idrivers/media/dvb/dvb-core
diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c
new file mode 100644
index 000000000000..d335c7d6fa0f
--- /dev/null
+++ b/drivers/staging/media/as102/as102_drv.c
@@ -0,0 +1,351 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+/* header file for Usb device driver*/
+#include "as102_drv.h"
+#include "as102_fw.h"
+#include "dvbdev.h"
+
+int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)");
+
+int dual_tuner;
+module_param_named(dual_tuner, dual_tuner, int, 0644);
+MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
+
+static int fw_upload = 1;
+module_param_named(fw_upload, fw_upload, int, 0644);
+MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
+
+static int pid_filtering;
+module_param_named(pid_filtering, pid_filtering, int, 0644);
+MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
+
+static int ts_auto_disable;
+module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
+MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
+
+int elna_enable = 1;
+module_param_named(elna_enable, elna_enable, int, 0644);
+MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
+
+#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+#endif
+
+static void as102_stop_stream(struct as102_dev_t *dev)
+{
+ struct as102_bus_adapter_t *bus_adap;
+
+ if (dev != NULL)
+ bus_adap = &dev->bus_adap;
+ else
+ return;
+
+ if (bus_adap->ops->stop_stream != NULL)
+ bus_adap->ops->stop_stream(dev);
+
+ if (ts_auto_disable) {
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return;
+
+ if (as10x_cmd_stop_streaming(bus_adap) < 0)
+ dprintk(debug, "as10x_cmd_stop_streaming failed\n");
+
+ mutex_unlock(&dev->bus_adap.lock);
+ }
+}
+
+static int as102_start_stream(struct as102_dev_t *dev)
+{
+ struct as102_bus_adapter_t *bus_adap;
+ int ret = -EFAULT;
+
+ if (dev != NULL)
+ bus_adap = &dev->bus_adap;
+ else
+ return ret;
+
+ if (bus_adap->ops->start_stream != NULL)
+ ret = bus_adap->ops->start_stream(dev);
+
+ if (ts_auto_disable) {
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return -EFAULT;
+
+ ret = as10x_cmd_start_streaming(bus_adap);
+
+ mutex_unlock(&dev->bus_adap.lock);
+ }
+
+ return ret;
+}
+
+static int as10x_pid_filter(struct as102_dev_t *dev,
+ int index, u16 pid, int onoff) {
+
+ struct as102_bus_adapter_t *bus_adap = &dev->bus_adap;
+ int ret = -EFAULT;
+
+ ENTER();
+
+ if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
+ dprintk(debug, "mutex_lock_interruptible(lock) failed !\n");
+ return -EBUSY;
+ }
+
+ switch (onoff) {
+ case 0:
+ ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
+ dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
+ index, pid, ret);
+ break;
+ case 1:
+ {
+ struct as10x_ts_filter filter;
+
+ filter.type = TS_PID_TYPE_TS;
+ filter.idx = 0xFF;
+ filter.pid = pid;
+
+ ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
+ dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
+ index, filter.idx, filter.pid, ret);
+ break;
+ }
+ }
+
+ mutex_unlock(&dev->bus_adap.lock);
+
+ LEAVE();
+ return ret;
+}
+
+static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ int ret = 0;
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct as102_dev_t *as102_dev = demux->priv;
+
+ ENTER();
+
+ if (mutex_lock_interruptible(&as102_dev->sem))
+ return -ERESTARTSYS;
+
+ if (pid_filtering) {
+ as10x_pid_filter(as102_dev,
+ dvbdmxfeed->index, dvbdmxfeed->pid, 1);
+ }
+
+ if (as102_dev->streaming++ == 0)
+ ret = as102_start_stream(as102_dev);
+
+ mutex_unlock(&as102_dev->sem);
+ LEAVE();
+ return ret;
+}
+
+static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct as102_dev_t *as102_dev = demux->priv;
+
+ ENTER();
+
+ if (mutex_lock_interruptible(&as102_dev->sem))
+ return -ERESTARTSYS;
+
+ if (--as102_dev->streaming == 0)
+ as102_stop_stream(as102_dev);
+
+ if (pid_filtering) {
+ as10x_pid_filter(as102_dev,
+ dvbdmxfeed->index, dvbdmxfeed->pid, 0);
+ }
+
+ mutex_unlock(&as102_dev->sem);
+ LEAVE();
+ return 0;
+}
+
+int as102_dvb_register(struct as102_dev_t *as102_dev)
+{
+ int ret = 0;
+ ENTER();
+
+ ret = dvb_register_adapter(&as102_dev->dvb_adap,
+ as102_dev->name,
+ THIS_MODULE,
+#if defined(CONFIG_AS102_USB)
+ &as102_dev->bus_adap.usb_dev->dev
+#elif defined(CONFIG_AS102_SPI)
+ &as102_dev->bus_adap.spi_dev->dev
+#else
+#error >>> dvb_register_adapter <<<
+#endif
+#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
+ , adapter_nr
+#endif
+ );
+ if (ret < 0) {
+ err("%s: dvb_register_adapter() failed (errno = %d)",
+ __func__, ret);
+ goto failed;
+ }
+
+ as102_dev->dvb_dmx.priv = as102_dev;
+ as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
+ as102_dev->dvb_dmx.feednum = 256;
+ as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
+ as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
+
+ as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING;
+
+ as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
+ as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
+ as102_dev->dvb_dmxdev.capabilities = 0;
+
+ ret = dvb_dmx_init(&as102_dev->dvb_dmx);
+ if (ret < 0) {
+ err("%s: dvb_dmx_init() failed (errno = %d)", __func__, ret);
+ goto failed;
+ }
+
+ ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
+ if (ret < 0) {
+ err("%s: dvb_dmxdev_init() failed (errno = %d)", __func__,
+ ret);
+ goto failed;
+ }
+
+ ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe);
+ if (ret < 0) {
+ err("%s: as102_dvb_register_frontend() failed (errno = %d)",
+ __func__, ret);
+ goto failed;
+ }
+
+ /* init bus mutex for token locking */
+ mutex_init(&as102_dev->bus_adap.lock);
+
+ /* init start / stop stream mutex */
+ mutex_init(&as102_dev->sem);
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+ /*
+ * try to load as102 firmware. If firmware upload failed, we'll be
+ * able to upload it later.
+ */
+ if (fw_upload)
+ try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
+ "firmware_class");
+#endif
+
+failed:
+ LEAVE();
+ /* FIXME: free dvb_XXX */
+ return ret;
+}
+
+void as102_dvb_unregister(struct as102_dev_t *as102_dev)
+{
+ ENTER();
+
+ /* unregister as102 frontend */
+ as102_dvb_unregister_fe(&as102_dev->dvb_fe);
+
+ /* unregister demux device */
+ dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
+ dvb_dmx_release(&as102_dev->dvb_dmx);
+
+ /* unregister dvb adapter */
+ dvb_unregister_adapter(&as102_dev->dvb_adap);
+
+ LEAVE();
+}
+
+static int __init as102_driver_init(void)
+{
+ int ret = 0;
+
+ ENTER();
+
+ /* register this driver with the low level subsystem */
+#if defined(CONFIG_AS102_USB)
+ ret = usb_register(&as102_usb_driver);
+ if (ret)
+ err("usb_register failed (ret = %d)", ret);
+#endif
+#if defined(CONFIG_AS102_SPI)
+ ret = spi_register_driver(&as102_spi_driver);
+ if (ret)
+ printk(KERN_ERR "spi_register failed (ret = %d)", ret);
+#endif
+
+ LEAVE();
+ return ret;
+}
+
+/*
+ * Mandatory function : Adds a special section to the module indicating
+ * where initialisation function is defined
+ */
+module_init(as102_driver_init);
+
+/**
+ * as102_driver_exit - as102 driver exit point
+ *
+ * This function is called when device has to be removed.
+ */
+static void __exit as102_driver_exit(void)
+{
+ ENTER();
+ /* deregister this driver with the low level bus subsystem */
+#if defined(CONFIG_AS102_USB)
+ usb_deregister(&as102_usb_driver);
+#endif
+#if defined(CONFIG_AS102_SPI)
+ spi_unregister_driver(&as102_spi_driver);
+#endif
+ LEAVE();
+}
+
+/*
+ * required function for unload: Adds a special section to the module
+ * indicating where unload function is defined
+ */
+module_exit(as102_driver_exit);
+/* modinfo details */
+MODULE_DESCRIPTION(DRIVER_FULL_NAME);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h
new file mode 100644
index 000000000000..bcda635b5a99
--- /dev/null
+++ b/drivers/staging/media/as102/as102_drv.h
@@ -0,0 +1,141 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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.
+ */
+
+#if defined(CONFIG_AS102_USB)
+#include <linux/usb.h>
+extern struct usb_driver as102_usb_driver;
+#endif
+
+#if defined(CONFIG_AS102_SPI)
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/cdev.h>
+
+extern struct spi_driver as102_spi_driver;
+#endif
+
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+
+#define DRIVER_FULL_NAME "Abilis Systems as10x usb driver"
+#define DRIVER_NAME "as10x_usb"
+
+extern int debug;
+
+#define dprintk(debug, args...) \
+ do { if (debug) { \
+ printk(KERN_DEBUG "%s: ",__FUNCTION__); \
+ printk(args); \
+ } } while (0)
+
+#ifdef TRACE
+#define ENTER() printk(">> enter %s\n", __FUNCTION__)
+#define LEAVE() printk("<< leave %s\n", __FUNCTION__)
+#else
+#define ENTER()
+#define LEAVE()
+#endif
+
+#define AS102_DEVICE_MAJOR 192
+
+#define AS102_USB_BUF_SIZE 512
+#define MAX_STREAM_URB 32
+
+#include "as10x_cmd.h"
+
+#if defined(CONFIG_AS102_USB)
+#include "as102_usb_drv.h"
+#endif
+
+#if defined(CONFIG_AS102_SPI)
+#include "as10x_spi_drv.h"
+#endif
+
+
+struct as102_bus_adapter_t {
+#if defined(CONFIG_AS102_USB)
+ struct usb_device *usb_dev;
+#elif defined(CONFIG_AS102_SPI)
+ struct spi_device *spi_dev;
+ struct cdev cdev; /* spidev raw device */
+
+ struct timer_list timer;
+ struct completion xfer_done;
+#endif
+ /* bus token lock */
+ struct mutex lock;
+ /* low level interface for bus adapter */
+ union as10x_bus_token_t {
+#if defined(CONFIG_AS102_USB)
+ /* usb token */
+ struct as10x_usb_token_cmd_t usb;
+#endif
+#if defined(CONFIG_AS102_SPI)
+ /* spi token */
+ struct as10x_spi_token_cmd_t spi;
+#endif
+ } token;
+
+ /* token cmd xfer id */
+ uint16_t cmd_xid;
+
+ /* as10x command and response for dvb interface*/
+ struct as10x_cmd_t *cmd, *rsp;
+
+ /* bus adapter private ops callback */
+ struct as102_priv_ops_t *ops;
+};
+
+struct as102_dev_t {
+ const char *name;
+ struct as102_bus_adapter_t bus_adap;
+ struct list_head device_entry;
+ struct kref kref;
+ unsigned long minor;
+
+ struct dvb_adapter dvb_adap;
+ struct dvb_frontend dvb_fe;
+ struct dvb_demux dvb_dmx;
+ struct dmxdev dvb_dmxdev;
+
+ /* demodulator stats */
+ struct as10x_demod_stats demod_stats;
+ /* signal strength */
+ uint16_t signal_strength;
+ /* bit error rate */
+ uint32_t ber;
+
+ /* timer handle to trig ts stream download */
+ struct timer_list timer_handle;
+
+ struct mutex sem;
+ dma_addr_t dma_addr;
+ void *stream;
+ int streaming;
+ struct urb *stream_urb[MAX_STREAM_URB];
+};
+
+int as102_dvb_register(struct as102_dev_t *dev);
+void as102_dvb_unregister(struct as102_dev_t *dev);
+
+int as102_dvb_register_fe(struct as102_dev_t *dev, struct dvb_frontend *fe);
+int as102_dvb_unregister_fe(struct dvb_frontend *dev);
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c
new file mode 100644
index 000000000000..3550f905367e
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fe.c
@@ -0,0 +1,603 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/version.h>
+
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+extern int elna_enable;
+
+static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst,
+ struct as10x_tps *src);
+
+static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst,
+ struct dvb_frontend_parameters *src);
+
+static int as102_fe_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ int ret = 0;
+ struct as102_dev_t *dev;
+ struct as10x_tune_args tune_args = { 0 };
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return -EBUSY;
+
+ as102_fe_copy_tune_parameters(&tune_args, params);
+
+ /* send abilis command: SET_TUNE */
+ ret = as10x_cmd_set_tune(&dev->bus_adap, &tune_args);
+ if (ret != 0)
+ dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret);
+
+ mutex_unlock(&dev->bus_adap.lock);
+
+ LEAVE();
+ return (ret < 0) ? -EINVAL : 0;
+}
+
+static int as102_fe_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p) {
+ int ret = 0;
+ struct as102_dev_t *dev;
+ struct as10x_tps tps = { 0 };
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return -EBUSY;
+
+ /* send abilis command: GET_TPS */
+ ret = as10x_cmd_get_tps(&dev->bus_adap, &tps);
+
+ if (ret == 0)
+ as10x_fe_copy_tps_parameters(p, &tps);
+
+ mutex_unlock(&dev->bus_adap.lock);
+
+ LEAVE();
+ return (ret < 0) ? -EINVAL : 0;
+}
+
+static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *settings) {
+ ENTER();
+
+#if 0
+ dprintk(debug, "step_size = %d\n", settings->step_size);
+ dprintk(debug, "max_drift = %d\n", settings->max_drift);
+ dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms,
+ 1000);
+#endif
+
+ settings->min_delay_ms = 1000;
+
+ LEAVE();
+ return 0;
+}
+
+
+static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ int ret = 0;
+ struct as102_dev_t *dev;
+ struct as10x_tune_status tstate = { 0 };
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return -EBUSY;
+
+ /* send abilis command: GET_TUNE_STATUS */
+ ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate);
+ if (ret < 0) {
+ dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ dev->signal_strength = tstate.signal_strength;
+ dev->ber = tstate.BER;
+
+ switch (tstate.tune_state) {
+ case TUNE_STATUS_SIGNAL_DVB_OK:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ break;
+ case TUNE_STATUS_STREAM_DETECTED:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC;
+ break;
+ case TUNE_STATUS_STREAM_TUNED:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ break;
+ default:
+ *status = TUNE_STATUS_NOT_TUNED;
+ }
+
+ dprintk(debug, "tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
+ tstate.tune_state, tstate.signal_strength,
+ tstate.PER, tstate.BER);
+
+ if (*status & FE_HAS_LOCK) {
+ if (as10x_cmd_get_demod_stats(&dev->bus_adap,
+ (struct as10x_demod_stats *) &dev->demod_stats) < 0) {
+ memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
+ dprintk(debug, "as10x_cmd_get_demod_stats failed "
+ "(probably not tuned)\n");
+ } else {
+ dprintk(debug,
+ "demod status: fc: 0x%08x, bad fc: 0x%08x, "
+ "bytes corrected: 0x%08x , MER: 0x%04x\n",
+ dev->demod_stats.frame_count,
+ dev->demod_stats.bad_frame_count,
+ dev->demod_stats.bytes_fixed_by_rs,
+ dev->demod_stats.mer);
+ }
+ } else {
+ memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
+ }
+
+out:
+ mutex_unlock(&dev->bus_adap.lock);
+ LEAVE();
+ return ret;
+}
+
+/*
+ * Note:
+ * - in AS102 SNR=MER
+ * - the SNR will be returned in linear terms, i.e. not in dB
+ * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
+ * - the accuracy is >2dB for SNR values outside this range
+ */
+static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct as102_dev_t *dev;
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ *snr = dev->demod_stats.mer;
+
+ LEAVE();
+ return 0;
+}
+
+static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct as102_dev_t *dev;
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ *ber = dev->ber;
+
+ LEAVE();
+ return 0;
+}
+
+static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct as102_dev_t *dev;
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ *strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2);
+
+ LEAVE();
+ return 0;
+}
+
+static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct as102_dev_t *dev;
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ if (dev->demod_stats.has_started)
+ *ucblocks = dev->demod_stats.bad_frame_count;
+ else
+ *ucblocks = 0;
+
+ LEAVE();
+ return 0;
+}
+
+static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct as102_dev_t *dev;
+ int ret;
+
+ ENTER();
+
+ dev = (struct as102_dev_t *) fe->tuner_priv;
+ if (dev == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dev->bus_adap.lock))
+ return -EBUSY;
+
+ if (acquire) {
+ if (elna_enable)
+ as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0);
+
+ ret = as10x_cmd_turn_on(&dev->bus_adap);
+ } else {
+ ret = as10x_cmd_turn_off(&dev->bus_adap);
+ }
+
+ mutex_unlock(&dev->bus_adap.lock);
+
+ LEAVE();
+ return ret;
+}
+
+static struct dvb_frontend_ops as102_fe_ops = {
+ .info = {
+ .name = "Unknown AS102 device",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_INVERSION_AUTO
+ | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
+ | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO
+ | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
+ | 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
+ },
+
+ .set_frontend = as102_fe_set_frontend,
+ .get_frontend = as102_fe_get_frontend,
+ .get_tune_settings = as102_fe_get_tune_settings,
+
+ .read_status = as102_fe_read_status,
+ .read_snr = as102_fe_read_snr,
+ .read_ber = as102_fe_read_ber,
+ .read_signal_strength = as102_fe_read_signal_strength,
+ .read_ucblocks = as102_fe_read_ucblocks,
+ .ts_bus_ctrl = as102_fe_ts_bus_ctrl,
+};
+
+int as102_dvb_unregister_fe(struct dvb_frontend *fe)
+{
+ /* unregister frontend */
+ dvb_unregister_frontend(fe);
+
+ /* detach frontend */
+ dvb_frontend_detach(fe);
+
+ return 0;
+}
+
+int as102_dvb_register_fe(struct as102_dev_t *as102_dev,
+ struct dvb_frontend *dvb_fe)
+{
+ int errno;
+ struct dvb_adapter *dvb_adap;
+
+ if (as102_dev == NULL)
+ return -EINVAL;
+
+ /* extract dvb_adapter */
+ dvb_adap = &as102_dev->dvb_adap;
+
+ /* init frontend callback ops */
+ memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
+ strncpy(dvb_fe->ops.info.name, as102_dev->name,
+ sizeof(dvb_fe->ops.info.name));
+
+ /* register dbvb frontend */
+ errno = dvb_register_frontend(dvb_adap, dvb_fe);
+ if (errno == 0)
+ dvb_fe->tuner_priv = as102_dev;
+
+ return errno;
+}
+
+static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst,
+ struct as10x_tps *as10x_tps)
+{
+
+ struct dvb_ofdm_parameters *fe_tps = &dst->u.ofdm;
+
+ /* extract consteallation */
+ switch (as10x_tps->constellation) {
+ case CONST_QPSK:
+ fe_tps->constellation = QPSK;
+ break;
+ case CONST_QAM16:
+ fe_tps->constellation = QAM_16;
+ break;
+ case CONST_QAM64:
+ fe_tps->constellation = QAM_64;
+ break;
+ }
+
+ /* extract hierarchy */
+ switch (as10x_tps->hierarchy) {
+ case HIER_NONE:
+ fe_tps->hierarchy_information = HIERARCHY_NONE;
+ break;
+ case HIER_ALPHA_1:
+ fe_tps->hierarchy_information = HIERARCHY_1;
+ break;
+ case HIER_ALPHA_2:
+ fe_tps->hierarchy_information = HIERARCHY_2;
+ break;
+ case HIER_ALPHA_4:
+ fe_tps->hierarchy_information = HIERARCHY_4;
+ break;
+ }
+
+ /* extract code rate HP */
+ switch (as10x_tps->code_rate_HP) {
+ case CODE_RATE_1_2:
+ fe_tps->code_rate_HP = FEC_1_2;
+ break;
+ case CODE_RATE_2_3:
+ fe_tps->code_rate_HP = FEC_2_3;
+ break;
+ case CODE_RATE_3_4:
+ fe_tps->code_rate_HP = FEC_3_4;
+ break;
+ case CODE_RATE_5_6:
+ fe_tps->code_rate_HP = FEC_5_6;
+ break;
+ case CODE_RATE_7_8:
+ fe_tps->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ /* extract code rate LP */
+ switch (as10x_tps->code_rate_LP) {
+ case CODE_RATE_1_2:
+ fe_tps->code_rate_LP = FEC_1_2;
+ break;
+ case CODE_RATE_2_3:
+ fe_tps->code_rate_LP = FEC_2_3;
+ break;
+ case CODE_RATE_3_4:
+ fe_tps->code_rate_LP = FEC_3_4;
+ break;
+ case CODE_RATE_5_6:
+ fe_tps->code_rate_LP = FEC_5_6;
+ break;
+ case CODE_RATE_7_8:
+ fe_tps->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ /* extract guard interval */
+ switch (as10x_tps->guard_interval) {
+ case GUARD_INT_1_32:
+ fe_tps->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case GUARD_INT_1_16:
+ fe_tps->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case GUARD_INT_1_8:
+ fe_tps->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case GUARD_INT_1_4:
+ fe_tps->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ /* extract transmission mode */
+ switch (as10x_tps->transmission_mode) {
+ case TRANS_MODE_2K:
+ fe_tps->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case TRANS_MODE_8K:
+ fe_tps->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+}
+
+static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg)
+{
+ uint8_t c;
+
+ switch (arg) {
+ case FEC_1_2:
+ c = CODE_RATE_1_2;
+ break;
+ case FEC_2_3:
+ c = CODE_RATE_2_3;
+ break;
+ case FEC_3_4:
+ c = CODE_RATE_3_4;
+ break;
+ case FEC_5_6:
+ c = CODE_RATE_5_6;
+ break;
+ case FEC_7_8:
+ c = CODE_RATE_7_8;
+ break;
+ default:
+ c = CODE_RATE_UNKNOWN;
+ break;
+ }
+
+ return c;
+}
+
+static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args,
+ struct dvb_frontend_parameters *params)
+{
+
+ /* set frequency */
+ tune_args->freq = params->frequency / 1000;
+
+ /* fix interleaving_mode */
+ tune_args->interleaving_mode = INTLV_NATIVE;
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_8_MHZ:
+ tune_args->bandwidth = BW_8_MHZ;
+ break;
+ case BANDWIDTH_7_MHZ:
+ tune_args->bandwidth = BW_7_MHZ;
+ break;
+ case BANDWIDTH_6_MHZ:
+ tune_args->bandwidth = BW_6_MHZ;
+ break;
+ default:
+ tune_args->bandwidth = BW_8_MHZ;
+ }
+
+ switch (params->u.ofdm.guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ tune_args->guard_interval = GUARD_INT_1_32;
+ break;
+ case GUARD_INTERVAL_1_16:
+ tune_args->guard_interval = GUARD_INT_1_16;
+ break;
+ case GUARD_INTERVAL_1_8:
+ tune_args->guard_interval = GUARD_INT_1_8;
+ break;
+ case GUARD_INTERVAL_1_4:
+ tune_args->guard_interval = GUARD_INT_1_4;
+ break;
+ case GUARD_INTERVAL_AUTO:
+ default:
+ tune_args->guard_interval = GUARD_UNKNOWN;
+ break;
+ }
+
+ switch (params->u.ofdm.constellation) {
+ case QPSK:
+ tune_args->constellation = CONST_QPSK;
+ break;
+ case QAM_16:
+ tune_args->constellation = CONST_QAM16;
+ break;
+ case QAM_64:
+ tune_args->constellation = CONST_QAM64;
+ break;
+ default:
+ tune_args->constellation = CONST_UNKNOWN;
+ break;
+ }
+
+ switch (params->u.ofdm.transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ tune_args->transmission_mode = TRANS_MODE_2K;
+ break;
+ case TRANSMISSION_MODE_8K:
+ tune_args->transmission_mode = TRANS_MODE_8K;
+ break;
+ default:
+ tune_args->transmission_mode = TRANS_MODE_UNKNOWN;
+ }
+
+ switch (params->u.ofdm.hierarchy_information) {
+ case HIERARCHY_NONE:
+ tune_args->hierarchy = HIER_NONE;
+ break;
+ case HIERARCHY_1:
+ tune_args->hierarchy = HIER_ALPHA_1;
+ break;
+ case HIERARCHY_2:
+ tune_args->hierarchy = HIER_ALPHA_2;
+ break;
+ case HIERARCHY_4:
+ tune_args->hierarchy = HIER_ALPHA_4;
+ break;
+ case HIERARCHY_AUTO:
+ tune_args->hierarchy = HIER_UNKNOWN;
+ break;
+ }
+
+ dprintk(debug, "tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n",
+ params->frequency,
+ tune_args->bandwidth,
+ tune_args->guard_interval);
+
+ /*
+ * Detect a hierarchy selection
+ * if HP/LP are both set to FEC_NONE, HP will be selected.
+ */
+ if ((tune_args->hierarchy != HIER_NONE) &&
+ ((params->u.ofdm.code_rate_LP == FEC_NONE) ||
+ (params->u.ofdm.code_rate_HP == FEC_NONE))) {
+
+ if (params->u.ofdm.code_rate_LP == FEC_NONE) {
+ tune_args->hier_select = HIER_HIGH_PRIORITY;
+ tune_args->code_rate =
+ as102_fe_get_code_rate(params->u.ofdm.code_rate_HP);
+ }
+
+ if (params->u.ofdm.code_rate_HP == FEC_NONE) {
+ tune_args->hier_select = HIER_LOW_PRIORITY;
+ tune_args->code_rate =
+ as102_fe_get_code_rate(params->u.ofdm.code_rate_LP);
+ }
+
+ dprintk(debug, "\thierarchy: 0x%02x "
+ "selected: %s code_rate_%s: 0x%02x\n",
+ tune_args->hierarchy,
+ tune_args->hier_select == HIER_HIGH_PRIORITY ?
+ "HP" : "LP",
+ tune_args->hier_select == HIER_HIGH_PRIORITY ?
+ "HP" : "LP",
+ tune_args->code_rate);
+ } else {
+ tune_args->code_rate =
+ as102_fe_get_code_rate(params->u.ofdm.code_rate_HP);
+ }
+}
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c
new file mode 100644
index 000000000000..c019df933cc9
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fw.c
@@ -0,0 +1,251 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+
+#include "as102_drv.h"
+#include "as102_fw.h"
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+char as102_st_fw1[] = "as102_data1_st.hex";
+char as102_st_fw2[] = "as102_data2_st.hex";
+char as102_dt_fw1[] = "as102_data1_dt.hex";
+char as102_dt_fw2[] = "as102_data2_dt.hex";
+
+static unsigned char atohx(unsigned char *dst, char *src)
+{
+ unsigned char value = 0;
+
+ char msb = tolower(*src) - '0';
+ char lsb = tolower(*(src + 1)) - '0';
+
+ if (msb > 9)
+ msb -= 7;
+ if (lsb > 9)
+ lsb -= 7;
+
+ *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
+ return value;
+}
+
+/*
+ * Parse INTEL HEX firmware file to extract address and data.
+ */
+static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
+ unsigned char *data, int *dataLength,
+ unsigned char *addr_has_changed) {
+
+ int count = 0;
+ unsigned char *src, dst;
+
+ if (*fw_data++ != ':') {
+ printk(KERN_ERR "invalid firmware file\n");
+ return -EFAULT;
+ }
+
+ /* locate end of line */
+ for (src = fw_data; *src != '\n'; src += 2) {
+ atohx(&dst, src);
+ /* parse line to split addr / data */
+ switch (count) {
+ case 0:
+ *dataLength = dst;
+ break;
+ case 1:
+ addr[2] = dst;
+ break;
+ case 2:
+ addr[3] = dst;
+ break;
+ case 3:
+ /* check if data is an address */
+ if (dst == 0x04)
+ *addr_has_changed = 1;
+ else
+ *addr_has_changed = 0;
+ break;
+ case 4:
+ case 5:
+ if (*addr_has_changed)
+ addr[(count - 4)] = dst;
+ else
+ data[(count - 4)] = dst;
+ break;
+ default:
+ data[(count - 4)] = dst;
+ break;
+ }
+ count++;
+ }
+
+ /* return read value + ':' + '\n' */
+ return (count * 2) + 2;
+}
+
+static int as102_firmware_upload(struct as102_bus_adapter_t *bus_adap,
+ unsigned char *cmd,
+ const struct firmware *firmware) {
+
+ struct as10x_fw_pkt_t fw_pkt;
+ int total_read_bytes = 0, errno = 0;
+ unsigned char addr_has_changed = 0;
+
+ ENTER();
+
+ for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
+ int read_bytes = 0, data_len = 0;
+
+ /* parse intel hex line */
+ read_bytes = parse_hex_line(
+ (u8 *) (firmware->data + total_read_bytes),
+ fw_pkt.raw.address,
+ fw_pkt.raw.data,
+ &data_len,
+ &addr_has_changed);
+
+ if (read_bytes <= 0)
+ goto error;
+
+ /* detect the end of file */
+ total_read_bytes += read_bytes;
+ if (total_read_bytes == firmware->size) {
+ fw_pkt.u.request[0] = 0x00;
+ fw_pkt.u.request[1] = 0x03;
+
+ /* send EOF command */
+ errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+ (uint8_t *)
+ &fw_pkt, 2, 0);
+ if (errno < 0)
+ goto error;
+ } else {
+ if (!addr_has_changed) {
+ /* prepare command to send */
+ fw_pkt.u.request[0] = 0x00;
+ fw_pkt.u.request[1] = 0x01;
+
+ data_len += sizeof(fw_pkt.u.request);
+ data_len += sizeof(fw_pkt.raw.address);
+
+ /* send cmd to device */
+ errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+ (uint8_t *)
+ &fw_pkt,
+ data_len,
+ 0);
+ if (errno < 0)
+ goto error;
+ }
+ }
+ }
+error:
+ LEAVE();
+ return (errno == 0) ? total_read_bytes : errno;
+}
+
+int as102_fw_upload(struct as102_bus_adapter_t *bus_adap)
+{
+ int errno = -EFAULT;
+ const struct firmware *firmware;
+ unsigned char *cmd_buf = NULL;
+ char *fw1, *fw2;
+
+#if defined(CONFIG_AS102_USB)
+ struct usb_device *dev = bus_adap->usb_dev;
+#endif
+#if defined(CONFIG_AS102_SPI)
+ struct spi_device *dev = bus_adap->spi_dev;
+#endif
+ ENTER();
+
+ /* select fw file to upload */
+ if (dual_tuner) {
+ fw1 = as102_dt_fw1;
+ fw2 = as102_dt_fw2;
+ } else {
+ fw1 = as102_st_fw1;
+ fw2 = as102_st_fw2;
+ }
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+ /* allocate buffer to store firmware upload command and data */
+ cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
+ if (cmd_buf == NULL) {
+ errno = -ENOMEM;
+ goto error;
+ }
+
+ /* request kernel to locate firmware file: part1 */
+ errno = request_firmware(&firmware, fw1, &dev->dev);
+ if (errno < 0) {
+ printk(KERN_ERR "%s: unable to locate firmware file: %s\n",
+ DRIVER_NAME, fw1);
+ goto error;
+ }
+
+ /* initiate firmware upload */
+ errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+ if (errno < 0) {
+ printk(KERN_ERR "%s: error during firmware upload part1\n",
+ DRIVER_NAME);
+ goto error;
+ }
+
+ printk(KERN_INFO "%s: fimrware: %s loaded with success\n",
+ DRIVER_NAME, fw1);
+ release_firmware(firmware);
+
+ /* wait for boot to complete */
+ mdelay(100);
+
+ /* request kernel to locate firmware file: part2 */
+ errno = request_firmware(&firmware, fw2, &dev->dev);
+ if (errno < 0) {
+ printk(KERN_ERR "%s: unable to locate firmware file: %s\n",
+ DRIVER_NAME, fw2);
+ goto error;
+ }
+
+ /* initiate firmware upload */
+ errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+ if (errno < 0) {
+ printk(KERN_ERR "%s: error during firmware upload part2\n",
+ DRIVER_NAME);
+ goto error;
+ }
+
+ printk(KERN_INFO "%s: fimrware: %s loaded with success\n",
+ DRIVER_NAME, fw2);
+error:
+ /* free data buffer */
+ kfree(cmd_buf);
+ /* release firmware if needed */
+ if (firmware != NULL)
+ release_firmware(firmware);
+#endif
+ LEAVE();
+ return errno;
+}
+#endif
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fw.h b/drivers/staging/media/as102/as102_fw.h
new file mode 100644
index 000000000000..27e5347e2e19
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fw.h
@@ -0,0 +1,42 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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.
+ */
+#define MAX_FW_PKT_SIZE 64
+
+extern int dual_tuner;
+
+#pragma pack(1)
+struct as10x_raw_fw_pkt {
+ unsigned char address[4];
+ unsigned char data[MAX_FW_PKT_SIZE - 6];
+};
+
+struct as10x_fw_pkt_t {
+ union {
+ unsigned char request[2];
+ unsigned char length[2];
+ } u;
+ struct as10x_raw_fw_pkt raw;
+};
+#pragma pack()
+
+#ifdef __KERNEL__
+int as102_fw_upload(struct as102_bus_adapter_t *bus_adap);
+#endif
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c
new file mode 100644
index 000000000000..264be2dbd2a4
--- /dev/null
+++ b/drivers/staging/media/as102/as102_usb_drv.c
@@ -0,0 +1,478 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+
+#include "as102_drv.h"
+#include "as102_usb_drv.h"
+#include "as102_fw.h"
+
+static void as102_usb_disconnect(struct usb_interface *interface);
+static int as102_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+
+static int as102_usb_start_stream(struct as102_dev_t *dev);
+static void as102_usb_stop_stream(struct as102_dev_t *dev);
+
+static int as102_open(struct inode *inode, struct file *file);
+static int as102_release(struct inode *inode, struct file *file);
+
+static struct usb_device_id as102_usb_id_table[] = {
+ { USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) },
+ { USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) },
+ { USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) },
+ { USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) },
+ { } /* Terminating entry */
+};
+
+/* Note that this table must always have the same number of entries as the
+ as102_usb_id_table struct */
+static const char *as102_device_names[] = {
+ AS102_REFERENCE_DESIGN,
+ AS102_PCTV_74E,
+ AS102_ELGATO_EYETV_DTT_NAME,
+ AS102_NBOX_DVBT_DONGLE_NAME,
+ NULL /* Terminating entry */
+};
+
+struct usb_driver as102_usb_driver = {
+ .name = DRIVER_FULL_NAME,
+ .probe = as102_usb_probe,
+ .disconnect = as102_usb_disconnect,
+ .id_table = as102_usb_id_table
+};
+
+static const struct file_operations as102_dev_fops = {
+ .owner = THIS_MODULE,
+ .open = as102_open,
+ .release = as102_release,
+};
+
+static struct usb_class_driver as102_usb_class_driver = {
+ .name = "aton2-%d",
+ .fops = &as102_dev_fops,
+ .minor_base = AS102_DEVICE_MAJOR,
+};
+
+static int as102_usb_xfer_cmd(struct as102_bus_adapter_t *bus_adap,
+ unsigned char *send_buf, int send_buf_len,
+ unsigned char *recv_buf, int recv_buf_len)
+{
+ int ret = 0;
+ ENTER();
+
+ if (send_buf != NULL) {
+ ret = usb_control_msg(bus_adap->usb_dev,
+ usb_sndctrlpipe(bus_adap->usb_dev, 0),
+ AS102_USB_DEVICE_TX_CTRL_CMD,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ bus_adap->cmd_xid, /* value */
+ 0, /* index */
+ send_buf, send_buf_len,
+ USB_CTRL_SET_TIMEOUT /* 200 */);
+ if (ret < 0) {
+ dprintk(debug, "usb_control_msg(send) failed, err %i\n",
+ ret);
+ return ret;
+ }
+
+ if (ret != send_buf_len) {
+ dprintk(debug, "only wrote %d of %d bytes\n",
+ ret, send_buf_len);
+ return -1;
+ }
+ }
+
+ if (recv_buf != NULL) {
+#ifdef TRACE
+ dprintk(debug, "want to read: %d bytes\n", recv_buf_len);
+#endif
+ ret = usb_control_msg(bus_adap->usb_dev,
+ usb_rcvctrlpipe(bus_adap->usb_dev, 0),
+ AS102_USB_DEVICE_RX_CTRL_CMD,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ bus_adap->cmd_xid, /* value */
+ 0, /* index */
+ recv_buf, recv_buf_len,
+ USB_CTRL_GET_TIMEOUT /* 200 */);
+ if (ret < 0) {
+ dprintk(debug, "usb_control_msg(recv) failed, err %i\n",
+ ret);
+ return ret;
+ }
+#ifdef TRACE
+ dprintk(debug, "read %d bytes\n", recv_buf_len);
+#endif
+ }
+
+ LEAVE();
+ return ret;
+}
+
+static int as102_send_ep1(struct as102_bus_adapter_t *bus_adap,
+ unsigned char *send_buf,
+ int send_buf_len,
+ int swap32)
+{
+ int ret = 0, actual_len;
+
+ ret = usb_bulk_msg(bus_adap->usb_dev,
+ usb_sndbulkpipe(bus_adap->usb_dev, 1),
+ send_buf, send_buf_len, &actual_len, 200);
+ if (ret) {
+ dprintk(debug, "usb_bulk_msg(send) failed, err %i\n", ret);
+ return ret;
+ }
+
+ if (actual_len != send_buf_len) {
+ dprintk(debug, "only wrote %d of %d bytes\n",
+ actual_len, send_buf_len);
+ return -1;
+ }
+ return ret ? ret : actual_len;
+}
+
+static int as102_read_ep2(struct as102_bus_adapter_t *bus_adap,
+ unsigned char *recv_buf, int recv_buf_len)
+{
+ int ret = 0, actual_len;
+
+ if (recv_buf == NULL)
+ return -EINVAL;
+
+ ret = usb_bulk_msg(bus_adap->usb_dev,
+ usb_rcvbulkpipe(bus_adap->usb_dev, 2),
+ recv_buf, recv_buf_len, &actual_len, 200);
+ if (ret) {
+ dprintk(debug, "usb_bulk_msg(recv) failed, err %i\n", ret);
+ return ret;
+ }
+
+ if (actual_len != recv_buf_len) {
+ dprintk(debug, "only read %d of %d bytes\n",
+ actual_len, recv_buf_len);
+ return -1;
+ }
+ return ret ? ret : actual_len;
+}
+
+struct as102_priv_ops_t as102_priv_ops = {
+ .upload_fw_pkt = as102_send_ep1,
+ .xfer_cmd = as102_usb_xfer_cmd,
+ .as102_read_ep2 = as102_read_ep2,
+ .start_stream = as102_usb_start_stream,
+ .stop_stream = as102_usb_stop_stream,
+};
+
+static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb)
+{
+ int err;
+
+ usb_fill_bulk_urb(urb,
+ dev->bus_adap.usb_dev,
+ usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2),
+ urb->transfer_buffer,
+ AS102_USB_BUF_SIZE,
+ as102_urb_stream_irq,
+ dev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err)
+ dprintk(debug, "%s: usb_submit_urb failed\n", __func__);
+
+ return err;
+}
+
+void as102_urb_stream_irq(struct urb *urb)
+{
+ struct as102_dev_t *as102_dev = urb->context;
+
+ if (urb->actual_length > 0) {
+ dvb_dmx_swfilter(&as102_dev->dvb_dmx,
+ urb->transfer_buffer,
+ urb->actual_length);
+ } else {
+ if (urb->actual_length == 0)
+ memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE);
+ }
+
+ /* is not stopped, re-submit urb */
+ if (as102_dev->streaming)
+ as102_submit_urb_stream(as102_dev, urb);
+}
+
+static void as102_free_usb_stream_buffer(struct as102_dev_t *dev)
+{
+ int i;
+
+ ENTER();
+
+ for (i = 0; i < MAX_STREAM_URB; i++)
+ usb_free_urb(dev->stream_urb[i]);
+
+ usb_free_coherent(dev->bus_adap.usb_dev,
+ MAX_STREAM_URB * AS102_USB_BUF_SIZE,
+ dev->stream,
+ dev->dma_addr);
+ LEAVE();
+}
+
+static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
+{
+ int i, ret = 0;
+
+ ENTER();
+
+ dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev,
+ MAX_STREAM_URB * AS102_USB_BUF_SIZE,
+ GFP_KERNEL,
+ &dev->dma_addr);
+ if (!dev->stream) {
+ dprintk(debug, "%s: usb_buffer_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE);
+
+ /* init urb buffers */
+ for (i = 0; i < MAX_STREAM_URB; i++) {
+ struct urb *urb;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (urb == NULL) {
+ dprintk(debug, "%s: usb_alloc_urb failed\n", __func__);
+ as102_free_usb_stream_buffer(dev);
+ return -ENOMEM;
+ }
+
+ urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE);
+ urb->transfer_buffer_length = AS102_USB_BUF_SIZE;
+
+ dev->stream_urb[i] = urb;
+ }
+ LEAVE();
+ return ret;
+}
+
+static void as102_usb_stop_stream(struct as102_dev_t *dev)
+{
+ int i;
+
+ for (i = 0; i < MAX_STREAM_URB; i++)
+ usb_kill_urb(dev->stream_urb[i]);
+}
+
+static int as102_usb_start_stream(struct as102_dev_t *dev)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < MAX_STREAM_URB; i++) {
+ ret = as102_submit_urb_stream(dev, dev->stream_urb[i]);
+ if (ret) {
+ as102_usb_stop_stream(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void as102_usb_release(struct kref *kref)
+{
+ struct as102_dev_t *as102_dev;
+
+ ENTER();
+
+ as102_dev = container_of(kref, struct as102_dev_t, kref);
+ if (as102_dev != NULL) {
+ usb_put_dev(as102_dev->bus_adap.usb_dev);
+ kfree(as102_dev);
+ }
+
+ LEAVE();
+}
+
+static void as102_usb_disconnect(struct usb_interface *intf)
+{
+ struct as102_dev_t *as102_dev;
+
+ ENTER();
+
+ /* extract as102_dev_t from usb_device private data */
+ as102_dev = usb_get_intfdata(intf);
+
+ /* unregister dvb layer */
+ as102_dvb_unregister(as102_dev);
+
+ /* free usb buffers */
+ as102_free_usb_stream_buffer(as102_dev);
+
+ usb_set_intfdata(intf, NULL);
+
+ /* usb unregister device */
+ usb_deregister_dev(intf, &as102_usb_class_driver);
+
+ /* decrement usage counter */
+ kref_put(&as102_dev->kref, as102_usb_release);
+
+ printk(KERN_INFO "%s: device has been disconnected\n", DRIVER_NAME);
+
+ LEAVE();
+}
+
+static int as102_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct as102_dev_t *as102_dev;
+ int i;
+
+ ENTER();
+
+ as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL);
+ if (as102_dev == NULL) {
+ err("%s: kzalloc failed", __func__);
+ return -ENOMEM;
+ }
+
+ /* This should never actually happen */
+ if ((sizeof(as102_usb_id_table) / sizeof(struct usb_device_id)) !=
+ (sizeof(as102_device_names) / sizeof(const char *))) {
+ printk(KERN_ERR "Device names table invalid size");
+ return -EINVAL;
+ }
+
+ /* Assign the user-friendly device name */
+ for (i = 0; i < (sizeof(as102_usb_id_table) /
+ sizeof(struct usb_device_id)); i++) {
+ if (id == &as102_usb_id_table[i])
+ as102_dev->name = as102_device_names[i];
+ }
+
+ if (as102_dev->name == NULL)
+ as102_dev->name = "Unknown AS102 device";
+
+ /* set private callback functions */
+ as102_dev->bus_adap.ops = &as102_priv_ops;
+
+ /* init cmd token for usb bus */
+ as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c;
+ as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r;
+
+ /* init kernel device reference */
+ kref_init(&as102_dev->kref);
+
+ /* store as102 device to usb_device private data */
+ usb_set_intfdata(intf, (void *) as102_dev);
+
+ /* store in as102 device the usb_device pointer */
+ as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf));
+
+ /* we can register the device now, as it is ready */
+ ret = usb_register_dev(intf, &as102_usb_class_driver);
+ if (ret < 0) {
+ /* something prevented us from registering this driver */
+ err("%s: usb_register_dev() failed (errno = %d)",
+ __func__, ret);
+ goto failed;
+ }
+
+ printk(KERN_INFO "%s: device has been detected\n", DRIVER_NAME);
+
+ /* request buffer allocation for streaming */
+ ret = as102_alloc_usb_stream_buffer(as102_dev);
+ if (ret != 0)
+ goto failed;
+
+ /* register dvb layer */
+ ret = as102_dvb_register(as102_dev);
+
+ LEAVE();
+ return ret;
+
+failed:
+ usb_set_intfdata(intf, NULL);
+ kfree(as102_dev);
+ return ret;
+}
+
+static int as102_open(struct inode *inode, struct file *file)
+{
+ int ret = 0, minor = 0;
+ struct usb_interface *intf = NULL;
+ struct as102_dev_t *dev = NULL;
+
+ ENTER();
+
+ /* read minor from inode */
+ minor = iminor(inode);
+
+ /* fetch device from usb interface */
+ intf = usb_find_interface(&as102_usb_driver, minor);
+ if (intf == NULL) {
+ printk(KERN_ERR "%s: can't find device for minor %d\n",
+ __func__, minor);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* get our device */
+ dev = usb_get_intfdata(intf);
+ if (dev == NULL) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ /* save our device object in the file's private structure */
+ file->private_data = dev;
+
+ /* increment our usage count for the device */
+ kref_get(&dev->kref);
+
+exit:
+ LEAVE();
+ return ret;
+}
+
+static int as102_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct as102_dev_t *dev = NULL;
+
+ ENTER();
+
+ dev = file->private_data;
+ if (dev != NULL) {
+ /* decrement the count on our device */
+ kref_put(&dev->kref, as102_usb_release);
+ }
+
+ LEAVE();
+ return ret;
+}
+
+MODULE_DEVICE_TABLE(usb, as102_usb_id_table);
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_usb_drv.h b/drivers/staging/media/as102/as102_usb_drv.h
new file mode 100644
index 000000000000..fb1fc41dcd79
--- /dev/null
+++ b/drivers/staging/media/as102/as102_usb_drv.h
@@ -0,0 +1,59 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/version.h>
+
+#ifndef _AS102_USB_DRV_H_
+#define _AS102_USB_DRV_H_
+
+#define AS102_USB_DEVICE_TX_CTRL_CMD 0xF1
+#define AS102_USB_DEVICE_RX_CTRL_CMD 0xF2
+
+/* define these values to match the supported devices */
+
+/* Abilis system: "TITAN" */
+#define AS102_REFERENCE_DESIGN "Abilis Systems DVB-Titan"
+#define AS102_USB_DEVICE_VENDOR_ID 0x1BA6
+#define AS102_USB_DEVICE_PID_0001 0x0001
+
+/* PCTV Systems: PCTV picoStick (74e) */
+#define AS102_PCTV_74E "PCTV Systems picoStick (74e)"
+#define PCTV_74E_USB_VID 0x2013
+#define PCTV_74E_USB_PID 0x0246
+
+/* Elgato: EyeTV DTT Deluxe */
+#define AS102_ELGATO_EYETV_DTT_NAME "Elgato EyeTV DTT Deluxe"
+#define ELGATO_EYETV_DTT_USB_VID 0x0fd9
+#define ELGATO_EYETV_DTT_USB_PID 0x002c
+
+/* nBox: nBox DVB-T Dongle */
+#define AS102_NBOX_DVBT_DONGLE_NAME "nBox DVB-T Dongle"
+#define NBOX_DVBT_DONGLE_USB_VID 0x0b89
+#define NBOX_DVBT_DONGLE_USB_PID 0x0007
+
+void as102_urb_stream_irq(struct urb *urb);
+
+struct as10x_usb_token_cmd_t {
+ /* token cmd */
+ struct as10x_cmd_t c;
+ /* token response */
+ struct as10x_cmd_t r;
+};
+#endif
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c
new file mode 100644
index 000000000000..0dcba8065780
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd.c
@@ -0,0 +1,452 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+/**
+ * as10x_cmd_turn_on - send turn on command to AS10x
+ * @phandle: pointer to AS10x handle
+ *
+ * Return 0 when no error, < 0 in case of error.
+ */
+int as10x_cmd_turn_on(as10x_handle_t *phandle)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.turn_on.req));
+
+ /* fill command */
+ pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.turn_on.req) +
+ HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.turn_on.rsp) +
+ HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_turn_off - send turn off command to AS10x
+ * @phandle: pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_turn_off(as10x_handle_t *phandle)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.turn_off.req));
+
+ /* fill command */
+ pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(
+ phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.turn_off.req) + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_set_tune - send set tune command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @ptune: tune parameters
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_set_tune(as10x_handle_t *phandle, struct as10x_tune_args *ptune)
+{
+ int error;
+ struct as10x_cmd_t *preq, *prsp;
+
+ ENTER();
+
+ preq = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(preq, (++phandle->cmd_xid),
+ sizeof(preq->body.set_tune.req));
+
+ /* fill command */
+ preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE);
+ preq->body.set_tune.req.args.freq = cpu_to_le32(ptune->freq);
+ preq->body.set_tune.req.args.bandwidth = ptune->bandwidth;
+ preq->body.set_tune.req.args.hier_select = ptune->hier_select;
+ preq->body.set_tune.req.args.constellation = ptune->constellation;
+ preq->body.set_tune.req.args.hierarchy = ptune->hierarchy;
+ preq->body.set_tune.req.args.interleaving_mode =
+ ptune->interleaving_mode;
+ preq->body.set_tune.req.args.code_rate = ptune->code_rate;
+ preq->body.set_tune.req.args.guard_interval = ptune->guard_interval;
+ preq->body.set_tune.req.args.transmission_mode =
+ ptune->transmission_mode;
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) preq,
+ sizeof(preq->body.set_tune.req)
+ + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.set_tune.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_get_tune_status - send get tune status command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @pstatus: pointer to updated status structure of the current tune
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_tune_status(as10x_handle_t *phandle,
+ struct as10x_tune_status *pstatus)
+{
+ int error;
+ struct as10x_cmd_t *preq, *prsp;
+
+ ENTER();
+
+ preq = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(preq, (++phandle->cmd_xid),
+ sizeof(preq->body.get_tune_status.req));
+
+ /* fill command */
+ preq->body.get_tune_status.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_GETTUNESTAT);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(
+ phandle,
+ (uint8_t *) preq,
+ sizeof(preq->body.get_tune_status.req) + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP);
+ if (error < 0)
+ goto out;
+
+ /* Response OK -> get response data */
+ pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state;
+ pstatus->signal_strength =
+ le16_to_cpu(prsp->body.get_tune_status.rsp.sts.signal_strength);
+ pstatus->PER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.PER);
+ pstatus->BER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.BER);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * send get TPS command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @ptps: pointer to TPS parameters structure
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_tps(as10x_handle_t *phandle, struct as10x_tps *ptps)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.get_tps.req));
+
+ /* fill command */
+ pcmd->body.get_tune_status.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_GETTPS);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) pcmd,
+ sizeof(pcmd->body.get_tps.req) +
+ HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.get_tps.rsp) +
+ HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP);
+ if (error < 0)
+ goto out;
+
+ /* Response OK -> get response data */
+ ptps->constellation = prsp->body.get_tps.rsp.tps.constellation;
+ ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy;
+ ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode;
+ ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP;
+ ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP;
+ ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval;
+ ptps->transmission_mode = prsp->body.get_tps.rsp.tps.transmission_mode;
+ ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP;
+ ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP;
+ ptps->cell_ID = le16_to_cpu(prsp->body.get_tps.rsp.tps.cell_ID);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_get_demod_stats - send get demod stats command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @pdemod_stats: pointer to demod stats parameters structure
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_demod_stats(as10x_handle_t *phandle,
+ struct as10x_demod_stats *pdemod_stats)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.get_demod_stats.req));
+
+ /* fill command */
+ pcmd->body.get_demod_stats.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) pcmd,
+ sizeof(pcmd->body.get_demod_stats.req)
+ + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.get_demod_stats.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_DEMOD_STATS_RSP);
+ if (error < 0)
+ goto out;
+
+ /* Response OK -> get response data */
+ pdemod_stats->frame_count =
+ le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.frame_count);
+ pdemod_stats->bad_frame_count =
+ le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bad_frame_count);
+ pdemod_stats->bytes_fixed_by_rs =
+ le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs);
+ pdemod_stats->mer =
+ le16_to_cpu(prsp->body.get_demod_stats.rsp.stats.mer);
+ pdemod_stats->has_started =
+ prsp->body.get_demod_stats.rsp.stats.has_started;
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_get_impulse_resp - send get impulse response command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @is_ready: pointer to value indicating when impulse
+ * response data is ready
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_impulse_resp(as10x_handle_t *phandle,
+ uint8_t *is_ready)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.get_impulse_rsp.req));
+
+ /* fill command */
+ pcmd->body.get_impulse_rsp.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) pcmd,
+ sizeof(pcmd->body.get_impulse_rsp.req)
+ + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.get_impulse_rsp.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_IMPULSE_RESP_RSP);
+ if (error < 0)
+ goto out;
+
+ /* Response OK -> get response data */
+ *is_ready = prsp->body.get_impulse_rsp.rsp.is_ready;
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_build - build AS10x command header
+ * @pcmd: pointer to AS10x command buffer
+ * @xid: sequence id of the command
+ * @cmd_len: length of the command
+ */
+void as10x_cmd_build(struct as10x_cmd_t *pcmd,
+ uint16_t xid, uint16_t cmd_len)
+{
+ pcmd->header.req_id = cpu_to_le16(xid);
+ pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID);
+ pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION);
+ pcmd->header.data_len = cpu_to_le16(cmd_len);
+}
+
+/**
+ * as10x_rsp_parse - Parse command response
+ * @prsp: pointer to AS10x command buffer
+ * @proc_id: id of the command
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
+{
+ int error;
+
+ /* extract command error code */
+ error = prsp->body.common.rsp.error;
+
+ if ((error == 0) &&
+ (le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) {
+ return 0;
+ }
+
+ return AS10X_CMD_ERROR;
+}
diff --git a/drivers/staging/media/as102/as10x_cmd.h b/drivers/staging/media/as102/as10x_cmd.h
new file mode 100644
index 000000000000..01a716380e0a
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd.h
@@ -0,0 +1,540 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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 _AS10X_CMD_H_
+#define _AS10X_CMD_H_
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#endif
+
+#include "as10x_types.h"
+
+/*********************************/
+/* MACRO DEFINITIONS */
+/*********************************/
+#define AS10X_CMD_ERROR -1
+
+#define SERVICE_PROG_ID 0x0002
+#define SERVICE_PROG_VERSION 0x0001
+
+#define HIER_NONE 0x00
+#define HIER_LOW_PRIORITY 0x01
+
+#define HEADER_SIZE (sizeof(struct as10x_cmd_header_t))
+
+/* context request types */
+#define GET_CONTEXT_DATA 1
+#define SET_CONTEXT_DATA 2
+
+/* ODSP suspend modes */
+#define CFG_MODE_ODSP_RESUME 0
+#define CFG_MODE_ODSP_SUSPEND 1
+
+/* Dump memory size */
+#define DUMP_BLOCK_SIZE_MAX 0x20
+
+/*********************************/
+/* TYPE DEFINITION */
+/*********************************/
+typedef enum {
+ CONTROL_PROC_TURNON = 0x0001,
+ CONTROL_PROC_TURNON_RSP = 0x0100,
+ CONTROL_PROC_SET_REGISTER = 0x0002,
+ CONTROL_PROC_SET_REGISTER_RSP = 0x0200,
+ CONTROL_PROC_GET_REGISTER = 0x0003,
+ CONTROL_PROC_GET_REGISTER_RSP = 0x0300,
+ CONTROL_PROC_SETTUNE = 0x000A,
+ CONTROL_PROC_SETTUNE_RSP = 0x0A00,
+ CONTROL_PROC_GETTUNESTAT = 0x000B,
+ CONTROL_PROC_GETTUNESTAT_RSP = 0x0B00,
+ CONTROL_PROC_GETTPS = 0x000D,
+ CONTROL_PROC_GETTPS_RSP = 0x0D00,
+ CONTROL_PROC_SETFILTER = 0x000E,
+ CONTROL_PROC_SETFILTER_RSP = 0x0E00,
+ CONTROL_PROC_REMOVEFILTER = 0x000F,
+ CONTROL_PROC_REMOVEFILTER_RSP = 0x0F00,
+ CONTROL_PROC_GET_IMPULSE_RESP = 0x0012,
+ CONTROL_PROC_GET_IMPULSE_RESP_RSP = 0x1200,
+ CONTROL_PROC_START_STREAMING = 0x0013,
+ CONTROL_PROC_START_STREAMING_RSP = 0x1300,
+ CONTROL_PROC_STOP_STREAMING = 0x0014,
+ CONTROL_PROC_STOP_STREAMING_RSP = 0x1400,
+ CONTROL_PROC_GET_DEMOD_STATS = 0x0015,
+ CONTROL_PROC_GET_DEMOD_STATS_RSP = 0x1500,
+ CONTROL_PROC_ELNA_CHANGE_MODE = 0x0016,
+ CONTROL_PROC_ELNA_CHANGE_MODE_RSP = 0x1600,
+ CONTROL_PROC_ODSP_CHANGE_MODE = 0x0017,
+ CONTROL_PROC_ODSP_CHANGE_MODE_RSP = 0x1700,
+ CONTROL_PROC_AGC_CHANGE_MODE = 0x0018,
+ CONTROL_PROC_AGC_CHANGE_MODE_RSP = 0x1800,
+
+ CONTROL_PROC_CONTEXT = 0x00FC,
+ CONTROL_PROC_CONTEXT_RSP = 0xFC00,
+ CONTROL_PROC_DUMP_MEMORY = 0x00FD,
+ CONTROL_PROC_DUMP_MEMORY_RSP = 0xFD00,
+ CONTROL_PROC_DUMPLOG_MEMORY = 0x00FE,
+ CONTROL_PROC_DUMPLOG_MEMORY_RSP = 0xFE00,
+ CONTROL_PROC_TURNOFF = 0x00FF,
+ CONTROL_PROC_TURNOFF_RSP = 0xFF00
+} control_proc;
+
+
+#pragma pack(1)
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ } rsp;
+} TURN_ON;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t err;
+ } rsp;
+} TURN_OFF;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* tune params */
+ struct as10x_tune_args args;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ } rsp;
+} SET_TUNE;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ /* tune status */
+ struct as10x_tune_status sts;
+ } rsp;
+} GET_TUNE_STATUS;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ /* tps details */
+ struct as10x_tps tps;
+ } rsp;
+} GET_TPS;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ } rsp;
+} COMMON;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* PID to filter */
+ uint16_t pid;
+ /* stream type (MPE, PSI/SI or PES )*/
+ uint8_t stream_type;
+ /* PID index in filter table */
+ uint8_t idx;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ /* Filter id */
+ uint8_t filter_id;
+ } rsp;
+} ADD_PID_FILTER;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* PID to remove */
+ uint16_t pid;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* response error */
+ uint8_t error;
+ } rsp;
+} DEL_PID_FILTER;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ } rsp;
+} START_STREAMING;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ } rsp;
+} STOP_STREAMING;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ /* demod stats */
+ struct as10x_demod_stats stats;
+ } rsp;
+} GET_DEMOD_STATS;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ /* impulse response ready */
+ uint8_t is_ready;
+ } rsp;
+} GET_IMPULSE_RESP;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* value to write (for set context)*/
+ struct as10x_register_value reg_val;
+ /* context tag */
+ uint16_t tag;
+ /* context request type */
+ uint16_t type;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* value read (for get context) */
+ struct as10x_register_value reg_val;
+ /* context request type */
+ uint16_t type;
+ /* error */
+ uint8_t error;
+ } rsp;
+} FW_CONTEXT;
+
+typedef union {
+ /* request */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* register description */
+ struct as10x_register_addr reg_addr;
+ /* register content */
+ struct as10x_register_value reg_val;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ } rsp;
+} SET_REGISTER;
+
+typedef union {
+ /* request */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* register description */
+ struct as10x_register_addr reg_addr;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ /* register content */
+ struct as10x_register_value reg_val;
+ } rsp;
+} GET_REGISTER;
+
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* mode */
+ uint8_t mode;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ } rsp;
+} CFG_CHANGE_MODE;
+
+struct as10x_cmd_header_t {
+ uint16_t req_id;
+ uint16_t prog;
+ uint16_t version;
+ uint16_t data_len;
+};
+
+#define DUMP_BLOCK_SIZE 16
+typedef union {
+ /* request */
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* dump memory type request */
+ uint8_t dump_req;
+ /* register description */
+ struct as10x_register_addr reg_addr;
+ /* nb blocks to read */
+ uint16_t num_blocks;
+ } req;
+ /* response */
+ struct {
+ /* response identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ /* dump response */
+ uint8_t dump_rsp;
+ /* data */
+ union {
+ uint8_t data8[DUMP_BLOCK_SIZE];
+ uint16_t data16[DUMP_BLOCK_SIZE / sizeof(uint16_t)];
+ uint32_t data32[DUMP_BLOCK_SIZE / sizeof(uint32_t)];
+ } u;
+ } rsp;
+} DUMP_MEMORY;
+
+typedef union {
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* dump memory type request */
+ uint8_t dump_req;
+ } req;
+ struct {
+ /* request identifier */
+ uint16_t proc_id;
+ /* error */
+ uint8_t error;
+ /* dump response */
+ uint8_t dump_rsp;
+ /* dump data */
+ uint8_t data[DUMP_BLOCK_SIZE];
+ } rsp;
+} DUMPLOG_MEMORY;
+
+typedef union {
+ /* request */
+ struct {
+ uint16_t proc_id;
+ uint8_t data[64 - sizeof(struct as10x_cmd_header_t) -2 /* proc_id */];
+ } req;
+ /* response */
+ struct {
+ uint16_t proc_id;
+ uint8_t error;
+ uint8_t data[64 - sizeof(struct as10x_cmd_header_t) /* header */
+ - 2 /* proc_id */ - 1 /* rc */];
+ } rsp;
+} RAW_DATA;
+
+struct as10x_cmd_t {
+ /* header */
+ struct as10x_cmd_header_t header;
+ /* body */
+ union {
+ TURN_ON turn_on;
+ TURN_OFF turn_off;
+ SET_TUNE set_tune;
+ GET_TUNE_STATUS get_tune_status;
+ GET_TPS get_tps;
+ COMMON common;
+ ADD_PID_FILTER add_pid_filter;
+ DEL_PID_FILTER del_pid_filter;
+ START_STREAMING start_streaming;
+ STOP_STREAMING stop_streaming;
+ GET_DEMOD_STATS get_demod_stats;
+ GET_IMPULSE_RESP get_impulse_rsp;
+ FW_CONTEXT context;
+ SET_REGISTER set_register;
+ GET_REGISTER get_register;
+ CFG_CHANGE_MODE cfg_change_mode;
+ DUMP_MEMORY dump_memory;
+ DUMPLOG_MEMORY dumplog_memory;
+ RAW_DATA raw_data;
+ } body;
+};
+
+struct as10x_token_cmd_t {
+ /* token cmd */
+ struct as10x_cmd_t c;
+ /* token response */
+ struct as10x_cmd_t r;
+};
+#pragma pack()
+
+
+/**************************/
+/* FUNCTION DECLARATION */
+/**************************/
+
+void as10x_cmd_build(struct as10x_cmd_t *pcmd, uint16_t proc_id,
+ uint16_t cmd_len);
+int as10x_rsp_parse(struct as10x_cmd_t *r, uint16_t proc_id);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* as10x cmd */
+int as10x_cmd_turn_on(as10x_handle_t *phandle);
+int as10x_cmd_turn_off(as10x_handle_t *phandle);
+
+int as10x_cmd_set_tune(as10x_handle_t *phandle,
+ struct as10x_tune_args *ptune);
+
+int as10x_cmd_get_tune_status(as10x_handle_t *phandle,
+ struct as10x_tune_status *pstatus);
+
+int as10x_cmd_get_tps(as10x_handle_t *phandle,
+ struct as10x_tps *ptps);
+
+int as10x_cmd_get_demod_stats(as10x_handle_t *phandle,
+ struct as10x_demod_stats *pdemod_stats);
+
+int as10x_cmd_get_impulse_resp(as10x_handle_t *phandle,
+ uint8_t *is_ready);
+
+/* as10x cmd stream */
+int as10x_cmd_add_PID_filter(as10x_handle_t *phandle,
+ struct as10x_ts_filter *filter);
+int as10x_cmd_del_PID_filter(as10x_handle_t *phandle,
+ uint16_t pid_value);
+
+int as10x_cmd_start_streaming(as10x_handle_t *phandle);
+int as10x_cmd_stop_streaming(as10x_handle_t *phandle);
+
+/* as10x cmd cfg */
+int as10x_cmd_set_context(as10x_handle_t *phandle,
+ uint16_t tag,
+ uint32_t value);
+int as10x_cmd_get_context(as10x_handle_t *phandle,
+ uint16_t tag,
+ uint32_t *pvalue);
+
+int as10x_cmd_eLNA_change_mode(as10x_handle_t *phandle, uint8_t mode);
+int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id);
+#ifdef __cplusplus
+}
+#endif
+#endif
+/* EOF - vim: set textwidth=80 ts=3 sw=3 sts=3 et: */
diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c
new file mode 100644
index 000000000000..ec6f69fcf399
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd_cfg.c
@@ -0,0 +1,215 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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/kernel.h>
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+/***************************/
+/* FUNCTION DEFINITION */
+/***************************/
+
+/**
+ * as10x_cmd_get_context - Send get context command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @tag: context tag
+ * @pvalue: pointer where to store context value read
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_context(as10x_handle_t *phandle, uint16_t tag,
+ uint32_t *pvalue)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.context.req));
+
+ /* fill command */
+ pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
+ pcmd->body.context.req.tag = cpu_to_le16(tag);
+ pcmd->body.context.req.type = cpu_to_le16(GET_CONTEXT_DATA);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) pcmd,
+ sizeof(pcmd->body.context.req)
+ + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.context.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response: context command do not follow the common response */
+ /* structure -> specific handling response parse required */
+ error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
+
+ if (error == 0) {
+ /* Response OK -> get response data */
+ *pvalue = le32_to_cpu(prsp->body.context.rsp.reg_val.u.value32);
+ /* value returned is always a 32-bit value */
+ }
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_set_context - send set context command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @tag: context tag
+ * @value: value to set in context
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_set_context(as10x_handle_t *phandle, uint16_t tag,
+ uint32_t value)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.context.req));
+
+ /* fill command */
+ pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
+ /* pcmd->body.context.req.reg_val.mode initialization is not required */
+ pcmd->body.context.req.reg_val.u.value32 = cpu_to_le32(value);
+ pcmd->body.context.req.tag = cpu_to_le16(tag);
+ pcmd->body.context.req.type = cpu_to_le16(SET_CONTEXT_DATA);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle,
+ (uint8_t *) pcmd,
+ sizeof(pcmd->body.context.req)
+ + HEADER_SIZE,
+ (uint8_t *) prsp,
+ sizeof(prsp->body.context.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response: context command do not follow the common response */
+ /* structure -> specific handling response parse required */
+ error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_eLNA_change_mode - send eLNA change mode command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @mode: mode selected:
+ * - ON : 0x0 => eLNA always ON
+ * - OFF : 0x1 => eLNA always OFF
+ * - AUTO : 0x2 => eLNA follow hysteresis parameters
+ * to be ON or OFF
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_eLNA_change_mode(as10x_handle_t *phandle, uint8_t mode)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.cfg_change_mode.req));
+
+ /* fill command */
+ pcmd->body.cfg_change_mode.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_ELNA_CHANGE_MODE);
+ pcmd->body.cfg_change_mode.req.mode = mode;
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.cfg_change_mode.req)
+ + HEADER_SIZE, (uint8_t *) prsp,
+ sizeof(prsp->body.cfg_change_mode.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_ELNA_CHANGE_MODE_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_context_rsp_parse - Parse context command response
+ * @prsp: pointer to AS10x command response buffer
+ * @proc_id: id of the command
+ *
+ * Since the contex command reponse does not follow the common
+ * response, a specific parse function is required.
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
+{
+ int err;
+
+ err = prsp->body.context.rsp.error;
+
+ if ((err == 0) &&
+ (le16_to_cpu(prsp->body.context.rsp.proc_id) == proc_id)) {
+ return 0;
+ }
+ return AS10X_CMD_ERROR;
+}
diff --git a/drivers/staging/media/as102/as10x_cmd_stream.c b/drivers/staging/media/as102/as10x_cmd_stream.c
new file mode 100644
index 000000000000..045c70683193
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd_stream.c
@@ -0,0 +1,223 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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/kernel.h>
+#include "as102_drv.h"
+#include "as10x_cmd.h"
+
+/**
+ * as10x_cmd_add_PID_filter - send add filter command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @filter: TSFilter filter for DVB-T
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_add_PID_filter(as10x_handle_t *phandle,
+ struct as10x_ts_filter *filter)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.add_pid_filter.req));
+
+ /* fill command */
+ pcmd->body.add_pid_filter.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_SETFILTER);
+ pcmd->body.add_pid_filter.req.pid = cpu_to_le16(filter->pid);
+ pcmd->body.add_pid_filter.req.stream_type = filter->type;
+
+ if (filter->idx < 16)
+ pcmd->body.add_pid_filter.req.idx = filter->idx;
+ else
+ pcmd->body.add_pid_filter.req.idx = 0xFF;
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.add_pid_filter.req)
+ + HEADER_SIZE, (uint8_t *) prsp,
+ sizeof(prsp->body.add_pid_filter.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_SETFILTER_RSP);
+
+ if (error == 0) {
+ /* Response OK -> get response data */
+ filter->idx = prsp->body.add_pid_filter.rsp.filter_id;
+ }
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_del_PID_filter - Send delete filter command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @pid_value: PID to delete
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_del_PID_filter(as10x_handle_t *phandle,
+ uint16_t pid_value)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.del_pid_filter.req));
+
+ /* fill command */
+ pcmd->body.del_pid_filter.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_REMOVEFILTER);
+ pcmd->body.del_pid_filter.req.pid = cpu_to_le16(pid_value);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.del_pid_filter.req)
+ + HEADER_SIZE, (uint8_t *) prsp,
+ sizeof(prsp->body.del_pid_filter.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_REMOVEFILTER_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_start_streaming - Send start streaming command to AS10x
+ * @phandle: pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_start_streaming(as10x_handle_t *phandle)
+{
+ int error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.start_streaming.req));
+
+ /* fill command */
+ pcmd->body.start_streaming.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_START_STREAMING);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.start_streaming.req)
+ + HEADER_SIZE, (uint8_t *) prsp,
+ sizeof(prsp->body.start_streaming.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_START_STREAMING_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
+
+/**
+ * as10x_cmd_stop_streaming - Send stop streaming command to AS10x
+ * @phandle: pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_stop_streaming(as10x_handle_t *phandle)
+{
+ int8_t error;
+ struct as10x_cmd_t *pcmd, *prsp;
+
+ ENTER();
+
+ pcmd = phandle->cmd;
+ prsp = phandle->rsp;
+
+ /* prepare command */
+ as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+ sizeof(pcmd->body.stop_streaming.req));
+
+ /* fill command */
+ pcmd->body.stop_streaming.req.proc_id =
+ cpu_to_le16(CONTROL_PROC_STOP_STREAMING);
+
+ /* send command */
+ if (phandle->ops->xfer_cmd) {
+ error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+ sizeof(pcmd->body.stop_streaming.req)
+ + HEADER_SIZE, (uint8_t *) prsp,
+ sizeof(prsp->body.stop_streaming.rsp)
+ + HEADER_SIZE);
+ } else {
+ error = AS10X_CMD_ERROR;
+ }
+
+ if (error < 0)
+ goto out;
+
+ /* parse response */
+ error = as10x_rsp_parse(prsp, CONTROL_PROC_STOP_STREAMING_RSP);
+
+out:
+ LEAVE();
+ return error;
+}
diff --git a/drivers/staging/media/as102/as10x_handle.h b/drivers/staging/media/as102/as10x_handle.h
new file mode 100644
index 000000000000..4f01a76e9829
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_handle.h
@@ -0,0 +1,58 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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.
+ */
+#ifdef __KERNEL__
+struct as102_bus_adapter_t;
+struct as102_dev_t;
+
+#define as10x_handle_t struct as102_bus_adapter_t
+#include "as10x_cmd.h"
+
+/* values for "mode" field */
+#define REGMODE8 8
+#define REGMODE16 16
+#define REGMODE32 32
+
+struct as102_priv_ops_t {
+ int (*upload_fw_pkt) (struct as102_bus_adapter_t *bus_adap,
+ unsigned char *buf, int buflen, int swap32);
+
+ int (*send_cmd) (struct as102_bus_adapter_t *bus_adap,
+ unsigned char *buf, int buflen);
+
+ int (*xfer_cmd) (struct as102_bus_adapter_t *bus_adap,
+ unsigned char *send_buf, int send_buf_len,
+ unsigned char *recv_buf, int recv_buf_len);
+/*
+ int (*pid_filter) (struct as102_bus_adapter_t *bus_adap,
+ int index, u16 pid, int onoff);
+*/
+ int (*start_stream) (struct as102_dev_t *dev);
+ void (*stop_stream) (struct as102_dev_t *dev);
+
+ int (*reset_target) (struct as102_bus_adapter_t *bus_adap);
+
+ int (*read_write)(struct as102_bus_adapter_t *bus_adap, uint8_t mode,
+ uint32_t rd_addr, uint16_t rd_len,
+ uint32_t wr_addr, uint16_t wr_len);
+
+ int (*as102_read_ep2) (struct as102_bus_adapter_t *bus_adap,
+ unsigned char *recv_buf,
+ int recv_buf_len);
+};
+#endif
diff --git a/drivers/staging/media/as102/as10x_types.h b/drivers/staging/media/as102/as10x_types.h
new file mode 100644
index 000000000000..3dedb3c1420a
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_types.h
@@ -0,0 +1,198 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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 _AS10X_TYPES_H_
+#define _AS10X_TYPES_H_
+
+#include "as10x_handle.h"
+
+/*********************************/
+/* MACRO DEFINITIONS */
+/*********************************/
+
+/* bandwidth constant values */
+#define BW_5_MHZ 0x00
+#define BW_6_MHZ 0x01
+#define BW_7_MHZ 0x02
+#define BW_8_MHZ 0x03
+
+/* hierarchy priority selection values */
+#define HIER_NO_PRIORITY 0x00
+#define HIER_LOW_PRIORITY 0x01
+#define HIER_HIGH_PRIORITY 0x02
+
+/* constellation available values */
+#define CONST_QPSK 0x00
+#define CONST_QAM16 0x01
+#define CONST_QAM64 0x02
+#define CONST_UNKNOWN 0xFF
+
+/* hierarchy available values */
+#define HIER_NONE 0x00
+#define HIER_ALPHA_1 0x01
+#define HIER_ALPHA_2 0x02
+#define HIER_ALPHA_4 0x03
+#define HIER_UNKNOWN 0xFF
+
+/* interleaving available values */
+#define INTLV_NATIVE 0x00
+#define INTLV_IN_DEPTH 0x01
+#define INTLV_UNKNOWN 0xFF
+
+/* code rate available values */
+#define CODE_RATE_1_2 0x00
+#define CODE_RATE_2_3 0x01
+#define CODE_RATE_3_4 0x02
+#define CODE_RATE_5_6 0x03
+#define CODE_RATE_7_8 0x04
+#define CODE_RATE_UNKNOWN 0xFF
+
+/* guard interval available values */
+#define GUARD_INT_1_32 0x00
+#define GUARD_INT_1_16 0x01
+#define GUARD_INT_1_8 0x02
+#define GUARD_INT_1_4 0x03
+#define GUARD_UNKNOWN 0xFF
+
+/* transmission mode available values */
+#define TRANS_MODE_2K 0x00
+#define TRANS_MODE_8K 0x01
+#define TRANS_MODE_4K 0x02
+#define TRANS_MODE_UNKNOWN 0xFF
+
+/* DVBH signalling available values */
+#define TIMESLICING_PRESENT 0x01
+#define MPE_FEC_PRESENT 0x02
+
+/* tune state available */
+#define TUNE_STATUS_NOT_TUNED 0x00
+#define TUNE_STATUS_IDLE 0x01
+#define TUNE_STATUS_LOCKING 0x02
+#define TUNE_STATUS_SIGNAL_DVB_OK 0x03
+#define TUNE_STATUS_STREAM_DETECTED 0x04
+#define TUNE_STATUS_STREAM_TUNED 0x05
+#define TUNE_STATUS_ERROR 0xFF
+
+/* available TS FID filter types */
+#define TS_PID_TYPE_TS 0
+#define TS_PID_TYPE_PSI_SI 1
+#define TS_PID_TYPE_MPE 2
+
+/* number of echos available */
+#define MAX_ECHOS 15
+
+/* Context types */
+#define CONTEXT_LNA 1010
+#define CONTEXT_ELNA_HYSTERESIS 4003
+#define CONTEXT_ELNA_GAIN 4004
+#define CONTEXT_MER_THRESHOLD 5005
+#define CONTEXT_MER_OFFSET 5006
+#define CONTEXT_IR_STATE 7000
+#define CONTEXT_TSOUT_MSB_FIRST 7004
+#define CONTEXT_TSOUT_FALLING_EDGE 7005
+
+/* Configuration modes */
+#define CFG_MODE_ON 0
+#define CFG_MODE_OFF 1
+#define CFG_MODE_AUTO 2
+
+#pragma pack(1)
+struct as10x_tps {
+ uint8_t constellation;
+ uint8_t hierarchy;
+ uint8_t interleaving_mode;
+ uint8_t code_rate_HP;
+ uint8_t code_rate_LP;
+ uint8_t guard_interval;
+ uint8_t transmission_mode;
+ uint8_t DVBH_mask_HP;
+ uint8_t DVBH_mask_LP;
+ uint16_t cell_ID;
+};
+
+struct as10x_tune_args {
+ /* frequency */
+ uint32_t freq;
+ /* bandwidth */
+ uint8_t bandwidth;
+ /* hierarchy selection */
+ uint8_t hier_select;
+ /* constellation */
+ uint8_t constellation;
+ /* hierarchy */
+ uint8_t hierarchy;
+ /* interleaving mode */
+ uint8_t interleaving_mode;
+ /* code rate */
+ uint8_t code_rate;
+ /* guard interval */
+ uint8_t guard_interval;
+ /* transmission mode */
+ uint8_t transmission_mode;
+};
+
+struct as10x_tune_status {
+ /* tune status */
+ uint8_t tune_state;
+ /* signal strength */
+ int16_t signal_strength;
+ /* packet error rate 10^-4 */
+ uint16_t PER;
+ /* bit error rate 10^-4 */
+ uint16_t BER;
+};
+
+struct as10x_demod_stats {
+ /* frame counter */
+ uint32_t frame_count;
+ /* Bad frame counter */
+ uint32_t bad_frame_count;
+ /* Number of wrong bytes fixed by Reed-Solomon */
+ uint32_t bytes_fixed_by_rs;
+ /* Averaged MER */
+ uint16_t mer;
+ /* statistics calculation state indicator (started or not) */
+ uint8_t has_started;
+};
+
+struct as10x_ts_filter {
+ uint16_t pid; /** valid PID value 0x00 : 0x2000 */
+ uint8_t type; /** Red TS_PID_TYPE_<N> values */
+ uint8_t idx; /** index in filtering table */
+};
+
+struct as10x_register_value {
+ uint8_t mode;
+ union {
+ uint8_t value8; /* 8 bit value */
+ uint16_t value16; /* 16 bit value */
+ uint32_t value32; /* 32 bit value */
+ }u;
+};
+
+#pragma pack()
+
+struct as10x_register_addr {
+ /* register addr */
+ uint32_t addr;
+ /* register mode access */
+ uint8_t mode;
+};
+
+
+#endif
diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/media/cxd2099/Kconfig
index b48aefddc84c..b48aefddc84c 100644
--- a/drivers/staging/cxd2099/Kconfig
+++ b/drivers/staging/media/cxd2099/Kconfig
diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile
index 64cfc77be357..64cfc77be357 100644
--- a/drivers/staging/cxd2099/Makefile
+++ b/drivers/staging/media/cxd2099/Makefile
diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/media/cxd2099/TODO
index 375bb6f8ee2c..375bb6f8ee2c 100644
--- a/drivers/staging/cxd2099/TODO
+++ b/drivers/staging/media/cxd2099/TODO
diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
index 1c04185bcfd7..1c04185bcfd7 100644
--- a/drivers/staging/cxd2099/cxd2099.c
+++ b/drivers/staging/media/cxd2099/cxd2099.c
diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h
index 19c588a59588..19c588a59588 100644
--- a/drivers/staging/cxd2099/cxd2099.h
+++ b/drivers/staging/media/cxd2099/cxd2099.h
diff --git a/drivers/staging/dt3155v4l/Kconfig b/drivers/staging/media/dt3155v4l/Kconfig
index 226a1ca90b3c..226a1ca90b3c 100644
--- a/drivers/staging/dt3155v4l/Kconfig
+++ b/drivers/staging/media/dt3155v4l/Kconfig
diff --git a/drivers/staging/dt3155v4l/Makefile b/drivers/staging/media/dt3155v4l/Makefile
index ce7a3ec2faf3..ce7a3ec2faf3 100644
--- a/drivers/staging/dt3155v4l/Makefile
+++ b/drivers/staging/media/dt3155v4l/Makefile
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index 5b212dc725e1..04e93c49f03a 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -193,7 +193,7 @@ dt3155_start_acq(struct dt3155_priv *pd)
struct vb2_buffer *vb = pd->curr_buf;
dma_addr_t dma_addr;
- dma_addr = vb2_dma_contig_plane_paddr(vb, 0);
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
iowrite32(dma_addr, pd->regs + EVEN_DMA_START);
iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START);
iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE);
@@ -357,7 +357,7 @@ dt3155_irq_handler_even(int irq, void *dev_id)
ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
list_del(&ivb->done_entry);
ipd->curr_buf = ivb;
- dma_addr = vb2_dma_contig_plane_paddr(ivb, 0);
+ dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);
iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);
iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START);
iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE);
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.h b/drivers/staging/media/dt3155v4l/dt3155v4l.h
index 2e4f89d402e4..2e4f89d402e4 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.h
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.h
diff --git a/drivers/staging/easycap/Kconfig b/drivers/staging/media/easycap/Kconfig
index a425a6f9cdca..a425a6f9cdca 100644
--- a/drivers/staging/easycap/Kconfig
+++ b/drivers/staging/media/easycap/Kconfig
diff --git a/drivers/staging/easycap/Makefile b/drivers/staging/media/easycap/Makefile
index a34e75f59c18..a34e75f59c18 100644
--- a/drivers/staging/easycap/Makefile
+++ b/drivers/staging/media/easycap/Makefile
diff --git a/drivers/staging/easycap/README b/drivers/staging/media/easycap/README
index 796b032384bd..796b032384bd 100644
--- a/drivers/staging/easycap/README
+++ b/drivers/staging/media/easycap/README
diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/media/easycap/easycap.h
index 7b256a948c27..7b256a948c27 100644
--- a/drivers/staging/easycap/easycap.h
+++ b/drivers/staging/media/easycap/easycap.h
diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/media/easycap/easycap_ioctl.c
index c99addfb6242..c99addfb6242 100644
--- a/drivers/staging/easycap/easycap_ioctl.c
+++ b/drivers/staging/media/easycap/easycap_ioctl.c
diff --git a/drivers/staging/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c
index 0385735ac6df..0385735ac6df 100644
--- a/drivers/staging/easycap/easycap_low.c
+++ b/drivers/staging/media/easycap/easycap_low.c
diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
index a45c0b507067..a45c0b507067 100644
--- a/drivers/staging/easycap/easycap_main.c
+++ b/drivers/staging/media/easycap/easycap_main.c
diff --git a/drivers/staging/easycap/easycap_settings.c b/drivers/staging/media/easycap/easycap_settings.c
index 70f59b13c34d..70f59b13c34d 100644
--- a/drivers/staging/easycap/easycap_settings.c
+++ b/drivers/staging/media/easycap/easycap_settings.c
diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/media/easycap/easycap_sound.c
index b22bb39b5f69..b22bb39b5f69 100644
--- a/drivers/staging/easycap/easycap_sound.c
+++ b/drivers/staging/media/easycap/easycap_sound.c
diff --git a/drivers/staging/easycap/easycap_testcard.c b/drivers/staging/media/easycap/easycap_testcard.c
index 0f71470ace39..0f71470ace39 100644
--- a/drivers/staging/easycap/easycap_testcard.c
+++ b/drivers/staging/media/easycap/easycap_testcard.c
diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig
index 7dfb2815b9ec..7dfb2815b9ec 100644
--- a/drivers/staging/go7007/Kconfig
+++ b/drivers/staging/media/go7007/Kconfig
diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/media/go7007/Makefile
index 6ee837c56706..6ee837c56706 100644
--- a/drivers/staging/go7007/Makefile
+++ b/drivers/staging/media/go7007/Makefile
diff --git a/drivers/staging/go7007/README b/drivers/staging/media/go7007/README
index 48f447637817..48f447637817 100644
--- a/drivers/staging/go7007/README
+++ b/drivers/staging/media/go7007/README
diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c
index 6c9279a6d606..6c9279a6d606 100644
--- a/drivers/staging/go7007/go7007-driver.c
+++ b/drivers/staging/media/go7007/go7007-driver.c
diff --git a/drivers/staging/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c
index c9a6409edfe3..c9a6409edfe3 100644
--- a/drivers/staging/go7007/go7007-fw.c
+++ b/drivers/staging/media/go7007/go7007-fw.c
diff --git a/drivers/staging/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c
index b8cfa1a6eaeb..b8cfa1a6eaeb 100644
--- a/drivers/staging/go7007/go7007-i2c.c
+++ b/drivers/staging/media/go7007/go7007-i2c.c
diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h
index b58c394c6555..b58c394c6555 100644
--- a/drivers/staging/go7007/go7007-priv.h
+++ b/drivers/staging/media/go7007/go7007-priv.h
diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c
index 3db3b0a91cc1..3db3b0a91cc1 100644
--- a/drivers/staging/go7007/go7007-usb.c
+++ b/drivers/staging/media/go7007/go7007-usb.c
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index 2b27d8da70a2..2b27d8da70a2 100644
--- a/drivers/staging/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
diff --git a/drivers/staging/go7007/go7007.h b/drivers/staging/media/go7007/go7007.h
index 7399c915a934..7399c915a934 100644
--- a/drivers/staging/go7007/go7007.h
+++ b/drivers/staging/media/go7007/go7007.h
diff --git a/drivers/staging/go7007/go7007.txt b/drivers/staging/media/go7007/go7007.txt
index 9db1f3952fd2..9db1f3952fd2 100644
--- a/drivers/staging/go7007/go7007.txt
+++ b/drivers/staging/media/go7007/go7007.txt
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c
index e7736a915530..e7736a915530 100644
--- a/drivers/staging/go7007/s2250-board.c
+++ b/drivers/staging/media/go7007/s2250-board.c
diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c
index 4e132519e253..4e132519e253 100644
--- a/drivers/staging/go7007/s2250-loader.c
+++ b/drivers/staging/media/go7007/s2250-loader.c
diff --git a/drivers/staging/go7007/s2250-loader.h b/drivers/staging/media/go7007/s2250-loader.h
index b7c301af16cc..b7c301af16cc 100644
--- a/drivers/staging/go7007/s2250-loader.h
+++ b/drivers/staging/media/go7007/s2250-loader.h
diff --git a/drivers/staging/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c
index cf7c34a99459..cf7c34a99459 100644
--- a/drivers/staging/go7007/saa7134-go7007.c
+++ b/drivers/staging/media/go7007/saa7134-go7007.c
diff --git a/drivers/staging/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c
index deac938d8505..deac938d8505 100644
--- a/drivers/staging/go7007/snd-go7007.c
+++ b/drivers/staging/media/go7007/snd-go7007.c
diff --git a/drivers/staging/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h
index 3c2b9be455df..3c2b9be455df 100644
--- a/drivers/staging/go7007/wis-i2c.h
+++ b/drivers/staging/media/go7007/wis-i2c.h
diff --git a/drivers/staging/go7007/wis-ov7640.c b/drivers/staging/media/go7007/wis-ov7640.c
index 6bc9470fecb6..6bc9470fecb6 100644
--- a/drivers/staging/go7007/wis-ov7640.c
+++ b/drivers/staging/media/go7007/wis-ov7640.c
diff --git a/drivers/staging/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c
index 05e0e1083864..05e0e1083864 100644
--- a/drivers/staging/go7007/wis-saa7113.c
+++ b/drivers/staging/media/go7007/wis-saa7113.c
diff --git a/drivers/staging/go7007/wis-saa7115.c b/drivers/staging/media/go7007/wis-saa7115.c
index 46cff59e28b7..46cff59e28b7 100644
--- a/drivers/staging/go7007/wis-saa7115.c
+++ b/drivers/staging/media/go7007/wis-saa7115.c
diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c
index 8f1b7d4f6a2e..8f1b7d4f6a2e 100644
--- a/drivers/staging/go7007/wis-sony-tuner.c
+++ b/drivers/staging/media/go7007/wis-sony-tuner.c
diff --git a/drivers/staging/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c
index 9134f03e3cf0..9134f03e3cf0 100644
--- a/drivers/staging/go7007/wis-tw2804.c
+++ b/drivers/staging/media/go7007/wis-tw2804.c
diff --git a/drivers/staging/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c
index 9230f4a80529..9230f4a80529 100644
--- a/drivers/staging/go7007/wis-tw9903.c
+++ b/drivers/staging/media/go7007/wis-tw9903.c
diff --git a/drivers/staging/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c
index 0127be2f3be0..0127be2f3be0 100644
--- a/drivers/staging/go7007/wis-uda1342.c
+++ b/drivers/staging/media/go7007/wis-uda1342.c
diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
index 526ec0fc2f04..526ec0fc2f04 100644
--- a/drivers/staging/lirc/Kconfig
+++ b/drivers/staging/media/lirc/Kconfig
diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/media/lirc/Makefile
index d76b0fa2af53..d76b0fa2af53 100644
--- a/drivers/staging/lirc/Makefile
+++ b/drivers/staging/media/lirc/Makefile
diff --git a/drivers/staging/lirc/TODO b/drivers/staging/media/lirc/TODO
index b6cb593f55c6..b6cb593f55c6 100644
--- a/drivers/staging/lirc/TODO
+++ b/drivers/staging/media/lirc/TODO
diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog
index a97800a8e127..a97800a8e127 100644
--- a/drivers/staging/lirc/TODO.lirc_zilog
+++ b/drivers/staging/media/lirc/TODO.lirc_zilog
diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c
index c5a0d27a02dc..c5a0d27a02dc 100644
--- a/drivers/staging/lirc/lirc_bt829.c
+++ b/drivers/staging/media/lirc/lirc_bt829.c
diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/media/lirc/lirc_ene0100.h
index 06bebd6acc46..06bebd6acc46 100644
--- a/drivers/staging/lirc/lirc_ene0100.h
+++ b/drivers/staging/media/lirc/lirc_ene0100.h
diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c
index 0dc2c2b22c2b..0dc2c2b22c2b 100644
--- a/drivers/staging/lirc/lirc_igorplugusb.c
+++ b/drivers/staging/media/lirc/lirc_igorplugusb.c
diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
index f5308d5929c6..f5308d5929c6 100644
--- a/drivers/staging/lirc/lirc_imon.c
+++ b/drivers/staging/media/lirc/lirc_imon.c
diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c
index 792aac0a8e7b..792aac0a8e7b 100644
--- a/drivers/staging/lirc/lirc_parallel.c
+++ b/drivers/staging/media/lirc/lirc_parallel.c
diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/media/lirc/lirc_parallel.h
index 4bed6afe0632..4bed6afe0632 100644
--- a/drivers/staging/lirc/lirc_parallel.h
+++ b/drivers/staging/media/lirc/lirc_parallel.h
diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c
index a2d18b0aa048..a2d18b0aa048 100644
--- a/drivers/staging/lirc/lirc_sasem.c
+++ b/drivers/staging/media/lirc/lirc_sasem.c
diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
index 8a060a8a7224..8a060a8a7224 100644
--- a/drivers/staging/lirc/lirc_serial.c
+++ b/drivers/staging/media/lirc/lirc_serial.c
diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index 6903d3992eca..6903d3992eca 100644
--- a/drivers/staging/lirc/lirc_sir.c
+++ b/drivers/staging/media/lirc/lirc_sir.c
diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/media/lirc/lirc_ttusbir.c
index e4b329b8cafd..e4b329b8cafd 100644
--- a/drivers/staging/lirc/lirc_ttusbir.c
+++ b/drivers/staging/media/lirc/lirc_ttusbir.c
diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
index 0302d82a12f7..0302d82a12f7 100644
--- a/drivers/staging/lirc/lirc_zilog.c
+++ b/drivers/staging/media/lirc/lirc_zilog.c
diff --git a/drivers/staging/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig
index 03dcac4ea4d0..03dcac4ea4d0 100644
--- a/drivers/staging/solo6x10/Kconfig
+++ b/drivers/staging/media/solo6x10/Kconfig
diff --git a/drivers/staging/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile
index 72816cf16704..72816cf16704 100644
--- a/drivers/staging/solo6x10/Makefile
+++ b/drivers/staging/media/solo6x10/Makefile
diff --git a/drivers/staging/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO
index 7e6c4fa130df..7e6c4fa130df 100644
--- a/drivers/staging/solo6x10/TODO
+++ b/drivers/staging/media/solo6x10/TODO
diff --git a/drivers/staging/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index f974f6412ad7..f974f6412ad7 100644
--- a/drivers/staging/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
diff --git a/drivers/staging/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index 884c0eb757c4..884c0eb757c4 100644
--- a/drivers/staging/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
diff --git a/drivers/staging/solo6x10/enc.c b/drivers/staging/media/solo6x10/enc.c
index de502599bb19..de502599bb19 100644
--- a/drivers/staging/solo6x10/enc.c
+++ b/drivers/staging/media/solo6x10/enc.c
diff --git a/drivers/staging/solo6x10/g723.c b/drivers/staging/media/solo6x10/g723.c
index 59274bfca95b..59274bfca95b 100644
--- a/drivers/staging/solo6x10/g723.c
+++ b/drivers/staging/media/solo6x10/g723.c
diff --git a/drivers/staging/solo6x10/gpio.c b/drivers/staging/media/solo6x10/gpio.c
index 0925e6f33a99..0925e6f33a99 100644
--- a/drivers/staging/solo6x10/gpio.c
+++ b/drivers/staging/media/solo6x10/gpio.c
diff --git a/drivers/staging/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c
index ef95a500b4da..ef95a500b4da 100644
--- a/drivers/staging/solo6x10/i2c.c
+++ b/drivers/staging/media/solo6x10/i2c.c
diff --git a/drivers/staging/solo6x10/jpeg.h b/drivers/staging/media/solo6x10/jpeg.h
index 50defec318cc..50defec318cc 100644
--- a/drivers/staging/solo6x10/jpeg.h
+++ b/drivers/staging/media/solo6x10/jpeg.h
diff --git a/drivers/staging/solo6x10/offsets.h b/drivers/staging/media/solo6x10/offsets.h
index 3d7e569f1cf8..3d7e569f1cf8 100644
--- a/drivers/staging/solo6x10/offsets.h
+++ b/drivers/staging/media/solo6x10/offsets.h
diff --git a/drivers/staging/solo6x10/osd-font.h b/drivers/staging/media/solo6x10/osd-font.h
index 591e0e82e0e8..591e0e82e0e8 100644
--- a/drivers/staging/solo6x10/osd-font.h
+++ b/drivers/staging/media/solo6x10/osd-font.h
diff --git a/drivers/staging/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c
index 56210f0fc5ec..56210f0fc5ec 100644
--- a/drivers/staging/solo6x10/p2m.c
+++ b/drivers/staging/media/solo6x10/p2m.c
diff --git a/drivers/staging/solo6x10/registers.h b/drivers/staging/media/solo6x10/registers.h
index aca544472c93..aca544472c93 100644
--- a/drivers/staging/solo6x10/registers.h
+++ b/drivers/staging/media/solo6x10/registers.h
diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index abee7213202f..abee7213202f 100644
--- a/drivers/staging/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
diff --git a/drivers/staging/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c
index db56b42c56c6..db56b42c56c6 100644
--- a/drivers/staging/solo6x10/tw28.c
+++ b/drivers/staging/media/solo6x10/tw28.c
diff --git a/drivers/staging/solo6x10/tw28.h b/drivers/staging/media/solo6x10/tw28.h
index a44a03afbd30..a44a03afbd30 100644
--- a/drivers/staging/solo6x10/tw28.h
+++ b/drivers/staging/media/solo6x10/tw28.h
diff --git a/drivers/staging/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index bee7280bbed9..bee7280bbed9 100644
--- a/drivers/staging/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
diff --git a/drivers/staging/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 571c3a348d30..571c3a348d30 100644
--- a/drivers/staging/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c
index f3c6060c96b8..7a1955583b7d 100644
--- a/drivers/staging/pohmelfs/inode.c
+++ b/drivers/staging/pohmelfs/inode.c
@@ -1197,7 +1197,7 @@ const struct inode_operations pohmelfs_file_inode_operations = {
void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info)
{
inode->i_mode = info->mode;
- inode->i_nlink = info->nlink;
+ set_nlink(inode, info->nlink);
inode->i_uid = info->uid;
inode->i_gid = info->gid;
inode->i_blocks = info->blocks;
diff --git a/drivers/staging/tm6000/CARDLIST b/drivers/staging/tm6000/CARDLIST
deleted file mode 100644
index b5edce487997..000000000000
--- a/drivers/staging/tm6000/CARDLIST
+++ /dev/null
@@ -1,16 +0,0 @@
- 1 -> Generic tm5600 board (tm5600) [6000:0001]
- 2 -> Generic tm6000 board (tm6000) [6000:0001]
- 3 -> Generic tm6010 board (tm6010) [6000:0002]
- 4 -> 10Moons UT821 (tm5600) [6000:0001]
- 5 -> 10Moons UT330 (tm5600)
- 6 -> ADSTech Dual TV (tm6000) [06e1:f332]
- 7 -> FreeCom and similar (tm6000) [14aa:0620]
- 8 -> ADSTech Mini Dual TV (tm6000) [06e1:b339]
- 9 -> Hauppauge WinTV HVR-900H/USB2 Stick (tm6010) [2040:6600,2040:6601,2040:6610,2040:6611]
- 10 -> Beholder Wander (tm6010) [6000:dec0]
- 11 -> Beholder Voyager (tm6010) [6000:dec1]
- 12 -> TerraTec Cinergy Hybrid XE/Cinergy Hybrid Stick (tm6010) [0ccd:0086,0ccd:00a5]
- 13 -> TwinHan TU501 (tm6010) [13d3:3240,13d3:3241,13d3:3243,13d3:3264]
- 14 -> Beholder Wander Lite (tm6010) [6000:dec2]
- 15 -> Beholder Voyager Lite (tm6010) [6000:dec3]
-
diff --git a/drivers/staging/tm6000/README b/drivers/staging/tm6000/README
deleted file mode 100644
index c340ebc2ee9f..000000000000
--- a/drivers/staging/tm6000/README
+++ /dev/null
@@ -1,22 +0,0 @@
-Todo:
- - Fix the loss of some blocks when receiving the video URB's
- - Add a lock at tm6000_read_write_usb() to prevent two simultaneous access to the
- URB control transfers
- - Properly add the locks at tm6000-video
- - Add audio support
- - Add vbi support
- - Add IR support
- - Do several cleanups
- - I think that frame1/frame0 are inverted. This causes a funny effect at the image.
- the fix is trivial, but require some tests
- - My tm6010 devices sometimes insist on stop working. I need to turn them off, removing
- from my machine and wait for a while for it to work again. I'm starting to think that
- it is an overheat issue - is there a workaround that we could do?
- - Sometimes, tm6010 doesn't read eeprom at the proper time (hardware bug). So, the device
- got miss-detected as a "generic" tm6000. This can be really bad if the tuner is the
- Low Power one, as it may result on loading the high power firmware, that could damage
- the device. Maybe we may read eeprom to double check, when the device is marked as "generic"
- - Coding Style fixes
- - sparse cleanups
-
-Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/tm6000/TODO b/drivers/staging/tm6000/TODO
deleted file mode 100644
index 135d0ea3ad70..000000000000
--- a/drivers/staging/tm6000/TODO
+++ /dev/null
@@ -1,8 +0,0 @@
-There a few things to do before putting this driver in production:
- - IR NEC with tm5600/6000 TV cards
- - IR RC5 with tm5600/6000/6010 TV cards
- - CodingStyle;
- - Fix audio;
- - Fix some panic/OOPS conditions.
-
-Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c
deleted file mode 100644
index 8b29d732ddcb..000000000000
--- a/drivers/staging/tm6000/tm6000-stds.c
+++ /dev/null
@@ -1,679 +0,0 @@
-/*
- * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
- *
- * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@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 version 2
- *
- * 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/kernel.h>
-#include "tm6000.h"
-#include "tm6000-regs.h"
-
-static unsigned int tm6010_a_mode = 0;
-module_param(tm6010_a_mode, int, 0644);
-MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode");
-
-struct tm6000_reg_settings {
- unsigned char req;
- unsigned char reg;
- unsigned char value;
-};
-
-
-struct tm6000_std_settings {
- v4l2_std_id id;
- struct tm6000_reg_settings common[27];
-};
-
-static struct tm6000_std_settings composite_stds[] = {
- {
- .id = V4L2_STD_PAL_M,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_PAL_Nc,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_PAL,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_SECAM,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
-
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_NTSC,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- },
-};
-
-static struct tm6000_std_settings svideo_stds[] = {
- {
- .id = V4L2_STD_PAL_M,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_PAL_Nc,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_PAL,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_SECAM,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
-
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- }, {
- .id = V4L2_STD_NTSC,
- .common = {
- {TM6010_REQ07_R3F_RESET, 0x01},
- {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01},
- {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f},
- {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
- {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03},
- {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30},
- {TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b},
- {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e},
- {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b},
- {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2},
- {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9},
- {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
- {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
- {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
- {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
- {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88},
- {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22},
- {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61},
- {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c},
- {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c},
- {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
- {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F},
-
- {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd},
- {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
- {TM6010_REQ07_R3F_RESET, 0x00},
- {0, 0, 0},
- },
- },
-};
-
-
-static int tm6000_set_audio_std(struct tm6000_core *dev)
-{
- uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
- uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
- uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
- uint8_t nicam_flag = 0; /* No NICAM */
-
- if (dev->radio) {
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
- tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
- tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
- /* set mono or stereo */
- if (dev->amode == V4L2_TUNER_MODE_MONO)
- tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
- else if (dev->amode == V4L2_TUNER_MODE_STEREO)
- tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02);
- tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
- tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
- tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
- tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
- tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
- return 0;
- }
-
- switch (tm6010_a_mode) {
- /* auto */
- case 0:
- switch (dev->norm) {
- case V4L2_STD_NTSC_M_KR:
- areg_05 |= 0x00;
- break;
- case V4L2_STD_NTSC_M_JP:
- areg_05 |= 0x40;
- break;
- case V4L2_STD_NTSC_M:
- case V4L2_STD_PAL_M:
- case V4L2_STD_PAL_N:
- areg_05 |= 0x20;
- break;
- case V4L2_STD_PAL_Nc:
- areg_05 |= 0x60;
- break;
- case V4L2_STD_SECAM_L:
- areg_05 |= 0x00;
- break;
- case V4L2_STD_DK:
- areg_05 |= 0x10;
- break;
- }
- break;
- /* A2 */
- case 1:
- switch (dev->norm) {
- case V4L2_STD_B:
- case V4L2_STD_GH:
- areg_05 = 0x05;
- break;
- case V4L2_STD_DK:
- areg_05 = 0x09;
- break;
- }
- break;
- /* NICAM */
- case 2:
- switch (dev->norm) {
- case V4L2_STD_B:
- case V4L2_STD_GH:
- areg_05 = 0x07;
- break;
- case V4L2_STD_DK:
- areg_05 = 0x06;
- break;
- case V4L2_STD_PAL_I:
- areg_05 = 0x08;
- break;
- case V4L2_STD_SECAM_L:
- areg_05 = 0x0a;
- areg_02 = 0x02;
- break;
- }
- nicam_flag = 1;
- break;
- /* other */
- case 3:
- switch (dev->norm) {
- /* DK3_A2 */
- case V4L2_STD_DK:
- areg_05 = 0x0b;
- break;
- /* Korea */
- case V4L2_STD_NTSC_M_KR:
- areg_05 = 0x04;
- break;
- /* EIAJ */
- case V4L2_STD_NTSC_M_JP:
- areg_05 = 0x03;
- break;
- default:
- areg_05 = 0x02;
- break;
- }
- break;
- }
-
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02);
- tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
- tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05);
- tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06);
- tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
- tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
- tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
- tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
- tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
- tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
- tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
- tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
- tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
- tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
- tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
- tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
- tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
- tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
- tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
- tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
- tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
- tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
- tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
- tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
- tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
- tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
-
- return 0;
-}
-
-void tm6000_get_std_res(struct tm6000_core *dev)
-{
- /* Currently, those are the only supported resoltions */
- if (dev->norm & V4L2_STD_525_60)
- dev->height = 480;
- else
- dev->height = 576;
-
- dev->width = 720;
-}
-
-static int tm6000_load_std(struct tm6000_core *dev,
- struct tm6000_reg_settings *set, int max_size)
-{
- int i, rc;
-
- /* Load board's initialization table */
- for (i = 0; max_size; i++) {
- if (!set[i].req)
- return 0;
-
- rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value);
- if (rc < 0) {
- printk(KERN_ERR "Error %i while setting "
- "req %d, reg %d to value %d\n",
- rc, set[i].req, set[i].reg, set[i].value);
- return rc;
- }
- }
-
- return 0;
-}
-
-int tm6000_set_standard(struct tm6000_core *dev)
-{
- int i, rc = 0;
- u8 reg_07_fe = 0x8a;
- u8 reg_08_f1 = 0xfc;
- u8 reg_08_e2 = 0xf0;
- u8 reg_08_e6 = 0x0f;
-
- tm6000_get_std_res(dev);
-
- if (dev->radio) {
- /* todo */
- }
-
- if (dev->dev_type == TM6010) {
- switch (dev->vinput[dev->input].vmux) {
- case TM6000_VMUX_VIDEO_A:
- tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4);
- tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
- tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
- tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
- tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
- reg_07_fe |= 0x01;
- break;
- case TM6000_VMUX_VIDEO_B:
- tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8);
- tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
- tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
- tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
- tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
- reg_07_fe |= 0x01;
- break;
- case TM6000_VMUX_VIDEO_AB:
- tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc);
- tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8);
- reg_08_e6 = 0x00;
- tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2);
- tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0);
- tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
- tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0);
- break;
- default:
- break;
- }
- switch (dev->vinput[dev->input].amux) {
- case TM6000_AMUX_ADC1:
- tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
- 0x00, 0x0f);
- break;
- case TM6000_AMUX_ADC2:
- tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
- 0x08, 0x0f);
- break;
- case TM6000_AMUX_SIF1:
- reg_08_e2 |= 0x02;
- reg_08_e6 = 0x08;
- reg_07_fe |= 0x40;
- reg_08_f1 |= 0x02;
- tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
- tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
- 0x02, 0x0f);
- break;
- case TM6000_AMUX_SIF2:
- reg_08_e2 |= 0x02;
- reg_08_e6 = 0x08;
- reg_07_fe |= 0x40;
- reg_08_f1 |= 0x02;
- tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7);
- tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
- 0x02, 0x0f);
- break;
- default:
- break;
- }
- tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2);
- tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6);
- tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1);
- tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe);
- } else {
- switch (dev->vinput[dev->input].vmux) {
- case TM6000_VMUX_VIDEO_A:
- tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
- tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
- tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
- tm6000_set_reg(dev,
- REQ_03_SET_GET_MCU_PIN, dev->vinput[dev->input].v_gpio, 0);
- break;
- case TM6000_VMUX_VIDEO_B:
- tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00);
- tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
- tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
- tm6000_set_reg(dev,
- REQ_03_SET_GET_MCU_PIN, dev->vinput[dev->input].v_gpio, 0);
- break;
- case TM6000_VMUX_VIDEO_AB:
- tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
- tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10);
- tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00);
- tm6000_set_reg(dev,
- REQ_03_SET_GET_MCU_PIN, dev->vinput[dev->input].v_gpio, 1);
- break;
- default:
- break;
- }
- switch (dev->vinput[dev->input].amux) {
- case TM6000_AMUX_ADC1:
- tm6000_set_reg_mask(dev,
- TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f);
- break;
- case TM6000_AMUX_ADC2:
- tm6000_set_reg_mask(dev,
- TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f);
- break;
- default:
- break;
- }
- }
- if (dev->vinput[dev->input].type == TM6000_INPUT_SVIDEO) {
- for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) {
- if (dev->norm & svideo_stds[i].id) {
- rc = tm6000_load_std(dev, svideo_stds[i].common,
- sizeof(svideo_stds[i].
- common));
- goto ret;
- }
- }
- return -EINVAL;
- } else {
- for (i = 0; i < ARRAY_SIZE(composite_stds); i++) {
- if (dev->norm & composite_stds[i].id) {
- rc = tm6000_load_std(dev,
- composite_stds[i].common,
- sizeof(composite_stds[i].
- common));
- goto ret;
- }
- }
- return -EINVAL;
- }
-
-ret:
- if (rc < 0)
- return rc;
-
- if ((dev->dev_type == TM6010) &&
- ((dev->vinput[dev->input].amux == TM6000_AMUX_SIF1) ||
- (dev->vinput[dev->input].amux == TM6000_AMUX_SIF2)))
- tm6000_set_audio_std(dev);
-
- msleep(40);
-
-
- return 0;
-}
diff --git a/drivers/staging/xgifb/XGI_main_26.c b/drivers/staging/xgifb/XGI_main_26.c
index 36db231cd804..277e408c39c5 100644
--- a/drivers/staging/xgifb/XGI_main_26.c
+++ b/drivers/staging/xgifb/XGI_main_26.c
@@ -1300,27 +1300,17 @@ static int XGIfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
return 0;
}
-static int XGIfb_pan_var(struct xgifb_video_info *xgifb_info,
- struct fb_var_screeninfo *var)
+static int XGIfb_pan_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
+ struct xgifb_video_info *xgifb_info = info->par;
unsigned int base;
/* printk("Inside pan_var"); */
- if (var->xoffset > (var->xres_virtual - var->xres)) {
- /* printk("Pan: xo: %d xv %d xr %d\n",
- var->xoffset, var->xres_virtual, var->xres); */
- return -EINVAL;
- }
- if (var->yoffset > (var->yres_virtual - var->yres)) {
- /* printk("Pan: yo: %d yv %d yr %d\n",
- var->yoffset, var->yres_virtual, var->yres); */
- return -EINVAL;
- }
- base = var->yoffset * var->xres_virtual + var->xoffset;
+ base = var->yoffset * info->var.xres_virtual + var->xoffset;
/* calculate base bpp dep. */
- switch (var->bits_per_pixel) {
+ switch (info->var.bits_per_pixel) {
case 16:
base >>= 1;
break;
@@ -1615,13 +1605,12 @@ static int XGIfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
int err;
- struct xgifb_video_info *xgifb_info = info->par;
/* printk("\nInside pan_display:\n"); */
- if (var->xoffset > (var->xres_virtual - var->xres))
+ if (var->xoffset > (info->var.xres_virtual - info->var.xres))
return -EINVAL;
- if (var->yoffset > (var->yres_virtual - var->yres))
+ if (var->yoffset > (info->var.yres_virtual - info->var.yres))
return -EINVAL;
if (var->vmode & FB_VMODE_YWRAP) {
@@ -1634,7 +1623,7 @@ static int XGIfb_pan_display(struct fb_var_screeninfo *var,
> info->var.yres_virtual)
return -EINVAL;
}
- err = XGIfb_pan_var(xgifb_info, var);
+ err = XGIfb_pan_var(var, info);
if (err < 0)
return err;
diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c
index b9926ee0052c..09de99fbb7e0 100644
--- a/drivers/staging/zram/zram_drv.c
+++ b/drivers/staging/zram/zram_drv.c
@@ -556,7 +556,7 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio)
/*
* Handler function for all zram I/O requests.
*/
-static int zram_make_request(struct request_queue *queue, struct bio *bio)
+static void zram_make_request(struct request_queue *queue, struct bio *bio)
{
struct zram *zram = queue->queuedata;
@@ -575,13 +575,12 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio)
__zram_make_request(zram, bio, bio_data_dir(bio));
up_read(&zram->init_lock);
- return 0;
+ return;
error_unlock:
up_read(&zram->init_lock);
error:
bio_io_error(bio);
- return 0;
}
void __zram_reset_device(struct zram *zram)
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 8816f53e004d..b3d17416d86a 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -1,6 +1,6 @@
config VT
bool "Virtual terminal" if EXPERT
- depends on !S390
+ depends on !S390 && !UML
select INPUT
default y
---help---
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 29cbfd8c4e7c..8131e2c28015 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -19,6 +19,7 @@
# define SUPPORT_SYSRQ
#endif
+#include <linux/atomic.h>
#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/io.h>
@@ -33,6 +34,8 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include "msm_serial.h"
@@ -589,9 +592,8 @@ static void msm_release_port(struct uart_port *port)
iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
GSBI_CONTROL);
- gsbi_resource = platform_get_resource_byname(pdev,
- IORESOURCE_MEM,
- "gsbi_resource");
+ gsbi_resource = platform_get_resource(pdev,
+ IORESOURCE_MEM, 1);
if (unlikely(!gsbi_resource))
return;
@@ -612,8 +614,7 @@ static int msm_request_port(struct uart_port *port)
resource_size_t size;
int ret;
- uart_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "uart_resource");
+ uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!uart_resource))
return -ENXIO;
@@ -628,8 +629,7 @@ static int msm_request_port(struct uart_port *port)
goto fail_release_port;
}
- gsbi_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "gsbi_resource");
+ gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
/* Is this a GSBI-based port? */
if (gsbi_resource) {
size = resource_size(gsbi_resource);
@@ -857,6 +857,8 @@ static struct uart_driver msm_uart_driver = {
.cons = MSM_CONSOLE,
};
+static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
+
static int __init msm_serial_probe(struct platform_device *pdev)
{
struct msm_port *msm_port;
@@ -864,6 +866,9 @@ static int __init msm_serial_probe(struct platform_device *pdev)
struct uart_port *port;
int irq;
+ if (pdev->id == -1)
+ pdev->id = atomic_inc_return(&msm_uart_next_id) - 1;
+
if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
return -ENXIO;
@@ -873,7 +878,7 @@ static int __init msm_serial_probe(struct platform_device *pdev)
port->dev = &pdev->dev;
msm_port = UART_TO_MSM(port);
- if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsbi_resource"))
+ if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
msm_port->is_uartdm = 1;
else
msm_port->is_uartdm = 0;
@@ -897,8 +902,7 @@ static int __init msm_serial_probe(struct platform_device *pdev)
printk(KERN_INFO "uartclk = %d\n", port->uartclk);
- resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "uart_resource");
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!resource))
return -ENXIO;
port->mapbase = resource->start;
@@ -922,11 +926,17 @@ static int __devexit msm_serial_remove(struct platform_device *pdev)
return 0;
}
+static struct of_device_id msm_match_table[] = {
+ { .compatible = "qcom,msm-uart" },
+ {}
+};
+
static struct platform_driver msm_platform_driver = {
.remove = msm_serial_remove,
.driver = {
.name = "msm_serial",
.owner = THIS_MODULE,
+ .of_match_table = msm_match_table,
},
};
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index e58cece6f443..e8c9cee07d00 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -200,17 +200,7 @@ static struct platform_driver of_platform_serial_driver = {
.remove = of_platform_serial_remove,
};
-static int __init of_platform_serial_init(void)
-{
- return platform_driver_register(&of_platform_serial_driver);
-}
-module_init(of_platform_serial_init);
-
-static void __exit of_platform_serial_exit(void)
-{
- return platform_driver_unregister(&of_platform_serial_driver);
-};
-module_exit(of_platform_serial_exit);
+module_platform_driver(of_platform_serial_driver);
MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 9871c57b348e..1945c70539c2 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1446,12 +1446,8 @@ static bool filter(struct dma_chan *chan, void *slave)
dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
param->slave_id);
- if (param->dma_dev == chan->device->dev) {
- chan->private = param;
- return true;
- } else {
- return false;
- }
+ chan->private = param;
+ return true;
}
static void rx_timer_fn(unsigned long arg)
@@ -1477,10 +1473,10 @@ static void sci_request_dma(struct uart_port *port)
dma_cap_mask_t mask;
int nent;
- dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__,
- port->line, s->cfg->dma_dev);
+ dev_dbg(port->dev, "%s: port %d\n", __func__,
+ port->line);
- if (!s->cfg->dma_dev)
+ if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)
return;
dma_cap_zero(mask);
@@ -1490,7 +1486,6 @@ static void sci_request_dma(struct uart_port *port)
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
param->slave_id = s->cfg->dma_slave_tx;
- param->dma_dev = s->cfg->dma_dev;
s->cookie_tx = -EINVAL;
chan = dma_request_channel(mask, filter, param);
@@ -1519,7 +1514,6 @@ static void sci_request_dma(struct uart_port *port)
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
param->slave_id = s->cfg->dma_slave_rx;
- param->dma_dev = s->cfg->dma_dev;
chan = dma_request_channel(mask, filter, param);
dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
@@ -1564,9 +1558,6 @@ static void sci_free_dma(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
- if (!s->cfg->dma_dev)
- return;
-
if (s->chan_tx)
sci_tx_dma_release(s, false);
if (s->chan_rx)
@@ -1981,9 +1972,9 @@ static int __devinit sci_init_single(struct platform_device *dev,
port->serial_in = sci_serial_in;
port->serial_out = sci_serial_out;
- if (p->dma_dev)
- dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n",
- p->dma_dev, p->dma_slave_tx, p->dma_slave_rx);
+ if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0)
+ dev_dbg(port->dev, "DMA tx %d, rx %d\n",
+ p->dma_slave_tx, p->dma_slave_rx);
return 0;
}
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 4ac2750491de..791f11bed606 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -62,7 +62,6 @@ config USB_ARCH_HAS_EHCI
boolean
default y if FSL_SOC
default y if PPC_MPC512x
- default y if SOC_AU1200
default y if ARCH_IXP4XX
default y if ARCH_W90X900
default y if ARCH_AT91SAM9G45
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index ed48a5d79e16..7ca290fcb070 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o
+obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o
diff --git a/drivers/usb/host/alchemy-common.c b/drivers/usb/host/alchemy-common.c
new file mode 100644
index 000000000000..b4192c964d0d
--- /dev/null
+++ b/drivers/usb/host/alchemy-common.c
@@ -0,0 +1,337 @@
+/*
+ * USB block power/access management abstraction.
+ *
+ * Au1000+: The OHCI block control register is at the far end of the OHCI memory
+ * area. Au1550 has OHCI on different base address. No need to handle
+ * UDC here.
+ * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG
+ * as well as the PHY for EHCI and UDC.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
+#include <asm/mach-au1x00/au1000.h>
+
+/* control register offsets */
+#define AU1000_OHCICFG 0x7fffc
+#define AU1550_OHCICFG 0x07ffc
+#define AU1200_USBCFG 0x04
+
+/* Au1000 USB block config bits */
+#define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */
+#define USBHEN_CE (1 << 3) /* OHCI block clock enable */
+#define USBHEN_E (1 << 2) /* OHCI block enable */
+#define USBHEN_C (1 << 1) /* OHCI block coherency bit */
+#define USBHEN_BE (1 << 0) /* OHCI Big-Endian */
+
+/* Au1200 USB config bits */
+#define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */
+#define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */
+#define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */
+#define USBCFG_SSD (1 << 23) /* serial short detect en */
+#define USBCFG_PPE (1 << 19) /* HS PHY PLL */
+#define USBCFG_UCE (1 << 18) /* UDC clock enable */
+#define USBCFG_ECE (1 << 17) /* EHCI clock enable */
+#define USBCFG_OCE (1 << 16) /* OHCI clock enable */
+#define USBCFG_FLA(x) (((x) & 0x3f) << 8)
+#define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */
+#define USBCFG_GME (1 << 6) /* OTG mem access */
+#define USBCFG_DBE (1 << 5) /* UDC busmaster enable */
+#define USBCFG_DME (1 << 4) /* UDC mem enable */
+#define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */
+#define USBCFG_EME (1 << 2) /* EHCI mem enable */
+#define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */
+#define USBCFG_OME (1 << 0) /* OHCI mem enable */
+#define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\
+ USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \
+ USBCFG_GME | USBCFG_DBE | USBCFG_DME | \
+ USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \
+ USBCFG_OME)
+
+
+static DEFINE_SPINLOCK(alchemy_usb_lock);
+
+
+static inline void __au1200_ohci_control(void __iomem *base, int enable)
+{
+ unsigned long r = __raw_readl(base + AU1200_USBCFG);
+ if (enable) {
+ __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG);
+ wmb();
+ udelay(2000);
+ } else {
+ __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG);
+ wmb();
+ udelay(1000);
+ }
+}
+
+static inline void __au1200_ehci_control(void __iomem *base, int enable)
+{
+ unsigned long r = __raw_readl(base + AU1200_USBCFG);
+ if (enable) {
+ __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG);
+ wmb();
+ udelay(1000);
+ } else {
+ if (!(r & USBCFG_UCE)) /* UDC also off? */
+ r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
+ __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG);
+ wmb();
+ udelay(1000);
+ }
+}
+
+static inline void __au1200_udc_control(void __iomem *base, int enable)
+{
+ unsigned long r = __raw_readl(base + AU1200_USBCFG);
+ if (enable) {
+ __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG);
+ wmb();
+ } else {
+ if (!(r & USBCFG_ECE)) /* EHCI also off? */
+ r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
+ __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG);
+ wmb();
+ }
+}
+
+static inline int au1200_coherency_bug(void)
+{
+#if defined(CONFIG_DMA_COHERENT)
+ /* Au1200 AB USB does not support coherent memory */
+ if (!(read_c0_prid() & 0xff)) {
+ printk(KERN_INFO "Au1200 USB: this is chip revision AB !!\n");
+ printk(KERN_INFO "Au1200 USB: update your board or re-configure"
+ " the kernel\n");
+ return -ENODEV;
+ }
+#endif
+ return 0;
+}
+
+static inline int au1200_usb_control(int block, int enable)
+{
+ void __iomem *base =
+ (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR);
+ int ret = 0;
+
+ switch (block) {
+ case ALCHEMY_USB_OHCI0:
+ ret = au1200_coherency_bug();
+ if (ret && enable)
+ goto out;
+ __au1200_ohci_control(base, enable);
+ break;
+ case ALCHEMY_USB_UDC0:
+ __au1200_udc_control(base, enable);
+ break;
+ case ALCHEMY_USB_EHCI0:
+ ret = au1200_coherency_bug();
+ if (ret && enable)
+ goto out;
+ __au1200_ehci_control(base, enable);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+out:
+ return ret;
+}
+
+
+/* initialize USB block(s) to a known working state */
+static inline void au1200_usb_init(void)
+{
+ void __iomem *base =
+ (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR);
+ __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG);
+ wmb();
+ udelay(1000);
+}
+
+static inline void au1000_usb_init(unsigned long rb, int reg)
+{
+ void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg);
+ unsigned long r = __raw_readl(base);
+
+#if defined(__BIG_ENDIAN)
+ r |= USBHEN_BE;
+#endif
+ r |= USBHEN_C;
+
+ __raw_writel(r, base);
+ wmb();
+ udelay(1000);
+}
+
+
+static inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg)
+{
+ void __iomem *base = (void __iomem *)KSEG1ADDR(rb);
+ unsigned long r = __raw_readl(base + creg);
+
+ if (enable) {
+ __raw_writel(r | USBHEN_CE, base + creg);
+ wmb();
+ udelay(1000);
+ __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg);
+ wmb();
+ udelay(1000);
+
+ /* wait for reset complete (read reg twice: au1500 erratum) */
+ while (__raw_readl(base + creg),
+ !(__raw_readl(base + creg) & USBHEN_RD))
+ udelay(1000);
+ } else {
+ __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg);
+ wmb();
+ }
+}
+
+static inline int au1000_usb_control(int block, int enable, unsigned long rb,
+ int creg)
+{
+ int ret = 0;
+
+ switch (block) {
+ case ALCHEMY_USB_OHCI0:
+ __au1xx0_ohci_control(enable, rb, creg);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ return ret;
+}
+
+/*
+ * alchemy_usb_control - control Alchemy on-chip USB blocks
+ * @block: USB block to target
+ * @enable: set 1 to enable a block, 0 to disable
+ */
+int alchemy_usb_control(int block, int enable)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&alchemy_usb_lock, flags);
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1000:
+ case ALCHEMY_CPU_AU1500:
+ case ALCHEMY_CPU_AU1100:
+ ret = au1000_usb_control(block, enable,
+ AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG);
+ break;
+ case ALCHEMY_CPU_AU1550:
+ ret = au1000_usb_control(block, enable,
+ AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG);
+ break;
+ case ALCHEMY_CPU_AU1200:
+ ret = au1200_usb_control(block, enable);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(&alchemy_usb_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(alchemy_usb_control);
+
+
+static unsigned long alchemy_usb_pmdata[2];
+
+static void au1000_usb_pm(unsigned long br, int creg, int susp)
+{
+ void __iomem *base = (void __iomem *)KSEG1ADDR(br);
+
+ if (susp) {
+ alchemy_usb_pmdata[0] = __raw_readl(base + creg);
+ /* There appears to be some undocumented reset register.... */
+ __raw_writel(0, base + 0x04);
+ wmb();
+ __raw_writel(0, base + creg);
+ wmb();
+ } else {
+ __raw_writel(alchemy_usb_pmdata[0], base + creg);
+ wmb();
+ }
+}
+
+static void au1200_usb_pm(int susp)
+{
+ void __iomem *base =
+ (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR);
+ if (susp) {
+ /* save OTG_CAP/MUX registers which indicate port routing */
+ /* FIXME: write an OTG driver to do that */
+ alchemy_usb_pmdata[0] = __raw_readl(base + 0x00);
+ alchemy_usb_pmdata[1] = __raw_readl(base + 0x04);
+ } else {
+ /* restore access to all MMIO areas */
+ au1200_usb_init();
+
+ /* restore OTG_CAP/MUX registers */
+ __raw_writel(alchemy_usb_pmdata[0], base + 0x00);
+ __raw_writel(alchemy_usb_pmdata[1], base + 0x04);
+ wmb();
+ }
+}
+
+static void alchemy_usb_pm(int susp)
+{
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1000:
+ case ALCHEMY_CPU_AU1500:
+ case ALCHEMY_CPU_AU1100:
+ au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp);
+ break;
+ case ALCHEMY_CPU_AU1550:
+ au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp);
+ break;
+ case ALCHEMY_CPU_AU1200:
+ au1200_usb_pm(susp);
+ break;
+ }
+}
+
+static int alchemy_usb_suspend(void)
+{
+ alchemy_usb_pm(1);
+ return 0;
+}
+
+static void alchemy_usb_resume(void)
+{
+ alchemy_usb_pm(0);
+}
+
+static struct syscore_ops alchemy_usb_pm_ops = {
+ .suspend = alchemy_usb_suspend,
+ .resume = alchemy_usb_resume,
+};
+
+static int __init alchemy_usb_init(void)
+{
+ switch (alchemy_get_cputype()) {
+ case ALCHEMY_CPU_AU1000:
+ case ALCHEMY_CPU_AU1500:
+ case ALCHEMY_CPU_AU1100:
+ au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG);
+ break;
+ case ALCHEMY_CPU_AU1550:
+ au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG);
+ break;
+ case ALCHEMY_CPU_AU1200:
+ au1200_usb_init();
+ break;
+ }
+
+ register_syscore_ops(&alchemy_usb_pm_ops);
+
+ return 0;
+}
+arch_initcall(alchemy_usb_init);
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index 65719e8d24e4..18bafa99fe57 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -14,61 +14,9 @@
#include <linux/platform_device.h>
#include <asm/mach-au1x00/au1000.h>
-#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG)
-#define USB_MCFG_PFEN (1<<31)
-#define USB_MCFG_RDCOMB (1<<30)
-#define USB_MCFG_SSDEN (1<<23)
-#define USB_MCFG_PHYPLLEN (1<<19)
-#define USB_MCFG_UCECLKEN (1<<18)
-#define USB_MCFG_EHCCLKEN (1<<17)
-#ifdef CONFIG_DMA_COHERENT
-#define USB_MCFG_UCAM (1<<7)
-#else
-#define USB_MCFG_UCAM (0)
-#endif
-#define USB_MCFG_EBMEN (1<<3)
-#define USB_MCFG_EMEMEN (1<<2)
-
-#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
-#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \
- USBH_ENABLE_CE | USB_MCFG_SSDEN | \
- USB_MCFG_UCAM | USB_MCFG_EBMEN | \
- USB_MCFG_EMEMEN)
-
-#define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
extern int usb_disabled(void);
-static void au1xxx_start_ehc(void)
-{
- /* enable clock to EHCI block and HS PHY PLL*/
- au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-
- /* enable EHCI mmio */
- au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-}
-
-static void au1xxx_stop_ehc(void)
-{
- unsigned long c;
-
- /* Disable mem */
- au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-
- /* Disable EHC clock. If the HS PHY is unused disable it too. */
- c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN;
- if (!(c & USB_MCFG_UCECLKEN)) /* UDC disabled? */
- c &= ~USB_MCFG_PHYPLLEN; /* yes: disable HS PHY PLL */
- au_writel(c, USB_HOST_CONFIG);
- au_sync();
-}
-
static int au1xxx_ehci_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -136,16 +84,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
if (usb_disabled())
return -ENODEV;
-#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
- /* Au1200 AB USB does not support coherent memory */
- if (!(read_c0_prid() & 0xff)) {
- printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name);
- printk(KERN_INFO "%s: update your board or re-configure"
- " the kernel\n", pdev->name);
- return -ENODEV;
- }
-#endif
-
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
@@ -171,7 +109,11 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
goto err2;
}
- au1xxx_start_ehc();
+ if (alchemy_usb_control(ALCHEMY_USB_EHCI0, 1)) {
+ printk(KERN_INFO "%s: controller init failed!\n", pdev->name);
+ ret = -ENODEV;
+ goto err3;
+ }
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
@@ -187,7 +129,8 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
return ret;
}
- au1xxx_stop_ehc();
+ alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
+err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
@@ -201,10 +144,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
+ alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
- au1xxx_stop_ehc();
platform_set_drvdata(pdev, NULL);
return 0;
@@ -236,7 +179,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
// could save FLADJ in case of Vaux power loss
// ... we'd only use it to handle clock skew
- au1xxx_stop_ehc();
+ alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
return rc;
}
@@ -246,7 +189,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- au1xxx_start_ehc();
+ alchemy_usb_control(ALCHEMY_USB_EHCI0, 1);
// maybe restore FLADJ
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 59e81615e09c..3ff9f82f7263 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1224,7 +1224,7 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_hcd_sh_driver
#endif
-#ifdef CONFIG_SOC_AU1200
+#ifdef CONFIG_MIPS_ALCHEMY
#include "ehci-au1xxx.c"
#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
#endif
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 944291e10f97..ba3a46b78b75 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -35,8 +35,7 @@ extern int usb_disabled(void);
static void at91_start_clock(void)
{
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_enable(hclk);
+ clk_enable(hclk);
clk_enable(iclk);
clk_enable(fclk);
clocked = 1;
@@ -46,8 +45,7 @@ static void at91_stop_clock(void)
{
clk_disable(fclk);
clk_disable(iclk);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_disable(hclk);
+ clk_disable(hclk);
clocked = 0;
}
@@ -142,8 +140,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
iclk = clk_get(&pdev->dev, "ohci_clk");
fclk = clk_get(&pdev->dev, "uhpck");
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- hclk = clk_get(&pdev->dev, "hck0");
+ hclk = clk_get(&pdev->dev, "hclk");
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
@@ -155,8 +152,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
/* Error handling */
at91_stop_hc(pdev);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_put(hclk);
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
@@ -192,8 +188,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_put(hclk);
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
fclk = iclk = hclk = NULL;
@@ -223,6 +218,156 @@ ohci_at91_start (struct usb_hcd *hcd)
return 0;
}
+static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
+{
+ if (port < 0 || port >= 2)
+ return;
+
+ gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable);
+}
+
+static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
+{
+ if (port < 0 || port >= 2)
+ return -EINVAL;
+
+ return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted;
+}
+
+/*
+ * Update the status data from the hub with the over-current indicator change.
+ */
+static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+ int length = ohci_hub_status_data(hcd, buf);
+ int port;
+
+ for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) {
+ if (pdata->overcurrent_changed[port]) {
+ if (! length)
+ length = 1;
+ buf[0] |= 1 << (port + 1);
+ }
+ }
+
+ return length;
+}
+
+/*
+ * Look at the control requests to the root hub and see if we need to override.
+ */
+static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+ struct usb_hub_descriptor *desc;
+ int ret = -EINVAL;
+ u32 *data = (u32 *)buf;
+
+ dev_dbg(hcd->self.controller,
+ "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n",
+ hcd, typeReq, wValue, wIndex, buf, wLength);
+
+ switch (typeReq) {
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER) {
+ dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
+ ohci_at91_usb_set_power(pdata, wIndex - 1, 1);
+ goto out;
+ }
+ break;
+
+ case ClearPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: C_OVER_CURRENT\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ pdata->overcurrent_changed[wIndex-1] = 0;
+ pdata->overcurrent_status[wIndex-1] = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: OVER_CURRENT\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ pdata->overcurrent_status[wIndex-1] = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: POWER\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ ohci_at91_usb_set_power(pdata, wIndex - 1, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ if (ret)
+ goto out;
+
+ switch (typeReq) {
+ case GetHubDescriptor:
+
+ /* update the hub's descriptor */
+
+ desc = (struct usb_hub_descriptor *)buf;
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ /* remove the old configurations for power-switching, and
+ * over-current protection, and insert our new configuration
+ */
+
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
+ desc->wHubCharacteristics |= cpu_to_le16(0x0001);
+
+ if (pdata->overcurrent_supported) {
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
+ desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001);
+ }
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ return ret;
+
+ case GetPortStatus:
+ /* check port status */
+
+ dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex);
+
+ if (wIndex == 1 || wIndex == 2) {
+ if (! ohci_at91_usb_get_power(pdata, wIndex-1)) {
+ *data &= ~cpu_to_le32(RH_PS_PPS);
+ }
+
+ if (pdata->overcurrent_changed[wIndex-1]) {
+ *data |= cpu_to_le32(RH_PS_OCIC);
+ }
+
+ if (pdata->overcurrent_status[wIndex-1]) {
+ *data |= cpu_to_le32(RH_PS_POCI);
+ }
+ }
+ }
+
+ out:
+ return ret;
+}
+
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_at91_hc_driver = {
@@ -258,8 +403,8 @@ static const struct hc_driver ohci_at91_hc_driver = {
/*
* root hub support
*/
- .hub_status_data = ohci_hub_status_data,
- .hub_control = ohci_hub_control,
+ .hub_status_data = ohci_at91_hub_status_data,
+ .hub_control = ohci_at91_hub_control,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -269,22 +414,71 @@ static const struct hc_driver ohci_at91_hc_driver = {
/*-------------------------------------------------------------------------*/
+static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct at91_usbh_data *pdata = pdev->dev.platform_data;
+ int val, gpio, port;
+
+ /* From the GPIO notifying the over-current situation, find
+ * out the corresponding port */
+ gpio = irq_to_gpio(irq);
+ for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) {
+ if (pdata->overcurrent_pin[port] == gpio)
+ break;
+ }
+
+ if (port == ARRAY_SIZE(pdata->overcurrent_pin)) {
+ dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n");
+ return IRQ_HANDLED;
+ }
+
+ val = gpio_get_value(gpio);
+
+ /* When notified of an over-current situation, disable power
+ on the corresponding port, and mark this port in
+ over-current. */
+ if (! val) {
+ ohci_at91_usb_set_power(pdata, port, 0);
+ pdata->overcurrent_status[port] = 1;
+ pdata->overcurrent_changed[port] = 1;
+ }
+
+ dev_dbg(& pdev->dev, "overcurrent situation %s\n",
+ val ? "exited" : "notified");
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
{
struct at91_usbh_data *pdata = pdev->dev.platform_data;
int i;
if (pdata) {
- /* REVISIT make the driver support per-port power switching,
- * and also overcurrent detection. Here we assume the ports
- * are always powered while this driver is active, and use
- * active-low power switches.
- */
for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) {
if (pdata->vbus_pin[i] <= 0)
continue;
gpio_request(pdata->vbus_pin[i], "ohci_vbus");
- gpio_direction_output(pdata->vbus_pin[i], 0);
+ ohci_at91_usb_set_power(pdata, i, 1);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) {
+ int ret;
+
+ if (pdata->overcurrent_pin[i] <= 0)
+ continue;
+ gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent");
+
+ ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]),
+ ohci_hcd_at91_overcurrent_irq,
+ IRQF_SHARED, "ohci_overcurrent", pdev);
+ if (ret) {
+ gpio_free(pdata->overcurrent_pin[i]);
+ dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n");
+ }
}
}
@@ -301,9 +495,16 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) {
if (pdata->vbus_pin[i] <= 0)
continue;
- gpio_direction_output(pdata->vbus_pin[i], 1);
+ ohci_at91_usb_set_power(pdata, i, 0);
gpio_free(pdata->vbus_pin[i]);
}
+
+ for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) {
+ if (pdata->overcurrent_pin[i] <= 0)
+ continue;
+ free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev);
+ gpio_free(pdata->overcurrent_pin[i]);
+ }
}
device_init_wakeup(&pdev->dev, 0);
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 6b7bc50dfeaa..9b66df8278f3 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -23,92 +23,9 @@
#include <asm/mach-au1x00/au1000.h>
-#ifndef CONFIG_SOC_AU1200
-
-#define USBH_ENABLE_BE (1<<0)
-#define USBH_ENABLE_C (1<<1)
-#define USBH_ENABLE_E (1<<2)
-#define USBH_ENABLE_CE (1<<3)
-#define USBH_ENABLE_RD (1<<4)
-
-#ifdef __LITTLE_ENDIAN
-#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
-#elif defined(__BIG_ENDIAN)
-#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
- USBH_ENABLE_BE)
-#else
-#error not byte order defined
-#endif
-
-#else /* Au1200 */
-
-#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG)
-#define USB_MCFG_PFEN (1<<31)
-#define USB_MCFG_RDCOMB (1<<30)
-#define USB_MCFG_SSDEN (1<<23)
-#define USB_MCFG_OHCCLKEN (1<<16)
-#ifdef CONFIG_DMA_COHERENT
-#define USB_MCFG_UCAM (1<<7)
-#else
-#define USB_MCFG_UCAM (0)
-#endif
-#define USB_MCFG_OBMEN (1<<1)
-#define USB_MCFG_OMEMEN (1<<0)
-
-#define USBH_ENABLE_CE USB_MCFG_OHCCLKEN
-
-#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \
- USBH_ENABLE_CE | USB_MCFG_SSDEN | \
- USB_MCFG_UCAM | \
- USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
-
-#define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
-
-#endif /* Au1200 */
extern int usb_disabled(void);
-static void au1xxx_start_ohc(void)
-{
- /* enable host controller */
-#ifndef CONFIG_SOC_AU1200
- au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-
- au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-
- /* wait for reset complete (read register twice; see au1500 errata) */
- while (au_readl(USB_HOST_CONFIG),
- !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
- udelay(1000);
-
-#else /* Au1200 */
- au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-
- au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
- au_sync();
- udelay(2000);
-#endif /* Au1200 */
-}
-
-static void au1xxx_stop_ohc(void)
-{
-#ifdef CONFIG_SOC_AU1200
- /* Disable mem */
- au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
- au_sync();
- udelay(1000);
-#endif
- /* Disable clock */
- au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
- au_sync();
-}
-
static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
@@ -178,17 +95,6 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
if (usb_disabled())
return -ENODEV;
-#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
- /* Au1200 AB USB does not support coherent memory */
- if (!(read_c0_prid() & 0xff)) {
- printk(KERN_INFO "%s: this is chip revision AB !!\n",
- pdev->name);
- printk(KERN_INFO "%s: update your board or re-configure "
- "the kernel\n", pdev->name);
- return -ENODEV;
- }
-#endif
-
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ\n");
return -ENOMEM;
@@ -214,7 +120,12 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
goto err2;
}
- au1xxx_start_ohc();
+ if (alchemy_usb_control(ALCHEMY_USB_OHCI0, 1)) {
+ printk(KERN_INFO "%s: controller init failed!\n", pdev->name);
+ ret = -ENODEV;
+ goto err3;
+ }
+
ohci_hcd_init(hcd_to_ohci(hcd));
ret = usb_add_hcd(hcd, pdev->resource[1].start,
@@ -224,7 +135,8 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
return ret;
}
- au1xxx_stop_ohc();
+ alchemy_usb_control(ALCHEMY_USB_OHCI0, 0);
+err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
@@ -238,7 +150,7 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- au1xxx_stop_ohc();
+ alchemy_usb_control(ALCHEMY_USB_OHCI0, 0);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
@@ -275,7 +187,7 @@ static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- au1xxx_stop_ohc();
+ alchemy_usb_control(ALCHEMY_USB_OHCI0, 0);
bail:
spin_unlock_irqrestore(&ohci->lock, flags);
@@ -286,7 +198,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
- au1xxx_start_ohc();
+ alchemy_usb_control(ALCHEMY_USB_OHCI0, 1);
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
ohci_finish_controller_resume(hcd);
diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c
index 75a39eab70c3..a425d65d5ba2 100644
--- a/drivers/video/68328fb.c
+++ b/drivers/video/68328fb.c
@@ -378,8 +378,8 @@ static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
|| var->xoffset)
return -EINVAL;
} else {
- if (var->xoffset + var->xres > info->var.xres_virtual ||
- var->yoffset + var->yres > info->var.yres_virtual)
+ if (var->xoffset + info->var.xres > info->var.xres_virtual ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
}
info->var.xoffset = var->xoffset;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 549b960667c8..d83e967e4e15 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -259,6 +259,15 @@ config FB_TILEBLITTING
comment "Frame buffer hardware drivers"
depends on FB
+config FB_GRVGA
+ tristate "Aeroflex Gaisler framebuffer support"
+ depends on FB && SPARC
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ This enables support for the SVGACTRL framebuffer in the GRLIB IP library from Aeroflex Gaisler.
+
config FB_CIRRUS
tristate "Cirrus Logic support"
depends on FB && (ZORRO || PCI)
@@ -1744,7 +1753,7 @@ endchoice
config FB_AU1100
bool "Au1100 LCD Driver"
- depends on (FB = y) && MIPS && SOC_AU1100
+ depends on (FB = y) && MIPS_ALCHEMY
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -1755,10 +1764,11 @@ config FB_AU1100
config FB_AU1200
bool "Au1200 LCD Driver"
- depends on (FB = y) && MIPS && SOC_AU1200
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
+ depends on (FB = y) && MIPS_ALCHEMY
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
help
This is the framebuffer driver for the AMD Au1200 SOC. It can drive
various panels and CRTs by passing in kernel cmd line option
@@ -2027,7 +2037,7 @@ config FB_TMIO_ACCELL
config FB_S3C
tristate "Samsung S3C framebuffer support"
- depends on FB && S3C_DEV_FB
+ depends on FB && (S3C_DEV_FB || S5P_DEV_FIMD0)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -2110,6 +2120,22 @@ config FB_SM501
If unsure, say N.
+config FB_SMSCUFX
+ tristate "SMSC UFX6000/7000 USB Framebuffer support"
+ depends on FB && USB
+ select FB_MODE_HELPERS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
+ ---help---
+ This is a kernel framebuffer driver for SMSC UFX USB devices.
+ Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and
+ mplayer -vo fbdev. Supports both UFX6000 (USB 2.0) and UFX7000
+ (USB 3.0) devices.
+ To compile as a module, choose M here: the module name is smscufx.
+
config FB_UDL
tristate "Displaylink USB Framebuffer support"
depends on FB && USB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 8b83129e209c..9b9d8fff7732 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o
obj-$(CONFIG_FB_ARC) += arcfb.o
obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o
obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o
+obj-$(CONFIG_FB_GRVGA) += grvga.o
obj-$(CONFIG_FB_PM2) += pm2fb.o
obj-$(CONFIG_FB_PM3) += pm3fb.o
@@ -127,6 +128,7 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_UDL) += udlfb.o
+obj-$(CONFIG_FB_SMSCUFX) += smscufx.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c
index 6183a57eb69d..b303f1715065 100644
--- a/drivers/video/acornfb.c
+++ b/drivers/video/acornfb.c
@@ -850,9 +850,10 @@ acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
u_int y_bottom = var->yoffset;
if (!(var->vmode & FB_VMODE_YWRAP))
- y_bottom += var->yres;
+ y_bottom += info->var.yres;
- BUG_ON(y_bottom > var->yres_virtual);
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
acornfb_update_dma(info, var);
diff --git a/drivers/video/arkfb.c b/drivers/video/arkfb.c
index 8686429cbdf0..555dd4c64f5b 100644
--- a/drivers/video/arkfb.c
+++ b/drivers/video/arkfb.c
@@ -908,13 +908,14 @@ static int arkfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info
unsigned int offset;
/* Calculate the offset */
- if (var->bits_per_pixel == 0) {
- offset = (var->yoffset / 16) * (var->xres_virtual / 2) + (var->xoffset / 2);
+ if (info->var.bits_per_pixel == 0) {
+ offset = (var->yoffset / 16) * (info->var.xres_virtual / 2)
+ + (var->xoffset / 2);
offset = offset >> 2;
} else {
offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset * var->bits_per_pixel / 8);
- offset = offset >> ((var->bits_per_pixel == 4) ? 2 : 3);
+ (var->xoffset * info->var.bits_per_pixel / 8);
+ offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 3);
}
/* Set the offset */
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index dda920623c6a..4ac48d9ee665 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -39,7 +39,8 @@
| FBINFO_HWACCEL_YPAN)
static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,
- struct fb_var_screeninfo *var)
+ struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
}
@@ -50,14 +51,16 @@ static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,
| FBINFO_HWACCEL_YPAN)
static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,
- struct fb_var_screeninfo *var)
+ struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
u32 dma2dcfg;
u32 pixeloff;
- pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f;
+ pixeloff = (var->xoffset * info->var.bits_per_pixel) & 0x1f;
- dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8;
+ dma2dcfg = (info->var.xres_virtual - info->var.xres)
+ * info->var.bits_per_pixel / 8;
dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET;
lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg);
@@ -249,14 +252,14 @@ static void atmel_lcdfb_update_dma(struct fb_info *info,
unsigned long dma_addr;
dma_addr = (fix->smem_start + var->yoffset * fix->line_length
- + var->xoffset * var->bits_per_pixel / 8);
+ + var->xoffset * info->var.bits_per_pixel / 8);
dma_addr &= ~3UL;
/* Set framebuffer DMA base address and pixel offset */
lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr);
- atmel_lcdfb_update_dma2d(sinfo, var);
+ atmel_lcdfb_update_dma2d(sinfo, var, info);
}
static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo)
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 32f8cf6200a7..150684882ef7 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -845,16 +845,16 @@ static int radeonfb_pan_display (struct fb_var_screeninfo *var,
{
struct radeonfb_info *rinfo = info->par;
- if ((var->xoffset + var->xres > var->xres_virtual)
- || (var->yoffset + var->yres > var->yres_virtual))
- return -EINVAL;
+ if ((var->xoffset + info->var.xres > info->var.xres_virtual)
+ || (var->yoffset + info->var.yres > info->var.yres_virtual))
+ return -EINVAL;
if (rinfo->asleep)
return 0;
radeon_fifo_wait(2);
- OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset)
- * var->bits_per_pixel / 8) & ~7);
+ OUTREG(CRTC_OFFSET, (var->yoffset * info->fix.line_length +
+ var->xoffset * info->var.bits_per_pixel / 8) & ~7);
return 0;
}
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index 01a8fde67f20..649cb35de4ed 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -110,12 +110,6 @@ static struct fb_var_screeninfo au1100fb_var __devinitdata = {
.vmode = FB_VMODE_NONINTERLACED,
};
-static struct au1100fb_drv_info drv_info;
-
-static int nocursor = 0;
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
-
/* fb_blank
* Blank the screen. Depending on the mode, the screen will be
* activated with the backlight color, or desactivated
@@ -132,7 +126,7 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
/* Turn on panel */
fbdev->regs->lcd_control |= LCD_CONTROL_GO;
#ifdef CONFIG_MIPS_PB1100
- if (drv_info.panel_idx == 1) {
+ if (fbdev->panel_idx == 1) {
au_writew(au_readw(PB1100_G_CONTROL)
| (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
PB1100_G_CONTROL);
@@ -147,7 +141,7 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
/* Turn off panel */
fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
#ifdef CONFIG_MIPS_PB1100
- if (drv_info.panel_idx == 1) {
+ if (fbdev->panel_idx == 1) {
au_writew(au_readw(PB1100_G_CONTROL)
& ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
PB1100_G_CONTROL);
@@ -428,17 +422,6 @@ int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
return 0;
}
-/* fb_cursor
- * Used to disable cursor drawing...
- */
-int au1100fb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
static struct fb_ops au1100fb_ops =
{
.owner = THIS_MODULE,
@@ -450,13 +433,53 @@ static struct fb_ops au1100fb_ops =
.fb_imageblit = cfb_imageblit,
.fb_rotate = au1100fb_fb_rotate,
.fb_mmap = au1100fb_fb_mmap,
- .fb_cursor = au1100fb_fb_cursor,
};
/*-------------------------------------------------------------------------*/
-/* AU1100 LCD controller device driver */
+static int au1100fb_setup(struct au1100fb_device *fbdev)
+{
+ char *this_opt, *options;
+ int num_panels = ARRAY_SIZE(known_lcd_panels);
+
+ if (num_panels <= 0) {
+ print_err("No LCD panels supported by driver!");
+ return -ENODEV;
+ }
+
+ if (fb_get_options(DRIVER_NAME, &options))
+ return -ENODEV;
+ if (!options)
+ return -ENODEV;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ /* Panel option */
+ if (!strncmp(this_opt, "panel:", 6)) {
+ int i;
+ this_opt += 6;
+ for (i = 0; i < num_panels; i++) {
+ if (!strncmp(this_opt, known_lcd_panels[i].name,
+ strlen(this_opt))) {
+ fbdev->panel = &known_lcd_panels[i];
+ fbdev->panel_idx = i;
+ break;
+ }
+ }
+ if (i >= num_panels) {
+ print_warn("Panel '%s' not supported!", this_opt);
+ return -ENODEV;
+ }
+ }
+ /* Unsupported option */
+ else
+ print_warn("Unsupported option \"%s\"", this_opt);
+ }
+
+ print_info("Panel=%s", fbdev->panel->name);
+
+ return 0;
+}
static int __devinit au1100fb_drv_probe(struct platform_device *dev)
{
@@ -465,22 +488,21 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
unsigned long page;
u32 sys_clksrc;
- if (!dev)
- return -EINVAL;
-
/* Allocate new device private */
- if (!(fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) {
+ fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL);
+ if (!fbdev) {
print_err("fail to allocate device private record");
return -ENOMEM;
}
- fbdev->panel = &known_lcd_panels[drv_info.panel_idx];
+ if (au1100fb_setup(fbdev))
+ goto failed;
platform_set_drvdata(dev, (void *)fbdev);
/* Allocate region for our registers and map them */
- if (!(regs_res = platform_get_resource(to_platform_device(dev),
- IORESOURCE_MEM, 0))) {
+ regs_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!regs_res) {
print_err("fail to retrieve registers resource");
return -EFAULT;
}
@@ -500,13 +522,11 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
print_dbg("Register memory map at %p", fbdev->regs);
print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len);
-
-
/* Allocate the framebuffer to the maximum screen size * nbr of video buffers */
fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres *
(fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS;
- fbdev->fb_mem = dma_alloc_coherent(dev, PAGE_ALIGN(fbdev->fb_len),
+ fbdev->fb_mem = dma_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len),
&fbdev->fb_phys, GFP_KERNEL);
if (!fbdev->fb_mem) {
print_err("fail to allocate frambuffer (size: %dK))",
@@ -525,7 +545,7 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len);
page += PAGE_SIZE) {
#if CONFIG_DMA_NONCOHERENT
- SetPageReserved(virt_to_page(CAC_ADDR(page)));
+ SetPageReserved(virt_to_page(CAC_ADDR((void *)page)));
#else
SetPageReserved(virt_to_page(page));
#endif
@@ -578,7 +598,8 @@ failed:
release_mem_region(fbdev->regs_phys, fbdev->regs_len);
}
if (fbdev->fb_mem) {
- dma_free_noncoherent(dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys);
+ dma_free_noncoherent(&dev->dev, fbdev->fb_len, fbdev->fb_mem,
+ fbdev->fb_phys);
}
if (fbdev->info.cmap.len != 0) {
fb_dealloc_cmap(&fbdev->info.cmap);
@@ -608,7 +629,8 @@ int au1100fb_drv_remove(struct platform_device *dev)
release_mem_region(fbdev->regs_phys, fbdev->regs_len);
- dma_free_coherent(dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys);
+ dma_free_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem,
+ fbdev->fb_phys);
fb_dealloc_cmap(&fbdev->info.cmap);
kfree(fbdev->info.pseudo_palette);
@@ -675,101 +697,18 @@ static struct platform_driver au1100fb_driver = {
.resume = au1100fb_drv_resume,
};
-/*-------------------------------------------------------------------------*/
-
-/* Kernel driver */
-
-int au1100fb_setup(char *options)
-{
- char* this_opt;
- int num_panels = ARRAY_SIZE(known_lcd_panels);
- char* mode = NULL;
- int panel_idx = 0;
-
- if (num_panels <= 0) {
- print_err("No LCD panels supported by driver!");
- return -EFAULT;
- }
-
- if (options) {
- while ((this_opt = strsep(&options,",")) != NULL) {
- /* Panel option */
- if (!strncmp(this_opt, "panel:", 6)) {
- int i;
- this_opt += 6;
- for (i = 0; i < num_panels; i++) {
- if (!strncmp(this_opt,
- known_lcd_panels[i].name,
- strlen(this_opt))) {
- panel_idx = i;
- break;
- }
- }
- if (i >= num_panels) {
- print_warn("Panel %s not supported!", this_opt);
- }
- }
- if (!strncmp(this_opt, "nocursor", 8)) {
- this_opt += 8;
- nocursor = 1;
- print_info("Cursor disabled");
- }
- /* Mode option (only option that start with digit) */
- else if (isdigit(this_opt[0])) {
- mode = kstrdup(this_opt, GFP_KERNEL);
- if (!mode) {
- print_err("memory allocation failed");
- return -ENOMEM;
- }
- }
- /* Unsupported option */
- else {
- print_warn("Unsupported option \"%s\"", this_opt);
- }
- }
- }
-
- drv_info.panel_idx = panel_idx;
- drv_info.opt_mode = mode;
-
- print_info("Panel=%s Mode=%s",
- known_lcd_panels[drv_info.panel_idx].name,
- drv_info.opt_mode ? drv_info.opt_mode : "default");
-
- return 0;
-}
-
-int __init au1100fb_init(void)
+static int __init au1100fb_load(void)
{
- char* options;
- int ret;
-
- print_info("" DRIVER_DESC "");
-
- memset(&drv_info, 0, sizeof(drv_info));
-
- if (fb_get_options(DRIVER_NAME, &options))
- return -ENODEV;
-
- /* Setup driver with options */
- ret = au1100fb_setup(options);
- if (ret < 0) {
- print_err("Fail to setup driver");
- return ret;
- }
-
return platform_driver_register(&au1100fb_driver);
}
-void __exit au1100fb_cleanup(void)
+static void __exit au1100fb_unload(void)
{
platform_driver_unregister(&au1100fb_driver);
-
- kfree(drv_info.opt_mode);
}
-module_init(au1100fb_init);
-module_exit(au1100fb_cleanup);
+module_init(au1100fb_load);
+module_exit(au1100fb_unload);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/au1100fb.h b/drivers/video/au1100fb.h
index 164fe2f231ec..12d9642d5465 100644
--- a/drivers/video/au1100fb.h
+++ b/drivers/video/au1100fb.h
@@ -108,6 +108,7 @@ struct au1100fb_device {
unsigned char* fb_mem; /* FrameBuffer memory map */
size_t fb_len;
dma_addr_t fb_phys;
+ int panel_idx;
};
/********************************************************************/
@@ -364,11 +365,6 @@ static struct au1100fb_panel known_lcd_panels[] =
},
};
-struct au1100fb_drv_info {
- int panel_idx;
- char *opt_mode;
-};
-
/********************************************************************/
/* Inline helpers */
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index 5dff32ac8044..72005598040f 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -46,18 +46,10 @@
#include <asm/mach-au1x00/au1000.h>
#include "au1200fb.h"
-#ifdef CONFIG_PM
-#include <asm/mach-au1x00/au1xxx_pm.h>
-#endif
-
-#ifndef CONFIG_FB_AU1200_DEVS
-#define CONFIG_FB_AU1200_DEVS 4
-#endif
-
#define DRIVER_NAME "au1200fb"
#define DRIVER_DESC "LCD controller driver for AU1200 processors"
-#define DEBUG 1
+#define DEBUG 0
#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg)
#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg)
@@ -150,7 +142,7 @@ struct au1200_lcd_iodata_t {
/* Private, per-framebuffer management information (independent of the panel itself) */
struct au1200fb_device {
- struct fb_info fb_info; /* FB driver info record */
+ struct fb_info *fb_info; /* FB driver info record */
int plane;
unsigned char* fb_mem; /* FrameBuffer memory map */
@@ -158,7 +150,6 @@ struct au1200fb_device {
dma_addr_t fb_phys;
};
-static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS];
/********************************************************************/
/* LCD controller restrictions */
@@ -171,10 +162,18 @@ static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS];
/* Default number of visible screen buffer to allocate */
#define AU1200FB_NBR_VIDEO_BUFFERS 1
+/* Default maximum number of fb devices to create */
+#define MAX_DEVICE_COUNT 4
+
+/* Default window configuration entry to use (see windows[]) */
+#define DEFAULT_WINDOW_INDEX 2
+
/********************************************************************/
+static struct fb_info *_au1200fb_infos[MAX_DEVICE_COUNT];
static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR;
-static int window_index = 2; /* default is zero */
+static int device_count = MAX_DEVICE_COUNT;
+static int window_index = DEFAULT_WINDOW_INDEX; /* default is zero */
static int panel_index = 2; /* default is zero */
static struct window_settings *win;
static struct panel_settings *panel;
@@ -205,12 +204,6 @@ struct window_settings {
extern int board_au1200fb_panel_init (void);
extern int board_au1200fb_panel_shutdown (void);
-#ifdef CONFIG_PM
-int au1200fb_pm_callback(au1xxx_power_dev_t *dev,
- au1xxx_request_t request, void *data);
-au1xxx_power_dev_t *LCD_pm_dev;
-#endif
-
/*
* Default window configurations
*/
@@ -652,25 +645,6 @@ static struct panel_settings known_lcd_panels[] =
/********************************************************************/
-#ifdef CONFIG_PM
-static int set_brightness(unsigned int brightness)
-{
- unsigned int hi1, divider;
-
- /* limit brightness pwm duty to >= 30/1600 */
- if (brightness < 30) {
- brightness = 30;
- }
- divider = (lcd->pwmdiv & 0x3FFFF) + 1;
- hi1 = (lcd->pwmhi >> 16) + 1;
- hi1 = (((brightness & 0xFF) + 1) * divider >> 8);
- lcd->pwmhi &= 0xFFFF;
- lcd->pwmhi |= (hi1 << 16);
-
- return brightness;
-}
-#endif /* CONFIG_PM */
-
static int winbpp (unsigned int winctrl1)
{
int bits = 0;
@@ -712,8 +686,8 @@ static int fbinfo2index (struct fb_info *fb_info)
{
int i;
- for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) {
- if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info))
+ for (i = 0; i < device_count; ++i) {
+ if (fb_info == _au1200fb_infos[i])
return i;
}
printk("au1200fb: ERROR: fbinfo2index failed!\n");
@@ -962,7 +936,7 @@ static void au1200_setmode(struct au1200fb_device *fbdev)
lcd->window[plane].winctrl2 = ( 0
| LCD_WINCTRL2_CKMODE_00
| LCD_WINCTRL2_DBM
- | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length)
+ | LCD_WINCTRL2_BX_N(fbdev->fb_info->fix.line_length)
| LCD_WINCTRL2_SCX_1
| LCD_WINCTRL2_SCY_1
) ;
@@ -1050,7 +1024,7 @@ static void au1200fb_update_fbinfo(struct fb_info *fbi)
static int au1200fb_fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *fbi)
{
- struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi;
+ struct au1200fb_device *fbdev = fbi->par;
u32 pixclock;
int screen_size, plane;
@@ -1142,7 +1116,7 @@ static int au1200fb_fb_check_var(struct fb_var_screeninfo *var,
*/
static int au1200fb_fb_set_par(struct fb_info *fbi)
{
- struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi;
+ struct au1200fb_device *fbdev = fbi->par;
au1200fb_update_fbinfo(fbi);
au1200_setmode(fbdev);
@@ -1246,11 +1220,7 @@ static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
unsigned int len;
unsigned long start=0, off;
- struct au1200fb_device *fbdev = (struct au1200fb_device *) info;
-
-#ifdef CONFIG_PM
- au1xxx_pm_access(LCD_pm_dev);
-#endif
+ struct au1200fb_device *fbdev = info->par;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
return -EINVAL;
@@ -1461,10 +1431,6 @@ static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd,
int plane;
int val;
-#ifdef CONFIG_PM
- au1xxx_pm_access(LCD_pm_dev);
-#endif
-
plane = fbinfo2index(info);
print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane);
@@ -1536,9 +1502,11 @@ static struct fb_ops au1200fb_fb_ops = {
.fb_set_par = au1200fb_fb_set_par,
.fb_setcolreg = au1200fb_fb_setcolreg,
.fb_blank = au1200fb_fb_blank,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
.fb_sync = NULL,
.fb_ioctl = au1200fb_ioctl,
.fb_mmap = au1200fb_fb_mmap,
@@ -1561,10 +1529,9 @@ static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id)
static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev)
{
- struct fb_info *fbi = &fbdev->fb_info;
+ struct fb_info *fbi = fbdev->fb_info;
int bpp;
- memset(fbi, 0, sizeof(struct fb_info));
fbi->fbops = &au1200fb_fb_ops;
bpp = winbpp(win->w[fbdev->plane].mode_winctrl1);
@@ -1623,24 +1590,36 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev)
/* AU1200 LCD controller device driver */
-static int au1200fb_drv_probe(struct platform_device *dev)
+static int __devinit au1200fb_drv_probe(struct platform_device *dev)
{
struct au1200fb_device *fbdev;
+ struct fb_info *fbi = NULL;
unsigned long page;
- int bpp, plane, ret;
+ int bpp, plane, ret, irq;
- if (!dev)
- return -EINVAL;
+ /* shut gcc up */
+ ret = 0;
+ fbdev = NULL;
+
+ /* Kickstart the panel */
+ au1200_setpanel(panel);
- for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) {
+ for (plane = 0; plane < device_count; ++plane) {
bpp = winbpp(win->w[plane].mode_winctrl1);
if (win->w[plane].xres == 0)
win->w[plane].xres = panel->Xres;
if (win->w[plane].yres == 0)
win->w[plane].yres = panel->Yres;
- fbdev = &_au1200fb_devices[plane];
- memset(fbdev, 0, sizeof(struct au1200fb_device));
+ fbi = framebuffer_alloc(sizeof(struct au1200fb_device),
+ &dev->dev);
+ if (!fbi)
+ goto failed;
+
+ _au1200fb_infos[plane] = fbi;
+ fbdev = fbi->par;
+ fbdev->fb_info = fbi;
+
fbdev->plane = plane;
/* Allocate the framebuffer to the maximum screen size */
@@ -1673,30 +1652,31 @@ static int au1200fb_drv_probe(struct platform_device *dev)
goto failed;
/* Register new framebuffer */
- if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) {
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
print_err("cannot register new framebuffer");
goto failed;
}
- au1200fb_fb_set_par(&fbdev->fb_info);
+ au1200fb_fb_set_par(fbi);
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
if (plane == 0)
- if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) {
+ if (fb_prepare_logo(fbi, FB_ROTATE_UR)) {
/* Start display and show logo on boot */
- fb_set_cmap(&fbdev->fb_info.cmap,
- &fbdev->fb_info);
-
- fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR);
+ fb_set_cmap(&fbi->cmap, fbi);
+ fb_show_logo(fbi, FB_ROTATE_UR);
}
#endif
}
/* Now hook interrupt too */
- if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq,
- IRQF_DISABLED | IRQF_SHARED, "lcd", (void *)dev)) < 0) {
+ irq = platform_get_irq(dev, 0);
+ ret = request_irq(irq, au1200fb_handle_irq,
+ IRQF_SHARED, "lcd", (void *)dev);
+ if (ret) {
print_err("fail to request interrupt line %d (err: %d)",
- AU1200_LCD_INT, ret);
+ irq, ret);
goto failed;
}
@@ -1705,84 +1685,108 @@ static int au1200fb_drv_probe(struct platform_device *dev)
failed:
/* NOTE: This only does the current plane/window that failed; others are still active */
if (fbdev->fb_mem)
- dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len),
+ dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len),
fbdev->fb_mem, fbdev->fb_phys);
- if (fbdev->fb_info.cmap.len != 0)
- fb_dealloc_cmap(&fbdev->fb_info.cmap);
- if (fbdev->fb_info.pseudo_palette)
- kfree(fbdev->fb_info.pseudo_palette);
+ if (fbi) {
+ if (fbi->cmap.len != 0)
+ fb_dealloc_cmap(&fbi->cmap);
+ kfree(fbi->pseudo_palette);
+ }
if (plane == 0)
free_irq(AU1200_LCD_INT, (void*)dev);
return ret;
}
-static int au1200fb_drv_remove(struct platform_device *dev)
+static int __devexit au1200fb_drv_remove(struct platform_device *dev)
{
struct au1200fb_device *fbdev;
+ struct fb_info *fbi;
int plane;
- if (!dev)
- return -ENODEV;
-
/* Turn off the panel */
au1200_setpanel(NULL);
- for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane)
- {
- fbdev = &_au1200fb_devices[plane];
+ for (plane = 0; plane < device_count; ++plane) {
+ fbi = _au1200fb_infos[plane];
+ fbdev = fbi->par;
/* Clean up all probe data */
- unregister_framebuffer(&fbdev->fb_info);
+ unregister_framebuffer(fbi);
if (fbdev->fb_mem)
dma_free_noncoherent(&dev->dev,
PAGE_ALIGN(fbdev->fb_len),
fbdev->fb_mem, fbdev->fb_phys);
- if (fbdev->fb_info.cmap.len != 0)
- fb_dealloc_cmap(&fbdev->fb_info.cmap);
- if (fbdev->fb_info.pseudo_palette)
- kfree(fbdev->fb_info.pseudo_palette);
+ if (fbi->cmap.len != 0)
+ fb_dealloc_cmap(&fbi->cmap);
+ kfree(fbi->pseudo_palette);
+
+ framebuffer_release(fbi);
+ _au1200fb_infos[plane] = NULL;
}
- free_irq(AU1200_LCD_INT, (void *)dev);
+ free_irq(platform_get_irq(dev, 0), (void *)dev);
return 0;
}
#ifdef CONFIG_PM
-static int au1200fb_drv_suspend(struct platform_device *dev, u32 state)
+static int au1200fb_drv_suspend(struct device *dev)
{
- /* TODO */
+ au1200_setpanel(NULL);
+
+ lcd->outmask = 0;
+ au_sync();
+
return 0;
}
-static int au1200fb_drv_resume(struct platform_device *dev)
+static int au1200fb_drv_resume(struct device *dev)
{
- /* TODO */
+ struct fb_info *fbi;
+ int i;
+
+ /* Kickstart the panel */
+ au1200_setpanel(panel);
+
+ for (i = 0; i < device_count; i++) {
+ fbi = _au1200fb_infos[i];
+ au1200fb_fb_set_par(fbi);
+ }
+
return 0;
}
+
+static const struct dev_pm_ops au1200fb_pmops = {
+ .suspend = au1200fb_drv_suspend,
+ .resume = au1200fb_drv_resume,
+ .freeze = au1200fb_drv_suspend,
+ .thaw = au1200fb_drv_resume,
+};
+
+#define AU1200FB_PMOPS (&au1200fb_pmops)
+
+#else
+#define AU1200FB_PMOPS NULL
#endif /* CONFIG_PM */
static struct platform_driver au1200fb_driver = {
.driver = {
- .name = "au1200-lcd",
- .owner = THIS_MODULE,
+ .name = "au1200-lcd",
+ .owner = THIS_MODULE,
+ .pm = AU1200FB_PMOPS,
},
.probe = au1200fb_drv_probe,
- .remove = au1200fb_drv_remove,
-#ifdef CONFIG_PM
- .suspend = au1200fb_drv_suspend,
- .resume = au1200fb_drv_resume,
-#endif
+ .remove = __devexit_p(au1200fb_drv_remove),
};
/*-------------------------------------------------------------------------*/
/* Kernel driver */
-static void au1200fb_setup(void)
+static int au1200fb_setup(void)
{
- char* options = NULL;
- char* this_opt;
+ char *options = NULL;
+ char *this_opt, *endptr;
int num_panels = ARRAY_SIZE(known_lcd_panels);
int panel_idx = -1;
@@ -1827,70 +1831,42 @@ static void au1200fb_setup(void)
nohwcursor = 1;
}
- /* Unsupported option */
- else {
- print_warn("Unsupported option \"%s\"", this_opt);
+ else if (strncmp(this_opt, "devices:", 8) == 0) {
+ this_opt += 8;
+ device_count = simple_strtol(this_opt,
+ &endptr, 0);
+ if ((device_count < 0) ||
+ (device_count > MAX_DEVICE_COUNT))
+ device_count = MAX_DEVICE_COUNT;
}
- }
- }
-}
-#ifdef CONFIG_PM
-static int au1200fb_pm_callback(au1xxx_power_dev_t *dev,
- au1xxx_request_t request, void *data) {
- int retval = -1;
- unsigned int d = 0;
- unsigned int brightness = 0;
-
- if (request == AU1XXX_PM_SLEEP) {
- board_au1200fb_panel_shutdown();
- }
- else if (request == AU1XXX_PM_WAKEUP) {
- if(dev->prev_state == SLEEP_STATE)
- {
- int plane;
- au1200_setpanel(panel);
- for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) {
- struct au1200fb_device *fbdev;
- fbdev = &_au1200fb_devices[plane];
- au1200fb_fb_set_par(&fbdev->fb_info);
+ else if (strncmp(this_opt, "wincfg:", 7) == 0) {
+ this_opt += 7;
+ window_index = simple_strtol(this_opt,
+ &endptr, 0);
+ if ((window_index < 0) ||
+ (window_index >= ARRAY_SIZE(windows)))
+ window_index = DEFAULT_WINDOW_INDEX;
}
- }
- d = *((unsigned int*)data);
- if(d <=10) brightness = 26;
- else if(d<=20) brightness = 51;
- else if(d<=30) brightness = 77;
- else if(d<=40) brightness = 102;
- else if(d<=50) brightness = 128;
- else if(d<=60) brightness = 153;
- else if(d<=70) brightness = 179;
- else if(d<=80) brightness = 204;
- else if(d<=90) brightness = 230;
- else brightness = 255;
- set_brightness(brightness);
- } else if (request == AU1XXX_PM_GETSTATUS) {
- return dev->cur_state;
- } else if (request == AU1XXX_PM_ACCESS) {
- if (dev->cur_state != SLEEP_STATE)
- return retval;
- else {
- au1200_setpanel(panel);
+ else if (strncmp(this_opt, "off", 3) == 0)
+ return 1;
+ /* Unsupported option */
+ else {
+ print_warn("Unsupported option \"%s\"", this_opt);
+ }
}
- } else if (request == AU1XXX_PM_IDLE) {
- } else if (request == AU1XXX_PM_CLEANUP) {
}
-
- return retval;
+ return 0;
}
-#endif
static int __init au1200fb_init(void)
{
print_info("" DRIVER_DESC "");
/* Setup driver with options */
- au1200fb_setup();
+ if (au1200fb_setup())
+ return -ENODEV;
/* Point to the panel selected */
panel = &known_lcd_panels[panel_index];
@@ -1899,17 +1875,6 @@ static int __init au1200fb_init(void)
printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name);
printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name);
- /* Kickstart the panel, the framebuffers/windows come soon enough */
- au1200_setpanel(panel);
-
- #ifdef CONFIG_PM
- LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL);
- if ( LCD_pm_dev == NULL)
- printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n");
- else
- printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n");
- #endif
-
return platform_driver_register(&au1200fb_driver);
}
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
index 183b6f639852..66bc74d9ce2a 100644
--- a/drivers/video/backlight/adp8860_bl.c
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -7,7 +7,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pm.h>
diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
index d06886a2bfb5..6c68a6899e87 100644
--- a/drivers/video/backlight/adp8870_bl.c
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -7,7 +7,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pm.h>
@@ -932,7 +931,6 @@ out:
out1:
backlight_device_unregister(bl);
out2:
- i2c_set_clientdata(client, NULL);
kfree(data);
return ret;
@@ -952,7 +950,6 @@ static int __devexit adp8870_remove(struct i2c_client *client)
&adp8870_bl_attr_group);
backlight_device_unregister(data->bl);
- i2c_set_clientdata(client, NULL);
kfree(data);
return 0;
diff --git a/drivers/video/backlight/generic_bl.c b/drivers/video/backlight/generic_bl.c
index 8c6befd65a33..adb191466d64 100644
--- a/drivers/video/backlight/generic_bl.c
+++ b/drivers/video/backlight/generic_bl.c
@@ -56,7 +56,7 @@ static int genericbl_get_intensity(struct backlight_device *bd)
* Called when the battery is low to limit the backlight intensity.
* If limit==0 clear any limit, otherwise limit the intensity
*/
-void corgibl_limit_intensity(int limit)
+void genericbl_limit_intensity(int limit)
{
struct backlight_device *bd = generic_backlight_device;
@@ -68,7 +68,7 @@ void corgibl_limit_intensity(int limit)
backlight_update_status(generic_backlight_device);
mutex_unlock(&bd->ops_lock);
}
-EXPORT_SYMBOL(corgibl_limit_intensity);
+EXPORT_SYMBOL(genericbl_limit_intensity);
static const struct backlight_ops genericbl_ops = {
.options = BL_CORE_SUSPENDRESUME,
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
index 98ad3e5f7c85..3543f1b7d5f1 100644
--- a/drivers/video/backlight/l4f00242t03.c
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -52,15 +52,11 @@ static void l4f00242t03_lcd_init(struct spi_device *spi)
dev_dbg(&spi->dev, "initializing LCD\n");
- if (priv->io_reg) {
- regulator_set_voltage(priv->io_reg, 1800000, 1800000);
- regulator_enable(priv->io_reg);
- }
+ regulator_set_voltage(priv->io_reg, 1800000, 1800000);
+ regulator_enable(priv->io_reg);
- if (priv->core_reg) {
- regulator_set_voltage(priv->core_reg, 2800000, 2800000);
- regulator_enable(priv->core_reg);
- }
+ regulator_set_voltage(priv->core_reg, 2800000, 2800000);
+ regulator_enable(priv->core_reg);
l4f00242t03_reset(pdata->reset_gpio);
@@ -78,11 +74,8 @@ static void l4f00242t03_lcd_powerdown(struct spi_device *spi)
gpio_set_value(pdata->data_enable_gpio, 0);
- if (priv->io_reg)
- regulator_disable(priv->io_reg);
-
- if (priv->core_reg)
- regulator_disable(priv->core_reg);
+ regulator_disable(priv->io_reg);
+ regulator_disable(priv->core_reg);
}
static int l4f00242t03_lcd_power_get(struct lcd_device *ld)
@@ -178,47 +171,34 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
priv->spi = spi;
- ret = gpio_request(pdata->reset_gpio, "lcd l4f00242t03 reset");
+ ret = gpio_request_one(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");
goto err;
}
- ret = gpio_direction_output(pdata->reset_gpio, 1);
- if (ret)
- goto err2;
-
- ret = gpio_request(pdata->data_enable_gpio,
- "lcd l4f00242t03 data enable");
+ ret = gpio_request_one(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 err2;
}
- ret = gpio_direction_output(pdata->data_enable_gpio, 0);
- if (ret)
+ priv->io_reg = regulator_get(&spi->dev, "vdd");
+ if (IS_ERR(priv->io_reg)) {
+ dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
+ __func__);
goto err3;
-
- if (pdata->io_supply) {
- priv->io_reg = regulator_get(NULL, pdata->io_supply);
-
- if (IS_ERR(priv->io_reg)) {
- pr_err("%s: Unable to get the IO regulator\n",
- __func__);
- goto err3;
- }
}
- if (pdata->core_supply) {
- priv->core_reg = regulator_get(NULL, pdata->core_supply);
-
- if (IS_ERR(priv->core_reg)) {
- pr_err("%s: Unable to get the core regulator\n",
- __func__);
- goto err4;
- }
+ priv->core_reg = regulator_get(&spi->dev, "vcore");
+ if (IS_ERR(priv->core_reg)) {
+ dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
+ __func__);
+ goto err4;
}
priv->ld = lcd_device_register("l4f00242t03",
@@ -238,11 +218,9 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
return 0;
err5:
- if (priv->core_reg)
- regulator_put(priv->core_reg);
+ regulator_put(priv->core_reg);
err4:
- if (priv->io_reg)
- regulator_put(priv->io_reg);
+ regulator_put(priv->io_reg);
err3:
gpio_free(pdata->data_enable_gpio);
err2:
@@ -266,10 +244,8 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi)
gpio_free(pdata->data_enable_gpio);
gpio_free(pdata->reset_gpio);
- if (priv->io_reg)
- regulator_put(priv->io_reg);
- if (priv->core_reg)
- regulator_put(priv->core_reg);
+ regulator_put(priv->io_reg);
+ regulator_put(priv->core_reg);
kfree(priv);
diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c
index 2464b910b590..56720fb476b3 100644
--- a/drivers/video/bf54x-lq043fb.c
+++ b/drivers/video/bf54x-lq043fb.c
@@ -633,7 +633,7 @@ static int __devinit bfin_bf54x_probe(struct platform_device *pdev)
goto out7;
}
- if (request_irq(info->irq, bfin_bf54x_irq_error, IRQF_DISABLED,
+ if (request_irq(info->irq, bfin_bf54x_irq_error, 0,
"PPI ERROR", info) < 0) {
printk(KERN_ERR DRIVER_NAME
": unable to request PPI ERROR IRQ\n");
diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c
index 23b6c4b62c78..c633068372c9 100644
--- a/drivers/video/bfin-lq035q1-fb.c
+++ b/drivers/video/bfin-lq035q1-fb.c
@@ -695,7 +695,7 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev)
goto out7;
}
- ret = request_irq(info->irq, bfin_lq035q1_irq_error, IRQF_DISABLED,
+ ret = request_irq(info->irq, bfin_lq035q1_irq_error, 0,
DRIVER_NAME" PPI ERROR", info);
if (ret < 0) {
dev_err(&pdev->dev, "unable to request PPI ERROR IRQ\n");
diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c
index d8de29f0dd8d..d5e126759612 100644
--- a/drivers/video/bfin-t350mcqb-fb.c
+++ b/drivers/video/bfin-t350mcqb-fb.c
@@ -529,7 +529,7 @@ static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev)
goto out7;
}
- ret = request_irq(info->irq, bfin_t350mcqb_irq_error, IRQF_DISABLED,
+ ret = request_irq(info->irq, bfin_t350mcqb_irq_error, 0,
"PPI ERROR", info);
if (ret < 0) {
printk(KERN_ERR DRIVER_NAME
diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c
index 8486f541156b..811dd7f6aa41 100644
--- a/drivers/video/bfin_adv7393fb.c
+++ b/drivers/video/bfin_adv7393fb.c
@@ -481,7 +481,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client,
goto out_4;
}
- if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, IRQF_DISABLED,
+ if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0,
"PPI ERROR", fbdev) < 0) {
dev_err(&client->dev, "unable to request PPI ERROR IRQ\n");
ret = -EFAULT;
diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c
index caaa27d4a46a..cb09aa1fa138 100644
--- a/drivers/video/carminefb.c
+++ b/drivers/video/carminefb.c
@@ -32,11 +32,11 @@
#define CARMINEFB_DEFAULT_VIDEO_MODE 1
static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
-module_param(fb_mode, uint, 444);
+module_param(fb_mode, uint, 0444);
MODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");
static char *fb_mode_str;
-module_param(fb_mode_str, charp, 444);
+module_param(fb_mode_str, charp, 0444);
MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");
/*
@@ -46,7 +46,7 @@ MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");
* 0b010 Display 1
*/
static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;
-module_param(fb_displays, int, 444);
+module_param(fb_displays, int, 0444);
MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");
struct carmine_hw {
diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c
index 9075bea55879..7b2c40abae15 100644
--- a/drivers/video/controlfb.c
+++ b/drivers/video/controlfb.c
@@ -550,7 +550,7 @@ static void control_set_hardware(struct fb_info_control *p, struct fb_par_contro
/*
- * Parse user speficied options (`video=controlfb:')
+ * Parse user specified options (`video=controlfb:')
*/
static void __init control_setup(char *options)
{
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index fcdac872522d..55f91d9ab00b 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -35,6 +35,9 @@
#define DRIVER_NAME "da8xx_lcdc"
+#define LCD_VERSION_1 1
+#define LCD_VERSION_2 2
+
/* LCD Status Register */
#define LCD_END_OF_FRAME1 BIT(9)
#define LCD_END_OF_FRAME0 BIT(8)
@@ -49,7 +52,9 @@
#define LCD_DMA_BURST_4 0x2
#define LCD_DMA_BURST_8 0x3
#define LCD_DMA_BURST_16 0x4
-#define LCD_END_OF_FRAME_INT_ENA BIT(2)
+#define LCD_V1_END_OF_FRAME_INT_ENA BIT(2)
+#define LCD_V2_END_OF_FRAME0_INT_ENA BIT(8)
+#define LCD_V2_END_OF_FRAME1_INT_ENA BIT(9)
#define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0)
/* LCD Control Register */
@@ -65,12 +70,18 @@
#define LCD_MONO_8BIT_MODE BIT(9)
#define LCD_RASTER_ORDER BIT(8)
#define LCD_TFT_MODE BIT(7)
-#define LCD_UNDERFLOW_INT_ENA BIT(6)
-#define LCD_PL_ENABLE BIT(4)
+#define LCD_V1_UNDERFLOW_INT_ENA BIT(6)
+#define LCD_V2_UNDERFLOW_INT_ENA BIT(5)
+#define LCD_V1_PL_INT_ENA BIT(4)
+#define LCD_V2_PL_INT_ENA BIT(6)
#define LCD_MONOCHROME_MODE BIT(1)
#define LCD_RASTER_ENABLE BIT(0)
#define LCD_TFT_ALT_ENABLE BIT(23)
#define LCD_STN_565_ENABLE BIT(24)
+#define LCD_V2_DMA_CLK_EN BIT(2)
+#define LCD_V2_LIDD_CLK_EN BIT(1)
+#define LCD_V2_CORE_CLK_EN BIT(0)
+#define LCD_V2_LPP_B10 26
/* LCD Raster Timing 2 Register */
#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16)
@@ -82,6 +93,7 @@
#define LCD_INVERT_FRAME_CLOCK BIT(20)
/* LCD Block */
+#define LCD_PID_REG 0x0
#define LCD_CTRL_REG 0x4
#define LCD_STAT_REG 0x8
#define LCD_RASTER_CTRL_REG 0x28
@@ -94,6 +106,17 @@
#define LCD_DMA_FRM_BUF_BASE_ADDR_1_REG 0x4C
#define LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG 0x50
+/* Interrupt Registers available only in Version 2 */
+#define LCD_RAW_STAT_REG 0x58
+#define LCD_MASKED_STAT_REG 0x5c
+#define LCD_INT_ENABLE_SET_REG 0x60
+#define LCD_INT_ENABLE_CLR_REG 0x64
+#define LCD_END_OF_INT_IND_REG 0x68
+
+/* Clock registers available only on Version 2 */
+#define LCD_CLK_ENABLE_REG 0x6c
+#define LCD_CLK_RESET_REG 0x70
+
#define LCD_NUM_BUFFERS 2
#define WSI_TIMEOUT 50
@@ -105,6 +128,8 @@
static resource_size_t da8xx_fb_reg_base;
static struct resource *lcdc_regs;
+static unsigned int lcd_revision;
+static irq_handler_t lcdc_irq_handler;
static inline unsigned int lcdc_read(unsigned int addr)
{
@@ -240,6 +265,7 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
u32 end;
u32 reg_ras;
u32 reg_dma;
+ u32 reg_int;
/* init reg to clear PLM (loading mode) fields */
reg_ras = lcdc_read(LCD_RASTER_CTRL_REG);
@@ -252,7 +278,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
end = par->dma_end;
reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY);
- reg_dma |= LCD_END_OF_FRAME_INT_ENA;
+ if (lcd_revision == LCD_VERSION_1) {
+ reg_dma |= LCD_V1_END_OF_FRAME_INT_ENA;
+ } else {
+ reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) |
+ LCD_V2_END_OF_FRAME0_INT_ENA |
+ LCD_V2_END_OF_FRAME1_INT_ENA;
+ lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG);
+ }
reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE;
lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
@@ -264,7 +297,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
end = start + par->palette_sz - 1;
reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY);
- reg_ras |= LCD_PL_ENABLE;
+
+ if (lcd_revision == LCD_VERSION_1) {
+ reg_ras |= LCD_V1_PL_INT_ENA;
+ } else {
+ reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) |
+ LCD_V2_PL_INT_ENA;
+ lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG);
+ }
lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
@@ -348,6 +388,7 @@ static void lcd_cfg_vertical_sync(int back_porch, int pulse_width,
static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
{
u32 reg;
+ u32 reg_int;
reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE |
LCD_MONO_8BIT_MODE |
@@ -375,7 +416,13 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
}
/* enable additional interrupts here */
- reg |= LCD_UNDERFLOW_INT_ENA;
+ if (lcd_revision == LCD_VERSION_1) {
+ reg |= LCD_V1_UNDERFLOW_INT_ENA;
+ } else {
+ reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) |
+ LCD_V2_UNDERFLOW_INT_ENA;
+ lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG);
+ }
lcdc_write(reg, LCD_RASTER_CTRL_REG);
@@ -413,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
/* Set the Panel Width */
/* Pixels per line = (PPL + 1)*16 */
- /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/
- width &= 0x3f0;
+ if (lcd_revision == LCD_VERSION_1) {
+ /*
+ * 0x3F in bits 4..9 gives max horizontal resolution = 1024
+ * pixels.
+ */
+ width &= 0x3f0;
+ } else {
+ /*
+ * 0x7F in bits 4..10 gives max horizontal resolution = 2048
+ * pixels.
+ */
+ width &= 0x7f0;
+ }
+
reg = lcdc_read(LCD_RASTER_TIMING_0_REG);
reg &= 0xfffffc00;
- reg |= ((width >> 4) - 1) << 4;
+ if (lcd_revision == LCD_VERSION_1) {
+ reg |= ((width >> 4) - 1) << 4;
+ } else {
+ width = (width >> 4) - 1;
+ reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3);
+ }
lcdc_write(reg, LCD_RASTER_TIMING_0_REG);
/* Set the Panel Height */
+ /* Set bits 9:0 of Lines Per Pixel */
reg = lcdc_read(LCD_RASTER_TIMING_1_REG);
reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00);
lcdc_write(reg, LCD_RASTER_TIMING_1_REG);
+ /* Set bit 10 of Lines Per Pixel */
+ if (lcd_revision == LCD_VERSION_2) {
+ reg = lcdc_read(LCD_RASTER_TIMING_2_REG);
+ reg |= ((height - 1) & 0x400) << 16;
+ lcdc_write(reg, LCD_RASTER_TIMING_2_REG);
+ }
+
/* Set the Raster Order of the Frame Buffer */
reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8);
if (raster_order)
@@ -511,6 +583,9 @@ static void lcd_reset(struct da8xx_fb_par *par)
/* DMA has to be disabled */
lcdc_write(0, LCD_DMA_CTRL_REG);
lcdc_write(0, LCD_RASTER_CTRL_REG);
+
+ if (lcd_revision == LCD_VERSION_2)
+ lcdc_write(0, LCD_INT_ENABLE_SET_REG);
}
static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
@@ -523,6 +598,11 @@ static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
/* Configure the LCD clock divisor. */
lcdc_write(LCD_CLK_DIVISOR(div) |
(LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
+
+ if (lcd_revision == LCD_VERSION_2)
+ lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN |
+ LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG);
+
}
static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
@@ -583,7 +663,63 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
return 0;
}
-static irqreturn_t lcdc_irq_handler(int irq, void *arg)
+/* IRQ handler for version 2 of LCDC */
+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();
+ lcdc_write(stat, LCD_MASKED_STAT_REG);
+ lcd_enable_raster();
+ } else if (stat & LCD_PL_LOAD_DONE) {
+ /*
+ * Must disable raster before changing state of any control bit.
+ * And also must be disabled before clearing the PL loading
+ * interrupt via the following write to the status register. If
+ * this is done after then one gets multiple PL done interrupts.
+ */
+ lcd_disable_raster();
+
+ 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);
+
+ /* Setup and start data loading mode */
+ lcd_blit(LOAD_DATA, par);
+ } else {
+ lcdc_write(stat, LCD_MASKED_STAT_REG);
+
+ if (stat & LCD_END_OF_FRAME0) {
+ 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);
+ par->vsync_flag = 1;
+ wake_up_interruptible(&par->vsync_wait);
+ }
+
+ if (stat & LCD_END_OF_FRAME1) {
+ 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);
+ par->vsync_flag = 1;
+ wake_up_interruptible(&par->vsync_wait);
+ }
+ }
+
+ lcdc_write(0, LCD_END_OF_INT_IND_REG);
+ return IRQ_HANDLED;
+}
+
+/* IRQ handler for version 1 LCDC */
+static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
{
struct da8xx_fb_par *par = arg;
u32 stat = lcdc_read(LCD_STAT_REG);
@@ -606,7 +742,7 @@ static irqreturn_t lcdc_irq_handler(int irq, void *arg)
/* Disable PL completion inerrupt */
reg_ras = lcdc_read(LCD_RASTER_CTRL_REG);
- reg_ras &= ~LCD_PL_ENABLE;
+ reg_ras &= ~LCD_V1_PL_INT_ENA;
lcdc_write(reg_ras, LCD_RASTER_CTRL_REG);
/* Setup and start data loading mode */
@@ -877,8 +1013,8 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
start = fix->smem_start +
new_var.yoffset * fix->line_length +
- new_var.xoffset * var->bits_per_pixel / 8;
- end = start + var->yres * fix->line_length - 1;
+ new_var.xoffset * fbi->var.bits_per_pixel / 8;
+ end = start + fbi->var.yres * fix->line_length - 1;
par->dma_start = start;
par->dma_end = end;
}
@@ -945,6 +1081,22 @@ static int __devinit fb_probe(struct platform_device *device)
if (ret)
goto err_clk_put;
+ /* Determine LCD IP Version */
+ switch (lcdc_read(LCD_PID_REG)) {
+ case 0x4C100102:
+ lcd_revision = LCD_VERSION_1;
+ break;
+ case 0x4F200800:
+ lcd_revision = LCD_VERSION_2;
+ break;
+ default:
+ dev_warn(&device->dev, "Unknown PID Reg value 0x%x, "
+ "defaulting to LCD revision 1\n",
+ lcdc_read(LCD_PID_REG));
+ lcd_revision = LCD_VERSION_1;
+ break;
+ }
+
for (i = 0, lcdc_info = known_lcd_panels;
i < ARRAY_SIZE(known_lcd_panels);
i++, lcdc_info++) {
@@ -1085,7 +1237,13 @@ static int __devinit fb_probe(struct platform_device *device)
}
#endif
- ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par);
+ if (lcd_revision == LCD_VERSION_1)
+ lcdc_irq_handler = lcdc_irq_handler_rev01;
+ else
+ lcdc_irq_handler = lcdc_irq_handler_rev02;
+
+ ret = request_irq(par->irq, lcdc_irq_handler, 0,
+ DRIVER_NAME, par);
if (ret)
goto irq_freq;
return 0;
diff --git a/drivers/video/fb-puv3.c b/drivers/video/fb-puv3.c
index 27f2c57e06e9..60a787fa32cf 100644
--- a/drivers/video/fb-puv3.c
+++ b/drivers/video/fb-puv3.c
@@ -624,8 +624,8 @@ static int unifb_pan_display(struct fb_var_screeninfo *var,
|| var->xoffset)
return -EINVAL;
} else {
- if (var->xoffset + var->xres > info->var.xres_virtual ||
- var->yoffset + var->yres > info->var.yres_virtual)
+ if (var->xoffset + info->var.xres > info->var.xres_virtual ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
}
info->var.xoffset = var->xoffset;
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 32814e8800e0..c27e153d8882 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -223,8 +223,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
int i;
BUG_ON(!fbdefio);
- cancel_delayed_work(&info->deferred_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&info->deferred_work);
/* clear out the mapping that we setup */
for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 5aac00eb1830..ad936295d8f4 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1738,8 +1738,6 @@ void fb_set_suspend(struct fb_info *info, int state)
{
struct fb_event event;
- if (!lock_fb_info(info))
- return;
event.info = info;
if (state) {
fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
@@ -1748,7 +1746,6 @@ void fb_set_suspend(struct fb_info *info, int state)
info->state = FBINFO_STATE_RUNNING;
fb_notifier_call_chain(FB_EVENT_RESUME, &event);
}
- unlock_fb_info(info);
}
/**
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 4f57485f8c54..cef65574db6c 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -493,7 +493,8 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
return num;
}
-static int get_std_timing(unsigned char *block, struct fb_videomode *mode)
+static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
+ int ver, int rev)
{
int xres, yres = 0, refresh, ratio, i;
@@ -504,7 +505,11 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode)
ratio = (block[1] & 0xc0) >> 6;
switch (ratio) {
case 0:
- yres = xres;
+ /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
+ if (ver < 1 || (ver == 1 && rev < 3))
+ yres = xres;
+ else
+ yres = (xres * 10)/16;
break;
case 1:
yres = (xres * 3)/4;
@@ -533,12 +538,12 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode)
}
static int get_dst_timing(unsigned char *block,
- struct fb_videomode *mode)
+ struct fb_videomode *mode, int ver, int rev)
{
int j, num = 0;
for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
- num += get_std_timing(block, &mode[num]);
+ num += get_std_timing(block, &mode[num], ver, rev);
return num;
}
@@ -599,6 +604,10 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
struct fb_videomode *mode, *m;
unsigned char *block;
int num = 0, i, first = 1;
+ int ver, rev;
+
+ ver = edid[EDID_STRUCT_VERSION];
+ rev = edid[EDID_STRUCT_REVISION];
mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
if (mode == NULL)
@@ -632,12 +641,12 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
DPRINTK(" Standard Timings\n");
block = edid + STD_TIMING_DESCRIPTIONS_START;
for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
- num += get_std_timing(block, &mode[num]);
+ num += get_std_timing(block, &mode[num], ver, rev);
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
- num += get_dst_timing(block + 5, &mode[num]);
+ num += get_dst_timing(block + 5, &mode[num], ver, rev);
}
/* Yikes, EDID data is totally useless */
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
index 04251ce89184..67afa9c2289d 100644
--- a/drivers/video/fbsysfs.c
+++ b/drivers/video/fbsysfs.c
@@ -399,9 +399,12 @@ static ssize_t store_fbstate(struct device *device,
state = simple_strtoul(buf, &last, 0);
+ if (!lock_fb_info(fb_info))
+ return -ENODEV;
console_lock();
fb_set_suspend(fb_info, (int)state);
console_unlock();
+ unlock_fb_info(fb_info);
return count;
}
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 0acc7d65aeaa..a16beeb5f548 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -30,37 +30,40 @@
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
-
-#include <linux/of_platform.h>
+#include <linux/spinlock.h>
#include <sysdev/fsl_soc.h>
#include <linux/fsl-diu-fb.h>
#include "edid.h"
-/*
- * These parameters give default parameters
- * for video output 1024x768,
- * FIXME - change timing to proper amounts
- * hsync 31.5kHz, vsync 60Hz
- */
-static struct fb_videomode __devinitdata fsl_diu_default_mode = {
- .refresh = 60,
- .xres = 1024,
- .yres = 768,
- .pixclock = 15385,
- .left_margin = 160,
- .right_margin = 24,
- .upper_margin = 29,
- .lower_margin = 3,
- .hsync_len = 136,
- .vsync_len = 6,
- .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- .vmode = FB_VMODE_NONINTERLACED
+#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */
+ /* 1 for plane 0, 2 for plane 1&2 each */
+
+/* HW cursor parameters */
+#define MAX_CURS 32
+
+/* INT_STATUS/INT_MASK field descriptions */
+#define INT_VSYNC 0x01 /* Vsync interrupt */
+#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */
+#define INT_UNDRUN 0x04 /* Under run exception interrupt */
+#define INT_PARERR 0x08 /* Display parameters error interrupt */
+#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */
+
+struct diu_addr {
+ void *vaddr; /* Virtual address */
+ dma_addr_t paddr; /* Physical address */
+ __u32 offset;
};
+/*
+ * List of supported video modes
+ *
+ * The first entry is the default video mode. The remain entries are in
+ * order if increasing resolution and frequency. The 320x240-60 mode is
+ * the initial AOI for the second and third planes.
+ */
static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
{
- .name = "1024x768-60",
.refresh = 60,
.xres = 1024,
.yres = 768,
@@ -75,7 +78,132 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "1024x768-70",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .pixclock = 79440,
+ .left_margin = 16,
+ .right_margin = 16,
+ .upper_margin = 16,
+ .lower_margin = 5,
+ .hsync_len = 48,
+ .vsync_len = 1,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 39722,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 33,
+ .lower_margin = 10,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 72,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 32052,
+ .left_margin = 128,
+ .right_margin = 24,
+ .upper_margin = 28,
+ .lower_margin = 9,
+ .hsync_len = 40,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 75,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 31747,
+ .left_margin = 120,
+ .right_margin = 16,
+ .upper_margin = 16,
+ .lower_margin = 1,
+ .hsync_len = 64,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 90,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 25057,
+ .left_margin = 120,
+ .right_margin = 32,
+ .upper_margin = 14,
+ .lower_margin = 25,
+ .hsync_len = 40,
+ .vsync_len = 14,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 100,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 22272,
+ .left_margin = 48,
+ .right_margin = 32,
+ .upper_margin = 17,
+ .lower_margin = 22,
+ .hsync_len = 128,
+ .vsync_len = 12,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 60,
+ .xres = 800,
+ .yres = 480,
+ .pixclock = 33805,
+ .left_margin = 96,
+ .right_margin = 24,
+ .upper_margin = 10,
+ .lower_margin = 3,
+ .hsync_len = 72,
+ .vsync_len = 7,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 60,
+ .xres = 800,
+ .yres = 600,
+ .pixclock = 25000,
+ .left_margin = 88,
+ .right_margin = 40,
+ .upper_margin = 23,
+ .lower_margin = 1,
+ .hsync_len = 128,
+ .vsync_len = 4,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 60,
+ .xres = 854,
+ .yres = 480,
+ .pixclock = 31518,
+ .left_margin = 104,
+ .right_margin = 16,
+ .upper_margin = 13,
+ .lower_margin = 1,
+ .hsync_len = 88,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
.refresh = 70,
.xres = 1024,
.yres = 768,
@@ -90,7 +218,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "1024x768-75",
.refresh = 75,
.xres = 1024,
.yres = 768,
@@ -105,7 +232,34 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "1280x1024-60",
+ .refresh = 60,
+ .xres = 1280,
+ .yres = 480,
+ .pixclock = 18939,
+ .left_margin = 353,
+ .right_margin = 47,
+ .upper_margin = 39,
+ .lower_margin = 4,
+ .hsync_len = 8,
+ .vsync_len = 2,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
+ .refresh = 60,
+ .xres = 1280,
+ .yres = 720,
+ .pixclock = 13426,
+ .left_margin = 192,
+ .right_margin = 64,
+ .upper_margin = 22,
+ .lower_margin = 1,
+ .hsync_len = 136,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
.refresh = 60,
.xres = 1280,
.yres = 1024,
@@ -120,7 +274,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "1280x1024-70",
.refresh = 70,
.xres = 1280,
.yres = 1024,
@@ -135,7 +288,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "1280x1024-75",
.refresh = 75,
.xres = 1280,
.yres = 1024,
@@ -150,40 +302,25 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
- .name = "320x240", /* for AOI only */
.refresh = 60,
- .xres = 320,
- .yres = 240,
- .pixclock = 15385,
- .left_margin = 0,
- .right_margin = 0,
- .upper_margin = 0,
- .lower_margin = 0,
- .hsync_len = 0,
- .vsync_len = 0,
- .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- .vmode = FB_VMODE_NONINTERLACED
- },
- {
- .name = "1280x480-60",
- .refresh = 60,
- .xres = 1280,
- .yres = 480,
- .pixclock = 18939,
- .left_margin = 353,
- .right_margin = 47,
- .upper_margin = 39,
- .lower_margin = 4,
- .hsync_len = 8,
- .vsync_len = 2,
+ .xres = 1920,
+ .yres = 1080,
+ .pixclock = 5787,
+ .left_margin = 328,
+ .right_margin = 120,
+ .upper_margin = 34,
+ .lower_margin = 1,
+ .hsync_len = 208,
+ .vsync_len = 3,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
},
};
-static char *fb_mode = "1024x768-32@60";
+static char *fb_mode;
static unsigned long default_bpp = 32;
-static int monitor_port;
+static enum fsl_diu_monitor_port monitor_port;
+static char *monitor_string;
#if defined(CONFIG_NOT_COHERENT_CACHE)
static u8 *coherence_data;
@@ -201,15 +338,27 @@ struct fsl_diu_data {
void *dummy_aoi_virt;
unsigned int irq;
int fb_enabled;
- int monitor_port;
+ enum fsl_diu_monitor_port monitor_port;
+ struct diu __iomem *diu_reg;
+ spinlock_t reg_lock;
+ struct diu_addr ad;
+ struct diu_addr gamma;
+ struct diu_addr pallete;
+ struct diu_addr cursor;
+};
+
+enum mfb_index {
+ PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */
+ PLANE1_AOI0, /* Plane 1, first AOI */
+ PLANE1_AOI1, /* Plane 1, second AOI */
+ PLANE2_AOI0, /* Plane 2, first AOI */
+ PLANE2_AOI1, /* Plane 2, second AOI */
};
struct mfb_info {
- int index;
- int type;
+ enum mfb_index index;
char *id;
int registered;
- int blank;
unsigned long pseudo_palette[16];
struct diu_ad *ad;
int cursor_reset;
@@ -223,63 +372,82 @@ struct mfb_info {
static struct mfb_info mfb_template[] = {
- { /* AOI 0 for plane 0 */
- .index = 0,
- .type = MFB_TYPE_OUTPUT,
- .id = "Panel0",
- .registered = 0,
- .count = 0,
- .x_aoi_d = 0,
- .y_aoi_d = 0,
+ {
+ .index = PLANE0,
+ .id = "Panel0",
+ .registered = 0,
+ .count = 0,
+ .x_aoi_d = 0,
+ .y_aoi_d = 0,
},
- { /* AOI 0 for plane 1 */
- .index = 1,
- .type = MFB_TYPE_OUTPUT,
- .id = "Panel1 AOI0",
- .registered = 0,
- .g_alpha = 0xff,
- .count = 0,
- .x_aoi_d = 0,
- .y_aoi_d = 0,
+ {
+ .index = PLANE1_AOI0,
+ .id = "Panel1 AOI0",
+ .registered = 0,
+ .g_alpha = 0xff,
+ .count = 0,
+ .x_aoi_d = 0,
+ .y_aoi_d = 0,
},
- { /* AOI 1 for plane 1 */
- .index = 2,
- .type = MFB_TYPE_OUTPUT,
- .id = "Panel1 AOI1",
- .registered = 0,
- .g_alpha = 0xff,
- .count = 0,
- .x_aoi_d = 0,
- .y_aoi_d = 480,
+ {
+ .index = PLANE1_AOI1,
+ .id = "Panel1 AOI1",
+ .registered = 0,
+ .g_alpha = 0xff,
+ .count = 0,
+ .x_aoi_d = 0,
+ .y_aoi_d = 480,
},
- { /* AOI 0 for plane 2 */
- .index = 3,
- .type = MFB_TYPE_OUTPUT,
- .id = "Panel2 AOI0",
- .registered = 0,
- .g_alpha = 0xff,
- .count = 0,
- .x_aoi_d = 640,
- .y_aoi_d = 0,
+ {
+ .index = PLANE2_AOI0,
+ .id = "Panel2 AOI0",
+ .registered = 0,
+ .g_alpha = 0xff,
+ .count = 0,
+ .x_aoi_d = 640,
+ .y_aoi_d = 0,
},
- { /* AOI 1 for plane 2 */
- .index = 4,
- .type = MFB_TYPE_OUTPUT,
- .id = "Panel2 AOI1",
- .registered = 0,
- .g_alpha = 0xff,
- .count = 0,
- .x_aoi_d = 640,
- .y_aoi_d = 480,
+ {
+ .index = PLANE2_AOI1,
+ .id = "Panel2 AOI1",
+ .registered = 0,
+ .g_alpha = 0xff,
+ .count = 0,
+ .x_aoi_d = 640,
+ .y_aoi_d = 480,
},
};
-static struct diu_hw dr = {
- .mode = MFB_MODE1,
- .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock),
-};
+/**
+ * fsl_diu_name_to_port - convert a port name to a monitor port enum
+ *
+ * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns
+ * the enum fsl_diu_monitor_port that corresponds to that string.
+ *
+ * For compatibility with older versions, a number ("0", "1", or "2") is also
+ * supported.
+ *
+ * If the string is unknown, DVI is assumed.
+ *
+ * If the particular port is not supported by the platform, another port
+ * (platform-specific) is chosen instead.
+ */
+static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
+{
+ enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;
+ unsigned long val;
-static struct diu_pool pool;
+ if (s) {
+ if (!strict_strtoul(s, 10, &val) && (val <= 2))
+ port = (enum fsl_diu_monitor_port) val;
+ else if (strncmp(s, "lvds", 4) == 0)
+ port = FSL_DIU_PORT_LVDS;
+ else if (strncmp(s, "dlvds", 5) == 0)
+ port = FSL_DIU_PORT_DLVDS;
+ }
+
+ return diu_ops.valid_monitor_port(port);
+}
/**
* fsl_diu_alloc - allocate memory for the DIU
@@ -292,14 +460,9 @@ static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
{
void *virt;
- pr_debug("size=%zu\n", size);
-
virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO);
- if (virt) {
+ if (virt)
*phys = virt_to_phys(virt);
- pr_debug("virt=%p phys=%llx\n", virt,
- (unsigned long long)*phys);
- }
return virt;
}
@@ -313,8 +476,6 @@ static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
*/
static void fsl_diu_free(void *virt, size_t size)
{
- pr_debug("virt=%p size=%zu\n", virt, size);
-
if (virt && size)
free_pages_exact(virt, size);
}
@@ -330,82 +491,72 @@ void wr_reg_wa(u32 *reg, u32 val)
} while (in_be32(reg) != val);
}
-static int fsl_diu_enable_panel(struct fb_info *info)
+static void fsl_diu_enable_panel(struct fb_info *info)
{
struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
- struct diu *hw = dr.diu_reg;
struct diu_ad *ad = mfbi->ad;
struct fsl_diu_data *machine_data = mfbi->parent;
- int res = 0;
+ struct diu __iomem *hw = machine_data->diu_reg;
- pr_debug("enable_panel index %d\n", mfbi->index);
- if (mfbi->type != MFB_TYPE_OFF) {
- switch (mfbi->index) {
- case 0: /* plane 0 */
- if (hw->desc[0] != ad->paddr)
- wr_reg_wa(&hw->desc[0], ad->paddr);
- break;
- case 1: /* plane 1 AOI 0 */
- cmfbi = machine_data->fsl_diu_info[2]->par;
- if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
- if (cmfbi->count > 0) /* AOI1 open */
- ad->next_ad =
- cpu_to_le32(cmfbi->ad->paddr);
- else
- ad->next_ad = 0;
- wr_reg_wa(&hw->desc[1], ad->paddr);
- }
- break;
- case 3: /* plane 2 AOI 0 */
- cmfbi = machine_data->fsl_diu_info[4]->par;
- if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
- if (cmfbi->count > 0) /* AOI1 open */
- ad->next_ad =
- cpu_to_le32(cmfbi->ad->paddr);
- else
- ad->next_ad = 0;
- wr_reg_wa(&hw->desc[2], ad->paddr);
- }
- break;
- case 2: /* plane 1 AOI 1 */
- pmfbi = machine_data->fsl_diu_info[1]->par;
- ad->next_ad = 0;
- if (hw->desc[1] == machine_data->dummy_ad->paddr)
- wr_reg_wa(&hw->desc[1], ad->paddr);
- else /* AOI0 open */
- pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
- break;
- case 4: /* plane 2 AOI 1 */
- pmfbi = machine_data->fsl_diu_info[3]->par;
- ad->next_ad = 0;
- if (hw->desc[2] == machine_data->dummy_ad->paddr)
- wr_reg_wa(&hw->desc[2], ad->paddr);
- else /* AOI0 was open */
- pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
- break;
- default:
- res = -EINVAL;
- break;
+ switch (mfbi->index) {
+ case PLANE0:
+ if (hw->desc[0] != ad->paddr)
+ wr_reg_wa(&hw->desc[0], ad->paddr);
+ break;
+ case PLANE1_AOI0:
+ cmfbi = machine_data->fsl_diu_info[2]->par;
+ if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
+ if (cmfbi->count > 0) /* AOI1 open */
+ ad->next_ad =
+ cpu_to_le32(cmfbi->ad->paddr);
+ else
+ ad->next_ad = 0;
+ wr_reg_wa(&hw->desc[1], ad->paddr);
}
- } else
- res = -EINVAL;
- return res;
+ break;
+ case PLANE2_AOI0:
+ cmfbi = machine_data->fsl_diu_info[4]->par;
+ if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
+ if (cmfbi->count > 0) /* AOI1 open */
+ ad->next_ad =
+ cpu_to_le32(cmfbi->ad->paddr);
+ else
+ ad->next_ad = 0;
+ wr_reg_wa(&hw->desc[2], ad->paddr);
+ }
+ break;
+ case PLANE1_AOI1:
+ pmfbi = machine_data->fsl_diu_info[1]->par;
+ ad->next_ad = 0;
+ if (hw->desc[1] == machine_data->dummy_ad->paddr)
+ wr_reg_wa(&hw->desc[1], ad->paddr);
+ else /* AOI0 open */
+ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
+ break;
+ case PLANE2_AOI1:
+ pmfbi = machine_data->fsl_diu_info[3]->par;
+ ad->next_ad = 0;
+ if (hw->desc[2] == machine_data->dummy_ad->paddr)
+ wr_reg_wa(&hw->desc[2], ad->paddr);
+ else /* AOI0 was open */
+ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
+ break;
+ }
}
-static int fsl_diu_disable_panel(struct fb_info *info)
+static void fsl_diu_disable_panel(struct fb_info *info)
{
struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
- struct diu *hw = dr.diu_reg;
struct diu_ad *ad = mfbi->ad;
struct fsl_diu_data *machine_data = mfbi->parent;
- int res = 0;
+ struct diu __iomem *hw = machine_data->diu_reg;
switch (mfbi->index) {
- case 0: /* plane 0 */
+ case PLANE0:
if (hw->desc[0] != machine_data->dummy_ad->paddr)
wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr);
break;
- case 1: /* plane 1 AOI 0 */
+ case PLANE1_AOI0:
cmfbi = machine_data->fsl_diu_info[2]->par;
if (cmfbi->count > 0) /* AOI1 is open */
wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
@@ -414,7 +565,7 @@ static int fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
/* close AOI 0 */
break;
- case 3: /* plane 2 AOI 0 */
+ case PLANE2_AOI0:
cmfbi = machine_data->fsl_diu_info[4]->par;
if (cmfbi->count > 0) /* AOI1 is open */
wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
@@ -423,7 +574,7 @@ static int fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
/* close AOI 0 */
break;
- case 2: /* plane 1 AOI 1 */
+ case PLANE1_AOI1:
pmfbi = machine_data->fsl_diu_info[1]->par;
if (hw->desc[1] != ad->paddr) {
/* AOI1 is not the first in the chain */
@@ -434,7 +585,7 @@ static int fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
/* close AOI 1 */
break;
- case 4: /* plane 2 AOI 1 */
+ case PLANE2_AOI1:
pmfbi = machine_data->fsl_diu_info[3]->par;
if (hw->desc[2] != ad->paddr) {
/* AOI1 is not the first in the chain */
@@ -445,31 +596,26 @@ static int fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
/* close AOI 1 */
break;
- default:
- res = -EINVAL;
- break;
}
-
- return res;
}
static void enable_lcdc(struct fb_info *info)
{
- struct diu *hw = dr.diu_reg;
struct mfb_info *mfbi = info->par;
struct fsl_diu_data *machine_data = mfbi->parent;
+ struct diu __iomem *hw = machine_data->diu_reg;
if (!machine_data->fb_enabled) {
- out_be32(&hw->diu_mode, dr.mode);
+ out_be32(&hw->diu_mode, MFB_MODE1);
machine_data->fb_enabled++;
}
}
static void disable_lcdc(struct fb_info *info)
{
- struct diu *hw = dr.diu_reg;
struct mfb_info *mfbi = info->par;
struct fsl_diu_data *machine_data = mfbi->parent;
+ struct diu __iomem *hw = machine_data->diu_reg;
if (machine_data->fb_enabled) {
out_be32(&hw->diu_mode, 0);
@@ -482,7 +628,8 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
{
struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
struct fsl_diu_data *machine_data = mfbi->parent;
- int available_height, upper_aoi_bottom, index = mfbi->index;
+ int available_height, upper_aoi_bottom;
+ enum mfb_index index = mfbi->index;
int lower_aoi_is_open, upper_aoi_is_open;
__u32 base_plane_width, base_plane_height, upper_aoi_height;
@@ -494,14 +641,14 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
if (mfbi->y_aoi_d < 0)
mfbi->y_aoi_d = 0;
switch (index) {
- case 0:
+ case PLANE0:
if (mfbi->x_aoi_d != 0)
mfbi->x_aoi_d = 0;
if (mfbi->y_aoi_d != 0)
mfbi->y_aoi_d = 0;
break;
- case 1: /* AOI 0 */
- case 3:
+ case PLANE1_AOI0:
+ case PLANE2_AOI0:
lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par;
lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
if (var->xres > base_plane_width)
@@ -518,8 +665,8 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
if ((mfbi->y_aoi_d + var->yres) > available_height)
mfbi->y_aoi_d = available_height - var->yres;
break;
- case 2: /* AOI 1 */
- case 4:
+ case PLANE1_AOI1:
+ case PLANE2_AOI1:
upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par;
upper_aoi_height =
machine_data->fsl_diu_info[index-1]->var.yres;
@@ -555,9 +702,6 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
static int fsl_diu_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
- pr_debug("check_var xres: %d\n", var->xres);
- pr_debug("check_var yres: %d\n", var->yres);
-
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
@@ -652,7 +796,7 @@ static void set_fix(struct fb_info *info)
struct fb_var_screeninfo *var = &info->var;
struct mfb_info *mfbi = info->par;
- strncpy(fix->id, mfbi->id, strlen(mfbi->id));
+ strncpy(fix->id, mfbi->id, sizeof(fix->id));
fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
fix->type = FB_TYPE_PACKED_PIXELS;
fix->accel = FB_ACCEL_NONE;
@@ -666,45 +810,37 @@ static void update_lcdc(struct fb_info *info)
struct fb_var_screeninfo *var = &info->var;
struct mfb_info *mfbi = info->par;
struct fsl_diu_data *machine_data = mfbi->parent;
- struct diu *hw;
+ struct diu __iomem *hw;
int i, j;
char __iomem *cursor_base, *gamma_table_base;
u32 temp;
- hw = dr.diu_reg;
-
- if (mfbi->type == MFB_TYPE_OFF) {
- fsl_diu_disable_panel(info);
- return;
- }
+ hw = machine_data->diu_reg;
diu_ops.set_monitor_port(machine_data->monitor_port);
- gamma_table_base = pool.gamma.vaddr;
- cursor_base = pool.cursor.vaddr;
+ gamma_table_base = machine_data->gamma.vaddr;
+ cursor_base = machine_data->cursor.vaddr;
/* Prep for DIU init - gamma table, cursor table */
for (i = 0; i <= 2; i++)
- for (j = 0; j <= 255; j++)
- *gamma_table_base++ = j;
+ for (j = 0; j <= 255; j++)
+ *gamma_table_base++ = j;
- diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr);
+ diu_ops.set_gamma_table(machine_data->monitor_port,
+ machine_data->gamma.vaddr);
- pr_debug("update-lcdc: HW - %p\n Disabling DIU\n", hw);
disable_lcdc(info);
/* Program DIU registers */
- out_be32(&hw->gamma, pool.gamma.paddr);
- out_be32(&hw->cursor, pool.cursor.paddr);
+ out_be32(&hw->gamma, machine_data->gamma.paddr);
+ out_be32(&hw->cursor, machine_data->cursor.paddr);
out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */
out_be32(&hw->bgnd_wb, 0); /* BGND_WB */
out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
/* DISP SIZE */
- pr_debug("DIU xres: %d\n", var->xres);
- pr_debug("DIU yres: %d\n", var->yres);
-
out_be32(&hw->wb_size, 0); /* WB SIZE */
out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
@@ -721,15 +857,6 @@ static void update_lcdc(struct fb_info *info)
out_be32(&hw->vsyn_para, temp);
- pr_debug("DIU right_margin - %d\n", var->right_margin);
- pr_debug("DIU left_margin - %d\n", var->left_margin);
- pr_debug("DIU hsync_len - %d\n", var->hsync_len);
- pr_debug("DIU upper_margin - %d\n", var->upper_margin);
- pr_debug("DIU lower_margin - %d\n", var->lower_margin);
- pr_debug("DIU vsync_len - %d\n", var->vsync_len);
- pr_debug("DIU HSYNC - 0x%08x\n", hw->hsyn_para);
- pr_debug("DIU VSYNC - 0x%08x\n", hw->vsyn_para);
-
diu_ops.set_pixel_clock(var->pixclock);
out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */
@@ -746,14 +873,9 @@ static int map_video_memory(struct fb_info *info)
phys_addr_t phys;
u32 smem_len = info->fix.line_length * info->var.yres_virtual;
- pr_debug("info->var.xres_virtual = %d\n", info->var.xres_virtual);
- pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
- pr_debug("info->fix.line_length = %d\n", info->fix.line_length);
- pr_debug("MAP_VIDEO_MEMORY: smem_len = %u\n", smem_len);
-
info->screen_base = fsl_diu_alloc(smem_len, &phys);
if (info->screen_base == NULL) {
- printk(KERN_ERR "Unable to allocate fb memory\n");
+ dev_err(info->dev, "unable to allocate fb memory\n");
return -ENOMEM;
}
mutex_lock(&info->mm_lock);
@@ -762,10 +884,6 @@ static int map_video_memory(struct fb_info *info)
mutex_unlock(&info->mm_lock);
info->screen_size = info->fix.smem_len;
- pr_debug("Allocated fb @ paddr=0x%08lx, size=%d.\n",
- info->fix.smem_start, info->fix.smem_len);
- pr_debug("screen base %p\n", info->screen_base);
-
return 0;
}
@@ -810,9 +928,9 @@ static int fsl_diu_set_par(struct fb_info *info)
struct mfb_info *mfbi = info->par;
struct fsl_diu_data *machine_data = mfbi->parent;
struct diu_ad *ad = mfbi->ad;
- struct diu *hw;
+ struct diu __iomem *hw;
- hw = dr.diu_reg;
+ hw = machine_data->diu_reg;
set_fix(info);
mfbi->cursor_reset = 1;
@@ -822,18 +940,16 @@ static int fsl_diu_set_par(struct fb_info *info)
if (len != info->fix.smem_len) {
if (info->fix.smem_start)
unmap_video_memory(info);
- pr_debug("SET PAR: smem_len = %d\n", info->fix.smem_len);
/* Memory allocation for framebuffer */
if (map_video_memory(info)) {
- printk(KERN_ERR "Unable to allocate fb memory 1\n");
+ dev_err(info->dev, "unable to allocate fb memory 1\n");
return -ENOMEM;
}
}
- ad->pix_fmt =
- diu_ops.get_pixel_format(var->bits_per_pixel,
- machine_data->monitor_port);
+ ad->pix_fmt = diu_ops.get_pixel_format(machine_data->monitor_port,
+ var->bits_per_pixel);
ad->addr = cpu_to_le32(info->fix.smem_start);
ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
var->xres_virtual) | mfbi->g_alpha;
@@ -851,14 +967,14 @@ static int fsl_diu_set_par(struct fb_info *info)
ad->ckmin_g = 255;
ad->ckmin_b = 255;
- if (mfbi->index == 0)
+ if (mfbi->index == PLANE0)
update_lcdc(info);
return 0;
}
static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
{
- return ((val<<width) + 0x7FFF - val)>>16;
+ return ((val << width) + 0x7FFF - val) >> 16;
}
/*
@@ -870,8 +986,9 @@ static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
* pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
* color palette.
*/
-static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp, struct fb_info *info)
+static int fsl_diu_setcolreg(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ unsigned int transp, struct fb_info *info)
{
int ret = 1;
@@ -906,9 +1023,6 @@ static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green,
ret = 0;
}
break;
- case FB_VISUAL_STATIC_PSEUDOCOLOR:
- case FB_VISUAL_PSEUDOCOLOR:
- break;
}
return ret;
@@ -944,37 +1058,6 @@ static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
return 0;
}
-/*
- * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking
- * succeeded, != 0 if un-/blanking failed.
- * blank_mode == 2: suspend vsync
- * blank_mode == 3: suspend hsync
- * blank_mode == 4: powerdown
- */
-static int fsl_diu_blank(int blank_mode, struct fb_info *info)
-{
- struct mfb_info *mfbi = info->par;
-
- mfbi->blank = blank_mode;
-
- switch (blank_mode) {
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_HSYNC_SUSPEND:
- /* FIXME: fixes to enable_panel and enable lcdc needed */
- case FB_BLANK_NORMAL:
- /* fsl_diu_disable_panel(info);*/
- break;
- case FB_BLANK_POWERDOWN:
- /* disable_lcdc(info); */
- break;
- case FB_BLANK_UNBLANK:
- /* fsl_diu_enable_panel(info);*/
- break;
- }
-
- return 0;
-}
-
static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -989,25 +1072,29 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
if (!arg)
return -EINVAL;
switch (cmd) {
+ case MFB_SET_PIXFMT_OLD:
+ dev_warn(info->dev,
+ "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",
+ MFB_SET_PIXFMT_OLD);
case MFB_SET_PIXFMT:
if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
return -EFAULT;
ad->pix_fmt = pix_fmt;
- pr_debug("Set pixel format to 0x%08x\n", ad->pix_fmt);
break;
+ case MFB_GET_PIXFMT_OLD:
+ dev_warn(info->dev,
+ "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",
+ MFB_GET_PIXFMT_OLD);
case MFB_GET_PIXFMT:
pix_fmt = ad->pix_fmt;
if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
return -EFAULT;
- pr_debug("get pixel format 0x%08x\n", ad->pix_fmt);
break;
case MFB_SET_AOID:
if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
return -EFAULT;
mfbi->x_aoi_d = aoi_d.x_aoi_d;
mfbi->y_aoi_d = aoi_d.y_aoi_d;
- pr_debug("set AOI display offset of index %d to (%d,%d)\n",
- mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
fsl_diu_check_var(&info->var, info);
fsl_diu_set_aoi(info);
break;
@@ -1016,14 +1103,11 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
aoi_d.y_aoi_d = mfbi->y_aoi_d;
if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
return -EFAULT;
- pr_debug("get AOI display offset of index %d (%d,%d)\n",
- mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
break;
case MFB_GET_ALPHA:
global_alpha = mfbi->g_alpha;
if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
return -EFAULT;
- pr_debug("get global alpha of index %d\n", mfbi->index);
break;
case MFB_SET_ALPHA:
/* set panel information */
@@ -1032,7 +1116,6 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
(global_alpha & 0xff);
mfbi->g_alpha = global_alpha;
- pr_debug("set global alpha for index %d\n", mfbi->index);
break;
case MFB_SET_CHROMA_KEY:
/* set panel winformation */
@@ -1060,27 +1143,9 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
ad->ckmin_g = ck.green_min;
ad->ckmin_b = ck.blue_min;
}
- pr_debug("set chroma key\n");
break;
- case FBIOGET_GWINFO:
- if (mfbi->type == MFB_TYPE_OFF)
- return -ENODEV;
- /* get graphic window information */
- if (copy_to_user(buf, ad, sizeof(*ad)))
- return -EFAULT;
- break;
- case FBIOGET_HWCINFO:
- pr_debug("FBIOGET_HWCINFO:0x%08x\n", FBIOGET_HWCINFO);
- break;
- case FBIOPUT_MODEINFO:
- pr_debug("FBIOPUT_MODEINFO:0x%08x\n", FBIOPUT_MODEINFO);
- break;
- case FBIOGET_DISPINFO:
- pr_debug("FBIOGET_DISPINFO:0x%08x\n", FBIOGET_DISPINFO);
- break;
-
default:
- printk(KERN_ERR "Unknown ioctl command (0x%08X)\n", cmd);
+ dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
return -ENOIOCTLCMD;
}
@@ -1095,22 +1160,18 @@ static int fsl_diu_open(struct fb_info *info, int user)
int res = 0;
/* free boot splash memory on first /dev/fb0 open */
- if (!mfbi->index && diu_ops.release_bootmem)
+ if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)
diu_ops.release_bootmem();
spin_lock(&diu_lock);
mfbi->count++;
if (mfbi->count == 1) {
- pr_debug("open plane index %d\n", mfbi->index);
fsl_diu_check_var(&info->var, info);
res = fsl_diu_set_par(info);
if (res < 0)
mfbi->count--;
- else {
- res = fsl_diu_enable_panel(info);
- if (res < 0)
- mfbi->count--;
- }
+ else
+ fsl_diu_enable_panel(info);
}
spin_unlock(&diu_lock);
@@ -1126,12 +1187,9 @@ static int fsl_diu_release(struct fb_info *info, int user)
spin_lock(&diu_lock);
mfbi->count--;
- if (mfbi->count == 0) {
- pr_debug("release plane index %d\n", mfbi->index);
- res = fsl_diu_disable_panel(info);
- if (res < 0)
- mfbi->count++;
- }
+ if (mfbi->count == 0)
+ fsl_diu_disable_panel(info);
+
spin_unlock(&diu_lock);
return res;
}
@@ -1141,7 +1199,6 @@ static struct fb_ops fsl_diu_ops = {
.fb_check_var = fsl_diu_check_var,
.fb_set_par = fsl_diu_set_par,
.fb_setcolreg = fsl_diu_setcolreg,
- .fb_blank = fsl_diu_blank,
.fb_pan_display = fsl_diu_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
@@ -1178,7 +1235,7 @@ static int __devinit install_fb(struct fb_info *info)
if (init_fbinfo(info))
return -EINVAL;
- if (mfbi->index == 0) { /* plane 0 */
+ if (mfbi->index == PLANE0) {
if (mfbi->edid_data) {
/* Now build modedb from EDID */
fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs);
@@ -1192,43 +1249,23 @@ static int __devinit install_fb(struct fb_info *info)
} else {
aoi_mode = init_aoi_mode;
}
- pr_debug("mode used = %s\n", aoi_mode);
- rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize,
- &fsl_diu_default_mode, default_bpp);
- switch (rc) {
- case 1:
- pr_debug("using mode specified in @mode\n");
- break;
- case 2:
- pr_debug("using mode specified in @mode "
- "with ignored refresh rate\n");
- break;
- case 3:
- pr_debug("using mode default mode\n");
- break;
- case 4:
- pr_debug("using mode from list\n");
- break;
- default:
- pr_debug("rc = %d\n", rc);
- pr_debug("failed to find mode\n");
+ rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,
+ default_bpp);
+ if (!rc) {
/*
* For plane 0 we continue and look into
* driver's internal modedb.
*/
- if (mfbi->index == 0 && mfbi->edid_data)
+ if ((mfbi->index == PLANE0) && mfbi->edid_data)
has_default_mode = 0;
else
return -EINVAL;
- break;
}
if (!has_default_mode) {
rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
- ARRAY_SIZE(fsl_diu_mode_db),
- &fsl_diu_default_mode,
- default_bpp);
- if (rc > 0 && rc < 5)
+ ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);
+ if (rc)
has_default_mode = 1;
}
@@ -1256,33 +1293,22 @@ static int __devinit install_fb(struct fb_info *info)
fb_videomode_to_var(&info->var, modedb);
}
- pr_debug("xres_virtual %d\n", info->var.xres_virtual);
- pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel);
-
- pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
- pr_debug("info->fix.line_length = %d\n", info->fix.line_length);
-
- if (mfbi->type == MFB_TYPE_OFF)
- mfbi->blank = FB_BLANK_NORMAL;
- else
- mfbi->blank = FB_BLANK_UNBLANK;
-
if (fsl_diu_check_var(&info->var, info)) {
- printk(KERN_ERR "fb_check_var failed");
+ dev_err(info->dev, "fsl_diu_check_var failed\n");
+ unmap_video_memory(info);
fb_dealloc_cmap(&info->cmap);
return -EINVAL;
}
if (register_framebuffer(info) < 0) {
- printk(KERN_ERR "register_framebuffer failed");
+ dev_err(info->dev, "register_framebuffer failed\n");
unmap_video_memory(info);
fb_dealloc_cmap(&info->cmap);
return -EINVAL;
}
mfbi->registered = 1;
- printk(KERN_INFO "fb%d: %s fb device registered successfully.\n",
- info->node, info->fix.id);
+ dev_info(info->dev, "%s registered successfully\n", mfbi->id);
return 0;
}
@@ -1294,7 +1320,7 @@ static void uninstall_fb(struct fb_info *info)
if (!mfbi->registered)
return;
- if (mfbi->index == 0)
+ if (mfbi->index == PLANE0)
kfree(mfbi->edid_data);
unregister_framebuffer(info);
@@ -1307,20 +1333,20 @@ static void uninstall_fb(struct fb_info *info)
static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
{
- struct diu *hw = dr.diu_reg;
+ struct diu __iomem *hw = dev_id;
unsigned int status = in_be32(&hw->int_status);
if (status) {
/* This is the workaround for underrun */
if (status & INT_UNDRUN) {
out_be32(&hw->diu_mode, 0);
- pr_debug("Err: DIU occurs underrun!\n");
udelay(1);
out_be32(&hw->diu_mode, 1);
}
#if defined(CONFIG_NOT_COHERENT_CACHE)
else if (status & INT_VSYNC) {
unsigned int i;
+
for (i = 0; i < coherence_data_size;
i += d_cache_line_size)
__asm__ __volatile__ (
@@ -1333,43 +1359,38 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
return IRQ_NONE;
}
-static int request_irq_local(int irq)
+static int request_irq_local(struct fsl_diu_data *machine_data)
{
- unsigned long status, ints;
- struct diu *hw;
+ struct diu __iomem *hw = machine_data->diu_reg;
+ u32 ints;
int ret;
- hw = dr.diu_reg;
-
/* Read to clear the status */
- status = in_be32(&hw->int_status);
+ in_be32(&hw->int_status);
- ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL);
- if (ret)
- pr_info("Request diu IRQ failed.\n");
- else {
+ ret = request_irq(machine_data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw);
+ if (!ret) {
ints = INT_PARERR | INT_LS_BF_VS;
#if !defined(CONFIG_NOT_COHERENT_CACHE)
ints |= INT_VSYNC;
#endif
- if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3)
- ints |= INT_VSYNC_WB;
/* Read to clear the status */
- status = in_be32(&hw->int_status);
+ in_be32(&hw->int_status);
out_be32(&hw->int_mask, ints);
}
+
return ret;
}
-static void free_irq_local(int irq)
+static void free_irq_local(struct fsl_diu_data *machine_data)
{
- struct diu *hw = dr.diu_reg;
+ struct diu __iomem *hw = machine_data->diu_reg;
/* Disable all LCDC interrupt */
out_be32(&hw->int_mask, 0x1f);
- free_irq(irq, NULL);
+ free_irq(machine_data->irq, NULL);
}
#ifdef CONFIG_PM
@@ -1406,49 +1427,42 @@ static int fsl_diu_resume(struct platform_device *ofdev)
static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
u32 bytes_align)
{
- u32 offset, ssize;
- u32 mask;
- dma_addr_t paddr = 0;
+ u32 offset;
+ dma_addr_t mask;
- ssize = size + bytes_align;
- buf->vaddr = dma_alloc_coherent(dev, ssize, &paddr, GFP_DMA |
- __GFP_ZERO);
+ buf->vaddr =
+ dma_alloc_coherent(dev, size + bytes_align, &buf->paddr,
+ GFP_DMA | __GFP_ZERO);
if (!buf->vaddr)
return -ENOMEM;
- buf->paddr = (__u32) paddr;
-
mask = bytes_align - 1;
- offset = (u32)buf->paddr & mask;
+ offset = buf->paddr & mask;
if (offset) {
buf->offset = bytes_align - offset;
- buf->paddr = (u32)buf->paddr + offset;
+ buf->paddr = buf->paddr + offset;
} else
buf->offset = 0;
+
return 0;
}
static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
u32 bytes_align)
{
- dma_free_coherent(dev, size + bytes_align,
- buf->vaddr, (buf->paddr - buf->offset));
- return;
+ dma_free_coherent(dev, size + bytes_align, buf->vaddr,
+ buf->paddr - buf->offset);
}
static ssize_t store_monitor(struct device *device,
struct device_attribute *attr, const char *buf, size_t count)
{
- int old_monitor_port;
- unsigned long val;
+ enum fsl_diu_monitor_port old_monitor_port;
struct fsl_diu_data *machine_data =
container_of(attr, struct fsl_diu_data, dev_attr);
- if (strict_strtoul(buf, 10, &val))
- return 0;
-
old_monitor_port = machine_data->monitor_port;
- machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val);
+ machine_data->monitor_port = fsl_diu_name_to_port(buf);
if (old_monitor_port != machine_data->monitor_port) {
/* All AOIs need adjust pixel format
@@ -1468,16 +1482,25 @@ static ssize_t show_monitor(struct device *device,
{
struct fsl_diu_data *machine_data =
container_of(attr, struct fsl_diu_data, dev_attr);
- return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
+
+ switch (machine_data->monitor_port) {
+ case FSL_DIU_PORT_DVI:
+ return sprintf(buf, "DVI\n");
+ case FSL_DIU_PORT_LVDS:
+ return sprintf(buf, "Single-link LVDS\n");
+ case FSL_DIU_PORT_DLVDS:
+ return sprintf(buf, "Dual-link LVDS\n");
+ }
+
+ return 0;
}
-static int __devinit fsl_diu_probe(struct platform_device *ofdev)
+static int __devinit fsl_diu_probe(struct platform_device *pdev)
{
- struct device_node *np = ofdev->dev.of_node;
+ struct device_node *np = pdev->dev.of_node;
struct mfb_info *mfbi;
- phys_addr_t dummy_ad_addr;
+ phys_addr_t dummy_ad_addr = 0;
int ret, i, error = 0;
- struct resource res;
struct fsl_diu_data *machine_data;
int diu_mode;
@@ -1485,11 +1508,13 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev)
if (!machine_data)
return -ENOMEM;
+ spin_lock_init(&machine_data->reg_lock);
+
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
machine_data->fsl_diu_info[i] =
- framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev);
+ framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
if (!machine_data->fsl_diu_info[i]) {
- dev_err(&ofdev->dev, "cannot allocate memory\n");
+ dev_err(&pdev->dev, "cannot allocate memory\n");
ret = -ENOMEM;
goto error2;
}
@@ -1497,7 +1522,7 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev)
memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
mfbi->parent = machine_data;
- if (mfbi->index == 0) {
+ if (mfbi->index == PLANE0) {
const u8 *prop;
int len;
@@ -1509,60 +1534,49 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev)
}
}
- ret = of_address_to_resource(np, 0, &res);
- if (ret) {
- dev_err(&ofdev->dev, "could not obtain DIU address\n");
- goto error;
- }
- if (!res.start) {
- dev_err(&ofdev->dev, "invalid DIU address\n");
- goto error;
- }
- dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start);
-
- dr.diu_reg = ioremap(res.start, sizeof(struct diu));
- if (!dr.diu_reg) {
- dev_err(&ofdev->dev, "Err: can't map DIU registers!\n");
+ machine_data->diu_reg = of_iomap(np, 0);
+ if (!machine_data->diu_reg) {
+ dev_err(&pdev->dev, "cannot map DIU registers\n");
ret = -EFAULT;
goto error2;
}
- diu_mode = in_be32(&dr.diu_reg->diu_mode);
- if (diu_mode != MFB_MODE1)
- out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */
+ diu_mode = in_be32(&machine_data->diu_reg->diu_mode);
+ if (diu_mode == MFB_MODE0)
+ out_be32(&machine_data->diu_reg->diu_mode, 0); /* disable DIU */
/* Get the IRQ of the DIU */
machine_data->irq = irq_of_parse_and_map(np, 0);
if (!machine_data->irq) {
- dev_err(&ofdev->dev, "could not get DIU IRQ\n");
+ dev_err(&pdev->dev, "could not get DIU IRQ\n");
ret = -EINVAL;
goto error;
}
machine_data->monitor_port = monitor_port;
/* Area descriptor memory pool aligns to 64-bit boundary */
- if (allocate_buf(&ofdev->dev, &pool.ad,
+ if (allocate_buf(&pdev->dev, &machine_data->ad,
sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
return -ENOMEM;
/* Get memory for Gamma Table - 32-byte aligned memory */
- if (allocate_buf(&ofdev->dev, &pool.gamma, 768, 32)) {
+ if (allocate_buf(&pdev->dev, &machine_data->gamma, 768, 32)) {
ret = -ENOMEM;
goto error;
}
/* For performance, cursor bitmap buffer aligns to 32-byte boundary */
- if (allocate_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
- 32)) {
+ if (allocate_buf(&pdev->dev, &machine_data->cursor,
+ MAX_CURS * MAX_CURS * 2, 32)) {
ret = -ENOMEM;
goto error;
}
i = ARRAY_SIZE(machine_data->fsl_diu_info);
- machine_data->dummy_ad = (struct diu_ad *)
- ((u32)pool.ad.vaddr + pool.ad.offset) + i;
- machine_data->dummy_ad->paddr = pool.ad.paddr +
+ machine_data->dummy_ad = (struct diu_ad *)((u32)machine_data->ad.vaddr +
+ machine_data->ad.offset) + i;
+ machine_data->dummy_ad->paddr = machine_data->ad.paddr +
i * sizeof(struct diu_ad);
machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
if (!machine_data->dummy_aoi_virt) {
@@ -1581,30 +1595,29 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev)
* Let DIU display splash screen if it was pre-initialized
* by the bootloader, set dummy area descriptor otherwise.
*/
- if (diu_mode != MFB_MODE1)
- out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
+ if (diu_mode == MFB_MODE0)
+ out_be32(&machine_data->diu_reg->desc[0],
+ machine_data->dummy_ad->paddr);
- out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr);
- out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr);
+ out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad->paddr);
+ out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad->paddr);
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
machine_data->fsl_diu_info[i]->fix.smem_start = 0;
mfbi = machine_data->fsl_diu_info[i]->par;
- mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr
- + pool.ad.offset) + i;
- mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad);
+ mfbi->ad = (struct diu_ad *)((u32)machine_data->ad.vaddr
+ + machine_data->ad.offset) + i;
+ mfbi->ad->paddr =
+ machine_data->ad.paddr + i * sizeof(struct diu_ad);
ret = install_fb(machine_data->fsl_diu_info[i]);
if (ret) {
- dev_err(&ofdev->dev,
- "Failed to register framebuffer %d\n",
- i);
+ dev_err(&pdev->dev, "could not register fb %d\n", i);
goto error;
}
}
- if (request_irq_local(machine_data->irq)) {
- dev_err(machine_data->fsl_diu_info[0]->dev,
- "could not request irq for diu.");
+ if (request_irq_local(machine_data)) {
+ dev_err(&pdev->dev, "could not claim irq\n");
goto error;
}
@@ -1616,29 +1629,28 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev)
error = device_create_file(machine_data->fsl_diu_info[0]->dev,
&machine_data->dev_attr);
if (error) {
- dev_err(machine_data->fsl_diu_info[0]->dev,
- "could not create sysfs %s file\n",
+ dev_err(&pdev->dev, "could not create sysfs file %s\n",
machine_data->dev_attr.attr.name);
}
- dev_set_drvdata(&ofdev->dev, machine_data);
+ dev_set_drvdata(&pdev->dev, machine_data);
return 0;
error:
- for (i = ARRAY_SIZE(machine_data->fsl_diu_info);
- i > 0; i--)
- uninstall_fb(machine_data->fsl_diu_info[i - 1]);
- if (pool.ad.vaddr)
- free_buf(&ofdev->dev, &pool.ad,
+ for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
+ uninstall_fb(machine_data->fsl_diu_info[i]);
+
+ if (machine_data->ad.vaddr)
+ free_buf(&pdev->dev, &machine_data->ad,
sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
- if (pool.gamma.vaddr)
- free_buf(&ofdev->dev, &pool.gamma, 768, 32);
- if (pool.cursor.vaddr)
- free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
- 32);
+ if (machine_data->gamma.vaddr)
+ free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
+ if (machine_data->cursor.vaddr)
+ free_buf(&pdev->dev, &machine_data->cursor,
+ MAX_CURS * MAX_CURS * 2, 32);
if (machine_data->dummy_aoi_virt)
fsl_diu_free(machine_data->dummy_aoi_virt, 64);
- iounmap(dr.diu_reg);
+ iounmap(machine_data->diu_reg);
error2:
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
@@ -1649,28 +1661,27 @@ error2:
return ret;
}
-
-static int fsl_diu_remove(struct platform_device *ofdev)
+static int fsl_diu_remove(struct platform_device *pdev)
{
struct fsl_diu_data *machine_data;
int i;
- machine_data = dev_get_drvdata(&ofdev->dev);
+ machine_data = dev_get_drvdata(&pdev->dev);
disable_lcdc(machine_data->fsl_diu_info[0]);
- free_irq_local(machine_data->irq);
- for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--)
- uninstall_fb(machine_data->fsl_diu_info[i - 1]);
- if (pool.ad.vaddr)
- free_buf(&ofdev->dev, &pool.ad,
+ free_irq_local(machine_data);
+ for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
+ uninstall_fb(machine_data->fsl_diu_info[i]);
+ if (machine_data->ad.vaddr)
+ free_buf(&pdev->dev, &machine_data->ad,
sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
- if (pool.gamma.vaddr)
- free_buf(&ofdev->dev, &pool.gamma, 768, 32);
- if (pool.cursor.vaddr)
- free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
- 32);
+ if (machine_data->gamma.vaddr)
+ free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
+ if (machine_data->cursor.vaddr)
+ free_buf(&pdev->dev, &machine_data->cursor,
+ MAX_CURS * MAX_CURS * 2, 32);
if (machine_data->dummy_aoi_virt)
fsl_diu_free(machine_data->dummy_aoi_virt, 64);
- iounmap(dr.diu_reg);
+ iounmap(machine_data->diu_reg);
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
if (machine_data->fsl_diu_info[i])
framebuffer_release(machine_data->fsl_diu_info[i]);
@@ -1692,8 +1703,7 @@ static int __init fsl_diu_setup(char *options)
if (!*opt)
continue;
if (!strncmp(opt, "monitor=", 8)) {
- if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2))
- monitor_port = val;
+ monitor_port = fsl_diu_name_to_port(opt + 8);
} else if (!strncmp(opt, "bpp=", 4)) {
if (!strict_strtoul(opt + 4, 10, &val))
default_bpp = val;
@@ -1720,7 +1730,7 @@ MODULE_DEVICE_TABLE(of, fsl_diu_match);
static struct platform_driver fsl_diu_driver = {
.driver = {
- .name = "fsl_diu",
+ .name = "fsl-diu-fb",
.owner = THIS_MODULE,
.of_match_table = fsl_diu_match,
},
@@ -1746,48 +1756,54 @@ static int __init fsl_diu_init(void)
if (fb_get_options("fslfb", &option))
return -ENODEV;
fsl_diu_setup(option);
+#else
+ monitor_port = fsl_diu_name_to_port(monitor_string);
#endif
- printk(KERN_INFO "Freescale DIU driver\n");
+ pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
#ifdef CONFIG_NOT_COHERENT_CACHE
np = of_find_node_by_type(NULL, "cpu");
if (!np) {
- printk(KERN_ERR "Err: can't find device node 'cpu'\n");
+ pr_err("fsl-diu-fb: can't find 'cpu' device node\n");
return -ENODEV;
}
prop = of_get_property(np, "d-cache-size", NULL);
if (prop == NULL) {
+ pr_err("fsl-diu-fb: missing 'd-cache-size' property' "
+ "in 'cpu' node\n");
of_node_put(np);
return -ENODEV;
}
- /* Freescale PLRU requires 13/8 times the cache size to do a proper
- displacement flush
+ /*
+ * Freescale PLRU requires 13/8 times the cache size to do a proper
+ * displacement flush
*/
- coherence_data_size = *prop * 13;
+ coherence_data_size = be32_to_cpup(prop) * 13;
coherence_data_size /= 8;
prop = of_get_property(np, "d-cache-line-size", NULL);
if (prop == NULL) {
+ pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
+ "in 'cpu' node\n");
of_node_put(np);
return -ENODEV;
}
- d_cache_line_size = *prop;
+ d_cache_line_size = be32_to_cpup(prop);
of_node_put(np);
coherence_data = vmalloc(coherence_data_size);
if (!coherence_data)
return -ENOMEM;
#endif
+
ret = platform_driver_register(&fsl_diu_driver);
if (ret) {
- printk(KERN_ERR
- "fsl-diu: failed to register platform driver\n");
+ pr_err("fsl-diu-fb: failed to register platform driver\n");
#if defined(CONFIG_NOT_COHERENT_CACHE)
vfree(coherence_data);
#endif
- iounmap(dr.diu_reg);
}
return ret;
}
@@ -1811,8 +1827,8 @@ module_param_named(mode, fb_mode, charp, 0);
MODULE_PARM_DESC(mode,
"Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
module_param_named(bpp, default_bpp, ulong, 0);
-MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
-module_param_named(monitor, monitor_port, int, 0);
-MODULE_PARM_DESC(monitor,
- "Specify the monitor port (0, 1 or 2) if supported by the platform");
+MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");
+module_param_named(monitor, monitor_string, charp, 0);
+MODULE_PARM_DESC(monitor, "Specify the monitor port "
+ "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");
diff --git a/drivers/video/g364fb.c b/drivers/video/g364fb.c
index d662317d85e3..223896cc5f7d 100644
--- a/drivers/video/g364fb.c
+++ b/drivers/video/g364fb.c
@@ -149,10 +149,11 @@ int g364fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
static int g364fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
- if (var->xoffset || var->yoffset + var->yres > var->yres_virtual)
+ if (var->xoffset ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
- *(unsigned int *) TOP_REG = var->yoffset * var->xres;
+ *(unsigned int *) TOP_REG = var->yoffset * info->var.xres;
return 0;
}
diff --git a/drivers/video/grvga.c b/drivers/video/grvga.c
new file mode 100644
index 000000000000..f37e02538203
--- /dev/null
+++ b/drivers/video/grvga.c
@@ -0,0 +1,579 @@
+/*
+ * Driver for Aeroflex Gaisler SVGACTRL framebuffer device.
+ *
+ * 2011 (c) Aeroflex Gaisler AB
+ *
+ * Full documentation of the core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * 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.
+ *
+ * Contributors: Kristoffer Glembo <kristoffer@gaisler.com>
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+
+struct grvga_regs {
+ u32 status; /* 0x00 */
+ u32 video_length; /* 0x04 */
+ u32 front_porch; /* 0x08 */
+ u32 sync_length; /* 0x0C */
+ u32 line_length; /* 0x10 */
+ u32 fb_pos; /* 0x14 */
+ u32 clk_vector[4]; /* 0x18 */
+ u32 clut; /* 0x20 */
+};
+
+struct grvga_par {
+ struct grvga_regs *regs;
+ u32 color_palette[16]; /* 16 entry pseudo palette used by fbcon in true color mode */
+ int clk_sel;
+ int fb_alloced; /* = 1 if framebuffer is allocated in main memory */
+};
+
+
+static const struct fb_videomode grvga_modedb[] = {
+ {
+ /* 640x480 @ 60 Hz */
+ NULL, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 800x600 @ 60 Hz */
+ NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 800x600 @ 72 Hz */
+ NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 60 Hz */
+ NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }
+ };
+
+static struct fb_fix_screeninfo grvga_fix __initdata = {
+ .id = "AG SVGACTRL",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_PSEUDOCOLOR,
+ .xpanstep = 0,
+ .ypanstep = 1,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+static int grvga_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct grvga_par *par = info->par;
+ int i;
+
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+ if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+ else if (var->bits_per_pixel <= 24)
+ var->bits_per_pixel = 24;
+ else if (var->bits_per_pixel <= 32)
+ var->bits_per_pixel = 32;
+ else
+ return -EINVAL;
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = 2*var->yres;
+
+ if (info->fix.smem_len) {
+ if ((var->yres_virtual*var->xres_virtual*var->bits_per_pixel/8) > info->fix.smem_len)
+ return -ENOMEM;
+ }
+
+ /* Which clocks that are available can be read out in these registers */
+ for (i = 0; i <= 3 ; i++) {
+ if (var->pixclock == par->regs->clk_vector[i])
+ break;
+ }
+ if (i <= 3)
+ par->clk_sel = i;
+ else
+ return -EINVAL;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ var->red = (struct fb_bitfield) {0, 8, 0}; /* offset, length, msb-right */
+ var->green = (struct fb_bitfield) {0, 8, 0};
+ var->blue = (struct fb_bitfield) {0, 8, 0};
+ var->transp = (struct fb_bitfield) {0, 0, 0};
+ break;
+ case 16:
+ var->red = (struct fb_bitfield) {11, 5, 0};
+ var->green = (struct fb_bitfield) {5, 6, 0};
+ var->blue = (struct fb_bitfield) {0, 5, 0};
+ var->transp = (struct fb_bitfield) {0, 0, 0};
+ break;
+ case 24:
+ case 32:
+ var->red = (struct fb_bitfield) {16, 8, 0};
+ var->green = (struct fb_bitfield) {8, 8, 0};
+ var->blue = (struct fb_bitfield) {0, 8, 0};
+ var->transp = (struct fb_bitfield) {24, 8, 0};
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int grvga_set_par(struct fb_info *info)
+{
+
+ u32 func = 0;
+ struct grvga_par *par = info->par;
+
+ __raw_writel(((info->var.yres - 1) << 16) | (info->var.xres - 1),
+ &par->regs->video_length);
+
+ __raw_writel((info->var.lower_margin << 16) | (info->var.right_margin),
+ &par->regs->front_porch);
+
+ __raw_writel((info->var.vsync_len << 16) | (info->var.hsync_len),
+ &par->regs->sync_length);
+
+ __raw_writel(((info->var.yres + info->var.lower_margin + info->var.upper_margin + info->var.vsync_len - 1) << 16) |
+ (info->var.xres + info->var.right_margin + info->var.left_margin + info->var.hsync_len - 1),
+ &par->regs->line_length);
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ func = 1;
+ break;
+ case 16:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ func = 2;
+ break;
+ case 24:
+ case 32:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ func = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __raw_writel((par->clk_sel << 6) | (func << 4) | 1,
+ &par->regs->status);
+
+ info->fix.line_length = (info->var.xres_virtual*info->var.bits_per_pixel)/8;
+ return 0;
+}
+
+static int grvga_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
+{
+ struct grvga_par *par;
+ par = info->par;
+
+ if (regno >= 256) /* Size of CLUT */
+ return -EINVAL;
+
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+
+#undef CNVT_TOHW
+
+ /* In PSEUDOCOLOR we use the hardware CLUT */
+ if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
+ __raw_writel((regno << 24) | (red << 16) | (green << 8) | blue,
+ &par->regs->clut);
+
+ /* Truecolor uses the pseudo palette */
+ else if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+ u32 v;
+ if (regno >= 16)
+ return -EINVAL;
+
+
+ v = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ ((u32 *) (info->pseudo_palette))[regno] = v;
+ }
+ return 0;
+}
+
+static int grvga_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct grvga_par *par = info->par;
+ struct fb_fix_screeninfo *fix = &info->fix;
+ u32 base_addr;
+
+ if (var->xoffset != 0)
+ return -EINVAL;
+
+ base_addr = fix->smem_start + (var->yoffset * fix->line_length);
+ base_addr &= ~3UL;
+
+ /* Set framebuffer base address */
+ __raw_writel(base_addr,
+ &par->regs->fb_pos);
+
+ return 0;
+}
+
+static struct fb_ops grvga_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = grvga_check_var,
+ .fb_set_par = grvga_set_par,
+ .fb_setcolreg = grvga_setcolreg,
+ .fb_pan_display = grvga_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit
+};
+
+static int __init grvga_parse_custom(char *options,
+ struct fb_var_screeninfo *screendata)
+{
+ char *this_opt;
+ int count = 0;
+ if (!options || !*options)
+ return -1;
+
+ while ((this_opt = strsep(&options, " ")) != NULL) {
+ if (!*this_opt)
+ continue;
+
+ switch (count) {
+ case 0:
+ screendata->pixclock = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 1:
+ screendata->xres = screendata->xres_virtual = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 2:
+ screendata->right_margin = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 3:
+ screendata->hsync_len = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 4:
+ screendata->left_margin = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 5:
+ screendata->yres = screendata->yres_virtual = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 6:
+ screendata->lower_margin = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 7:
+ screendata->vsync_len = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 8:
+ screendata->upper_margin = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ case 9:
+ screendata->bits_per_pixel = simple_strtoul(this_opt, NULL, 0);
+ count++;
+ break;
+ default:
+ return -1;
+ }
+ }
+ screendata->activate = FB_ACTIVATE_NOW;
+ screendata->vmode = FB_VMODE_NONINTERLACED;
+ return 0;
+}
+
+static int __devinit grvga_probe(struct platform_device *dev)
+{
+ struct fb_info *info;
+ int retval = -ENOMEM;
+ unsigned long virtual_start;
+ unsigned long grvga_fix_addr = 0;
+ unsigned long physical_start = 0;
+ unsigned long grvga_mem_size = 0;
+ struct grvga_par *par = NULL;
+ char *options = NULL, *mode_opt = NULL;
+
+ info = framebuffer_alloc(sizeof(struct grvga_par), &dev->dev);
+ if (!info) {
+ dev_err(&dev->dev, "framebuffer_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ /* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]
+ *
+ * If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters
+ * If address is left out, we allocate memory,
+ * if size is left out we only allocate enough to support the given mode.
+ */
+ if (fb_get_options("grvga", &options)) {
+ retval = -ENODEV;
+ goto err;
+ }
+
+ if (!options || !*options)
+ options = "640x480-8@60";
+
+ while (1) {
+ char *this_opt = strsep(&options, ",");
+
+ if (!this_opt)
+ break;
+
+ if (!strncmp(this_opt, "custom", 6)) {
+ 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;
+ }
+ } else if (!strncmp(this_opt, "addr", 4))
+ grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
+ else if (!strncmp(this_opt, "size", 4))
+ grvga_mem_size = simple_strtoul(this_opt + 5, NULL, 0);
+ else
+ mode_opt = this_opt;
+ }
+
+ par = info->par;
+ info->fbops = &grvga_ops;
+ info->fix = grvga_fix;
+ info->pseudo_palette = par->color_palette;
+ 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")) {
+ dev_err(&dev->dev, "registers already mapped\n");
+ retval = -EBUSY;
+ goto err;
+ }
+
+ par->regs = of_ioremap(&dev->resource[0], 0,
+ resource_size(&dev->resource[0]),
+ "grlib-svgactrl regs");
+
+ if (!par->regs) {
+ dev_err(&dev->dev, "failed to map registers\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ 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;
+ }
+
+ if (mode_opt) {
+ retval = fb_find_mode(&info->var, info, mode_opt,
+ grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
+ if (!retval || retval == 4) {
+ retval = -EINVAL;
+ goto err3;
+ }
+ }
+
+ if (!grvga_mem_size)
+ grvga_mem_size = info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel/8;
+
+ if (grvga_fix_addr) {
+ /* Got framebuffer base address from argument list */
+
+ physical_start = grvga_fix_addr;
+
+ if (!request_mem_region(physical_start, grvga_mem_size, dev->name)) {
+ dev_err(&dev->dev, "failed to request memory region\n");
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
+
+ if (!virtual_start) {
+ dev_err(&dev->dev, "error mapping framebuffer memory\n");
+ retval = -ENOMEM;
+ goto err4;
+ }
+ } else { /* Allocate frambuffer memory */
+
+ unsigned long page;
+
+ virtual_start = (unsigned long) __get_free_pages(GFP_DMA,
+ get_order(grvga_mem_size));
+ if (!virtual_start) {
+ dev_err(&dev->dev,
+ "unable to allocate framebuffer memory (%lu bytes)\n",
+ grvga_mem_size);
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
+
+ /* Set page reserved so that mmap will work. This is necessary
+ * since we'll be remapping normal memory.
+ */
+ for (page = virtual_start;
+ page < PAGE_ALIGN(virtual_start + grvga_mem_size);
+ page += PAGE_SIZE) {
+ SetPageReserved(virt_to_page(page));
+ }
+
+ par->fb_alloced = 1;
+ }
+
+ memset((unsigned long *) virtual_start, 0, grvga_mem_size);
+
+ info->screen_base = (char __iomem *) virtual_start;
+ info->fix.smem_start = physical_start;
+ info->fix.smem_len = grvga_mem_size;
+
+ dev_set_drvdata(&dev->dev, info);
+
+ dev_info(&dev->dev,
+ "Aeroflex Gaisler framebuffer device (fb%d), %dx%d-%d, using %luK of video memory @ %p\n",
+ info->node, info->var.xres, info->var.yres, info->var.bits_per_pixel,
+ grvga_mem_size >> 10, info->screen_base);
+
+ retval = register_framebuffer(info);
+ if (retval < 0) {
+ dev_err(&dev->dev, "failed to register framebuffer\n");
+ goto err4;
+ }
+
+ __raw_writel(physical_start, &par->regs->fb_pos);
+ __raw_writel(__raw_readl(&par->regs->status) | 1, /* Enable framebuffer */
+ &par->regs->status);
+
+ return 0;
+
+err4:
+ dev_set_drvdata(&dev->dev, NULL);
+ if (grvga_fix_addr) {
+ release_mem_region(physical_start, grvga_mem_size);
+ iounmap((void *)virtual_start);
+ } else
+ kfree((void *)virtual_start);
+err3:
+ fb_dealloc_cmap(&info->cmap);
+err2:
+ 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:
+ framebuffer_release(info);
+
+ return retval;
+}
+
+static int __devexit grvga_remove(struct platform_device *device)
+{
+ struct fb_info *info = dev_get_drvdata(&device->dev);
+ struct grvga_par *par = info->par;
+
+ if (info) {
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+
+ 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);
+ iounmap(info->screen_base);
+ } else
+ kfree((void *)info->screen_base);
+
+ framebuffer_release(info);
+ dev_set_drvdata(&device->dev, NULL);
+ }
+
+ return 0;
+}
+
+static struct of_device_id svgactrl_of_match[] = {
+ {
+ .name = "GAISLER_SVGACTRL",
+ },
+ {
+ .name = "01_063",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, svgactrl_of_match);
+
+static struct platform_driver grvga_driver = {
+ .driver = {
+ .name = "grlib-svgactrl",
+ .owner = THIS_MODULE,
+ .of_match_table = svgactrl_of_match,
+ },
+ .probe = grvga_probe,
+ .remove = __devexit_p(grvga_remove),
+};
+
+
+static int __init grvga_init(void)
+{
+ return platform_driver_register(&grvga_driver);
+}
+
+static void __exit grvga_exit(void)
+{
+ platform_driver_unregister(&grvga_driver);
+}
+
+module_init(grvga_init);
+module_exit(grvga_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Aeroflex Gaisler");
+MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");
diff --git a/drivers/video/gxt4500.c b/drivers/video/gxt4500.c
index 896e53dea906..0fad23f810a3 100644
--- a/drivers/video/gxt4500.c
+++ b/drivers/video/gxt4500.c
@@ -543,8 +543,8 @@ static int gxt4500_pan_display(struct fb_var_screeninfo *var,
if (var->xoffset & 7)
return -EINVAL;
- if (var->xoffset + var->xres > var->xres_virtual ||
- var->yoffset + var->yres > var->yres_virtual)
+ if (var->xoffset + info->var.xres > info->var.xres_virtual ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset);
diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c
index 4052718eefaa..4394389caf68 100644
--- a/drivers/video/hgafb.c
+++ b/drivers/video/hgafb.c
@@ -422,8 +422,8 @@ static int hgafb_pan_display(struct fb_var_screeninfo *var,
var->xoffset)
return -EINVAL;
} else {
- if (var->xoffset + var->xres > info->var.xres_virtual
- || var->yoffset + var->yres > info->var.yres_virtual
+ if (var->xoffset + info->var.xres > info->var.xres_virtual
+ || var->yoffset + info->var.yres > info->var.yres_virtual
|| var->yoffset % 8)
return -EINVAL;
}
diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c
index efb2c10656b0..8149356471e4 100644
--- a/drivers/video/imsttfb.c
+++ b/drivers/video/imsttfb.c
@@ -749,7 +749,7 @@ set_offset (struct fb_var_screeninfo *var, struct fb_info *info)
{
struct imstt_par *par = info->par;
__u32 off = var->yoffset * (info->fix.line_length >> 3)
- + ((var->xoffset * (var->bits_per_pixel >> 3)) >> 3);
+ + ((var->xoffset * (info->var.bits_per_pixel >> 3)) >> 3);
write_reg_le32(par->dc_regs, SSR, off);
}
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c
index 38065cf94ac4..fbad61da359f 100644
--- a/drivers/video/intelfb/intelfbhw.c
+++ b/drivers/video/intelfb/intelfbhw.c
@@ -390,12 +390,12 @@ int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
xoffset = ROUND_DOWN_TO(var->xoffset, 8);
yoffset = var->yoffset;
- if ((xoffset + var->xres > var->xres_virtual) ||
- (yoffset + var->yres > var->yres_virtual))
+ if ((xoffset + info->var.xres > info->var.xres_virtual) ||
+ (yoffset + info->var.yres > info->var.yres_virtual))
return -EINVAL;
offset = (yoffset * dinfo->pitch) +
- (xoffset * var->bits_per_pixel) / 8;
+ (xoffset * info->var.bits_per_pixel) / 8;
offset += dinfo->fb.offset << 12;
diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c
index b953099edd8e..934081d2b7ae 100644
--- a/drivers/video/mb862xx/mb862xx-i2c.c
+++ b/drivers/video/mb862xx/mb862xx-i2c.c
@@ -23,7 +23,7 @@ static int mb862xx_i2c_wait_event(struct i2c_adapter *adap)
u32 reg;
do {
- udelay(1);
+ udelay(10);
reg = inreg(i2c, GC_I2C_BCR);
if (reg & (I2C_INT | I2C_BER))
break;
diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c
index ee1de3e26dec..c16ff1d62e91 100644
--- a/drivers/video/mb862xx/mb862xxfbdrv.c
+++ b/drivers/video/mb862xx/mb862xxfbdrv.c
@@ -278,7 +278,7 @@ static int mb862xxfb_pan(struct fb_var_screeninfo *var,
reg = pack(var->yoffset, var->xoffset);
outreg(disp, GC_L0WY_L0WX, reg);
- reg = pack(var->yres_virtual, var->xres_virtual);
+ reg = pack(info->var.yres_virtual, info->var.xres_virtual);
outreg(disp, GC_L0WH_L0WW, reg);
return 0;
}
@@ -737,7 +737,7 @@ static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev)
if (mb862xx_gdc_init(par))
goto io_unmap;
- if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED,
+ if (request_irq(par->irq, mb862xx_intr, 0,
DRV_NAME, (void *)par)) {
dev_err(dev, "Cannot request irq\n");
goto io_unmap;
@@ -1073,7 +1073,7 @@ static int __devinit mb862xx_pci_probe(struct pci_dev *pdev,
if (mb862xx_pci_gdc_init(par))
goto io_unmap;
- if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED | IRQF_SHARED,
+ if (request_irq(par->irq, mb862xx_intr, IRQF_SHARED,
DRV_NAME, (void *)par)) {
dev_err(dev, "Cannot request irq\n");
goto io_unmap;
diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c
index afea9abbd678..6ce34160da78 100644
--- a/drivers/video/mbx/mbxfb.c
+++ b/drivers/video/mbx/mbxfb.c
@@ -34,7 +34,7 @@
#include "regs.h"
#include "reg_bits.h"
-static unsigned long virt_base_2700;
+static void __iomem *virt_base_2700;
#define write_reg(val, reg) do { writel((val), (reg)); } while(0)
@@ -850,7 +850,7 @@ static int mbxfb_suspend(struct platform_device *dev, pm_message_t state)
{
/* make frame buffer memory enter self-refresh mode */
write_reg_dly(LMPWR_MC_PWR_SRM, LMPWR);
- while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM)
+ while (readl(LMPWRSTAT) != LMPWRSTAT_MC_PWR_SRM)
; /* empty statement */
/* reset the device, since it's initial state is 'mostly sleeping' */
@@ -946,7 +946,7 @@ static int __devinit mbxfb_probe(struct platform_device *dev)
ret = -EINVAL;
goto err3;
}
- virt_base_2700 = (unsigned long)mfbi->reg_virt_addr;
+ virt_base_2700 = mfbi->reg_virt_addr;
mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr,
res_size(mfbi->fb_req));
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index cb175fe7abc0..a9a907c440d7 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -491,55 +491,56 @@ EXPORT_SYMBOL(vesa_modes);
static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
const struct fb_videomode *mode, unsigned int bpp)
{
- int err = 0;
-
- DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname",
- mode->xres, mode->yres, bpp, mode->refresh);
- var->xres = mode->xres;
- var->yres = mode->yres;
- var->xres_virtual = mode->xres;
- var->yres_virtual = mode->yres;
- var->xoffset = 0;
- var->yoffset = 0;
- var->bits_per_pixel = bpp;
- var->activate |= FB_ACTIVATE_TEST;
- var->pixclock = mode->pixclock;
- var->left_margin = mode->left_margin;
- var->right_margin = mode->right_margin;
- var->upper_margin = mode->upper_margin;
- var->lower_margin = mode->lower_margin;
- var->hsync_len = mode->hsync_len;
- var->vsync_len = mode->vsync_len;
- var->sync = mode->sync;
- var->vmode = mode->vmode;
- if (info->fbops->fb_check_var)
- err = info->fbops->fb_check_var(var, info);
- var->activate &= ~FB_ACTIVATE_TEST;
- return err;
+ int err = 0;
+
+ DPRINTK("Trying mode %s %dx%d-%d@%d\n",
+ mode->name ? mode->name : "noname",
+ mode->xres, mode->yres, bpp, mode->refresh);
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = mode->xres;
+ var->yres_virtual = mode->yres;
+ var->xoffset = 0;
+ var->yoffset = 0;
+ var->bits_per_pixel = bpp;
+ var->activate |= FB_ACTIVATE_TEST;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = mode->vmode;
+ if (info->fbops->fb_check_var)
+ err = info->fbops->fb_check_var(var, info);
+ var->activate &= ~FB_ACTIVATE_TEST;
+ return err;
}
/**
- * fb_find_mode - finds a valid video mode
- * @var: frame buffer user defined part of display
- * @info: frame buffer info structure
- * @mode_option: string video mode to find
- * @db: video mode database
- * @dbsize: size of @db
- * @default_mode: default video mode to fall back to
- * @default_bpp: default color depth in bits per pixel
+ * fb_find_mode - finds a valid video mode
+ * @var: frame buffer user defined part of display
+ * @info: frame buffer info structure
+ * @mode_option: string video mode to find
+ * @db: video mode database
+ * @dbsize: size of @db
+ * @default_mode: default video mode to fall back to
+ * @default_bpp: default color depth in bits per pixel
*
- * Finds a suitable video mode, starting with the specified mode
- * in @mode_option with fallback to @default_mode. If
- * @default_mode fails, all modes in the video mode database will
- * be tried.
+ * Finds a suitable video mode, starting with the specified mode
+ * in @mode_option with fallback to @default_mode. If
+ * @default_mode fails, all modes in the video mode database will
+ * be tried.
*
- * Valid mode specifiers for @mode_option:
+ * Valid mode specifiers for @mode_option:
*
- * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
- * <name>[-<bpp>][@<refresh>]
+ * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
+ * <name>[-<bpp>][@<refresh>]
*
- * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
- * <name> a string.
+ * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
+ * <name> a string.
*
* If 'M' is present after yres (and before refresh/bpp if present),
* the function will compute the timings using VESA(tm) Coordinated
@@ -551,12 +552,12 @@ static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
*
* 1024x768MR-8@60m - Reduced blank with margins at 60Hz.
*
- * NOTE: The passed struct @var is _not_ cleared! This allows you
- * to supply values for e.g. the grayscale and accel_flags fields.
+ * NOTE: The passed struct @var is _not_ cleared! This allows you
+ * to supply values for e.g. the grayscale and accel_flags fields.
*
- * Returns zero for failure, 1 if using specified @mode_option,
- * 2 if using specified @mode_option with an ignored refresh rate,
- * 3 if default mode is used, 4 if fall back to any valid mode.
+ * Returns zero for failure, 1 if using specified @mode_option,
+ * 2 if using specified @mode_option with an ignored refresh rate,
+ * 3 if default mode is used, 4 if fall back to any valid mode.
*
*/
@@ -566,198 +567,203 @@ int fb_find_mode(struct fb_var_screeninfo *var,
const struct fb_videomode *default_mode,
unsigned int default_bpp)
{
- int i;
-
- /* Set up defaults */
- if (!db) {
- db = modedb;
- dbsize = ARRAY_SIZE(modedb);
- }
-
- if (!default_mode)
- default_mode = &db[0];
-
- if (!default_bpp)
- default_bpp = 8;
-
- /* Did the user specify a video mode? */
- if (!mode_option)
- mode_option = fb_mode_option;
- if (mode_option) {
- const char *name = mode_option;
- unsigned int namelen = strlen(name);
- int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
- unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
- int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
- u32 best, diff, tdiff;
-
- for (i = namelen-1; i >= 0; i--) {
- switch (name[i]) {
- case '@':
- namelen = i;
- if (!refresh_specified && !bpp_specified &&
- !yres_specified) {
- refresh = simple_strtol(&name[i+1], NULL, 10);
- refresh_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case '-':
- namelen = i;
- if (!bpp_specified && !yres_specified) {
- bpp = simple_strtol(&name[i+1], NULL, 10);
- bpp_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case 'x':
- if (!yres_specified) {
- yres = simple_strtol(&name[i+1], NULL, 10);
- yres_specified = 1;
- } else
- goto done;
- break;
- case '0' ... '9':
- break;
- case 'M':
- if (!yres_specified)
- cvt = 1;
- break;
- case 'R':
- if (!cvt)
- rb = 1;
- break;
- case 'm':
- if (!cvt)
- margins = 1;
- break;
- case 'i':
- if (!cvt)
- interlace = 1;
- break;
- default:
- goto done;
- }
- }
- if (i < 0 && yres_specified) {
- xres = simple_strtol(name, NULL, 10);
- res_specified = 1;
- }
-done:
- if (cvt) {
- struct fb_videomode cvt_mode;
- int ret;
-
- DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
- (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
- "", (margins) ? " with margins" : "", (interlace) ?
- " interlaced" : "");
-
- memset(&cvt_mode, 0, sizeof(cvt_mode));
- cvt_mode.xres = xres;
- cvt_mode.yres = yres;
- cvt_mode.refresh = (refresh) ? refresh : 60;
+ int i;
- if (interlace)
- cvt_mode.vmode |= FB_VMODE_INTERLACED;
- else
- cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
+ /* Set up defaults */
+ if (!db) {
+ db = modedb;
+ dbsize = ARRAY_SIZE(modedb);
+ }
- ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
+ if (!default_mode)
+ default_mode = &db[0];
+
+ if (!default_bpp)
+ default_bpp = 8;
+
+ /* Did the user specify a video mode? */
+ if (!mode_option)
+ mode_option = fb_mode_option;
+ if (mode_option) {
+ const char *name = mode_option;
+ unsigned int namelen = strlen(name);
+ int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
+ unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
+ int yres_specified = 0, cvt = 0, rb = 0, interlace = 0;
+ int margins = 0;
+ u32 best, diff, tdiff;
+
+ for (i = namelen-1; i >= 0; i--) {
+ switch (name[i]) {
+ case '@':
+ namelen = i;
+ if (!refresh_specified && !bpp_specified &&
+ !yres_specified) {
+ refresh = simple_strtol(&name[i+1], NULL,
+ 10);
+ refresh_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case '-':
+ namelen = i;
+ if (!bpp_specified && !yres_specified) {
+ bpp = simple_strtol(&name[i+1], NULL,
+ 10);
+ bpp_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case 'x':
+ if (!yres_specified) {
+ yres = simple_strtol(&name[i+1], NULL,
+ 10);
+ yres_specified = 1;
+ } else
+ goto done;
+ break;
+ case '0' ... '9':
+ break;
+ case 'M':
+ if (!yres_specified)
+ cvt = 1;
+ break;
+ case 'R':
+ if (!cvt)
+ rb = 1;
+ break;
+ case 'm':
+ if (!cvt)
+ margins = 1;
+ break;
+ case 'i':
+ if (!cvt)
+ interlace = 1;
+ break;
+ default:
+ goto done;
+ }
+ }
+ if (i < 0 && yres_specified) {
+ xres = simple_strtol(name, NULL, 10);
+ res_specified = 1;
+ }
+done:
+ if (cvt) {
+ struct fb_videomode cvt_mode;
+ int ret;
+
+ DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
+ (refresh) ? refresh : 60,
+ (rb) ? " reduced blanking" : "",
+ (margins) ? " with margins" : "",
+ (interlace) ? " interlaced" : "");
+
+ memset(&cvt_mode, 0, sizeof(cvt_mode));
+ cvt_mode.xres = xres;
+ cvt_mode.yres = yres;
+ cvt_mode.refresh = (refresh) ? refresh : 60;
+
+ if (interlace)
+ cvt_mode.vmode |= FB_VMODE_INTERLACED;
+ else
+ cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
+
+ ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
+
+ if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
+ DPRINTK("modedb CVT: CVT mode ok\n");
+ return 1;
+ }
- if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
- DPRINTK("modedb CVT: CVT mode ok\n");
- return 1;
- }
+ DPRINTK("CVT mode invalid, getting mode from database\n");
+ }
- DPRINTK("CVT mode invalid, getting mode from database\n");
- }
+ DPRINTK("Trying specified video mode%s %ix%i\n",
+ refresh_specified ? "" : " (ignoring refresh rate)",
+ xres, yres);
- DPRINTK("Trying specified video mode%s %ix%i\n",
- refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);
-
- if (!refresh_specified) {
- /*
- * If the caller has provided a custom mode database and a
- * valid monspecs structure, we look for the mode with the
- * highest refresh rate. Otherwise we play it safe it and
- * try to find a mode with a refresh rate closest to the
- * standard 60 Hz.
- */
- if (db != modedb &&
- info->monspecs.vfmin && info->monspecs.vfmax &&
- info->monspecs.hfmin && info->monspecs.hfmax &&
- info->monspecs.dclkmax) {
- refresh = 1000;
- } else {
- refresh = 60;
+ if (!refresh_specified) {
+ /*
+ * If the caller has provided a custom mode database and
+ * a valid monspecs structure, we look for the mode with
+ * the highest refresh rate. Otherwise we play it safe
+ * it and try to find a mode with a refresh rate closest
+ * to the standard 60 Hz.
+ */
+ if (db != modedb &&
+ info->monspecs.vfmin && info->monspecs.vfmax &&
+ info->monspecs.hfmin && info->monspecs.hfmax &&
+ info->monspecs.dclkmax) {
+ refresh = 1000;
+ } else {
+ refresh = 60;
+ }
}
- }
- diff = -1;
- best = -1;
- for (i = 0; i < dbsize; i++) {
- if ((name_matches(db[i], name, namelen) ||
- (res_specified && res_matches(db[i], xres, yres))) &&
- !fb_try_mode(var, info, &db[i], bpp)) {
- if (refresh_specified && db[i].refresh == refresh) {
- return 1;
- } else {
+ diff = -1;
+ best = -1;
+ for (i = 0; i < dbsize; i++) {
+ if ((name_matches(db[i], name, namelen) ||
+ (res_specified && res_matches(db[i], xres, yres))) &&
+ !fb_try_mode(var, info, &db[i], bpp)) {
+ if (refresh_specified && db[i].refresh == refresh)
+ return 1;
+
if (abs(db[i].refresh - refresh) < diff) {
diff = abs(db[i].refresh - refresh);
best = i;
}
}
}
- }
- if (best != -1) {
- fb_try_mode(var, info, &db[best], bpp);
- return (refresh_specified) ? 2 : 1;
- }
-
- diff = 2 * (xres + yres);
- best = -1;
- DPRINTK("Trying best-fit modes\n");
- for (i = 0; i < dbsize; i++) {
- DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);
- if (!fb_try_mode(var, info, &db[i], bpp)) {
- tdiff = abs(db[i].xres - xres) +
- abs(db[i].yres - yres);
-
- /*
- * Penalize modes with resolutions smaller
- * than requested.
- */
- if (xres > db[i].xres || yres > db[i].yres)
- tdiff += xres + yres;
+ if (best != -1) {
+ fb_try_mode(var, info, &db[best], bpp);
+ return (refresh_specified) ? 2 : 1;
+ }
- if (diff > tdiff) {
- diff = tdiff;
- best = i;
+ diff = 2 * (xres + yres);
+ best = -1;
+ DPRINTK("Trying best-fit modes\n");
+ for (i = 0; i < dbsize; i++) {
+ DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);
+ if (!fb_try_mode(var, info, &db[i], bpp)) {
+ tdiff = abs(db[i].xres - xres) +
+ abs(db[i].yres - yres);
+
+ /*
+ * Penalize modes with resolutions smaller
+ * than requested.
+ */
+ if (xres > db[i].xres || yres > db[i].yres)
+ tdiff += xres + yres;
+
+ if (diff > tdiff) {
+ diff = tdiff;
+ best = i;
+ }
}
}
+ if (best != -1) {
+ fb_try_mode(var, info, &db[best], bpp);
+ return 5;
+ }
}
- if (best != -1) {
- fb_try_mode(var, info, &db[best], bpp);
- return 5;
- }
- }
- DPRINTK("Trying default video mode\n");
- if (!fb_try_mode(var, info, default_mode, default_bpp))
- return 3;
+ DPRINTK("Trying default video mode\n");
+ if (!fb_try_mode(var, info, default_mode, default_bpp))
+ return 3;
- DPRINTK("Trying all modes\n");
- for (i = 0; i < dbsize; i++)
- if (!fb_try_mode(var, info, &db[i], default_bpp))
- return 4;
+ DPRINTK("Trying all modes\n");
+ for (i = 0; i < dbsize; i++)
+ if (!fb_try_mode(var, info, &db[i], default_bpp))
+ return 4;
- DPRINTK("No valid mode found\n");
- return 0;
+ DPRINTK("No valid mode found\n");
+ return 0;
}
/**
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c
index 178b0720bd79..4527cbf0a4ec 100644
--- a/drivers/video/msm/mddi.c
+++ b/drivers/video/msm/mddi.c
@@ -715,7 +715,7 @@ static int __devinit mddi_probe(struct platform_device *pdev)
mddi->int_enable = 0;
mddi_writel(mddi->int_enable, INTEN);
- ret = request_irq(mddi->irq, mddi_isr, IRQF_DISABLED, "mddi",
+ ret = request_irq(mddi->irq, mddi_isr, 0, "mddi",
&mddi->client_data);
if (ret) {
printk(KERN_ERR "mddi: failed to request enable irq!\n");
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 243d16f09b8a..b9344772bac9 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -421,10 +421,11 @@ int mdp_probe(struct platform_device *pdev)
clk = clk_get(&pdev->dev, "mdp_clk");
if (IS_ERR(clk)) {
printk(KERN_INFO "mdp: failed to get mdp clk");
- return PTR_ERR(clk);
+ ret = PTR_ERR(clk);
+ goto error_get_clk;
}
- ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp);
+ ret = request_irq(mdp->irq, mdp_isr, 0, "msm_mdp", mdp);
if (ret)
goto error_request_irq;
disable_irq(mdp->irq);
@@ -495,6 +496,7 @@ int mdp_probe(struct platform_device *pdev)
error_device_register:
free_irq(mdp->irq, mdp);
error_request_irq:
+error_get_clk:
iounmap(mdp->base);
error_get_irq:
error_ioremap:
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index 7e3a490e8d76..e3406ab31305 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -382,6 +382,9 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi)
uint32_t enabled;
unsigned long flags;
+ if (mx3_fbi->txd == NULL)
+ return;
+
spin_lock_irqsave(&mx3fb->lock, flags);
enabled = sdc_fb_uninit(mx3_fbi);
@@ -986,9 +989,19 @@ static void __blank(int blank, struct fb_info *fbi)
{
struct mx3fb_info *mx3_fbi = fbi->par;
struct mx3fb_data *mx3fb = mx3_fbi->mx3fb;
+ int was_blank = mx3_fbi->blank;
mx3_fbi->blank = blank;
+ /* Attention!
+ * Do not call sdc_disable_channel() for a channel that is disabled
+ * already! This will result in a kernel NULL pointer dereference
+ * (mx3_fbi->txd is NULL). Hide the fact, that all blank modes are
+ * handled equally by this driver.
+ */
+ if (blank > FB_BLANK_UNBLANK && was_blank > FB_BLANK_UNBLANK)
+ return;
+
switch (blank) {
case FB_BLANK_POWERDOWN:
case FB_BLANK_VSYNC_SUSPEND:
@@ -1062,15 +1075,15 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
y_bottom = var->yoffset;
if (!(var->vmode & FB_VMODE_YWRAP))
- y_bottom += var->yres;
+ y_bottom += fbi->var.yres;
if (y_bottom > fbi->var.yres_virtual)
return -EINVAL;
mutex_lock(&mx3_fbi->mutex);
- offset = (var->yoffset * var->xres_virtual + var->xoffset) *
- (var->bits_per_pixel / 8);
+ offset = var->yoffset * fbi->fix.line_length
+ + var->xoffset * (fbi->var.bits_per_pixel / 8);
base = fbi->fix.smem_start + offset;
dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n",
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
index 0b2f2dd41416..d837d63c456f 100644
--- a/drivers/video/mxsfb.c
+++ b/drivers/video/mxsfb.c
@@ -39,6 +39,7 @@
* the required value in the imx_fb_videomode structure.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c
index 588527a254c2..feea7b1dc386 100644
--- a/drivers/video/neofb.c
+++ b/drivers/video/neofb.c
@@ -1185,8 +1185,8 @@ static int neofb_pan_display(struct fb_var_screeninfo *var,
DBG("neofb_update_start");
- Base = (var->yoffset * var->xres_virtual + var->xoffset) >> 2;
- Base *= (var->bits_per_pixel + 7) / 8;
+ Base = (var->yoffset * info->var.xres_virtual + var->xoffset) >> 2;
+ Base *= (info->var.bits_per_pixel + 7) / 8;
neoUnlock();
diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c
index 0fff59782e45..d1fbbd888cf4 100644
--- a/drivers/video/nuc900fb.c
+++ b/drivers/video/nuc900fb.c
@@ -39,7 +39,6 @@
#include <mach/regs-clock.h>
#include <mach/regs-ldm.h>
#include <mach/fb.h>
-#include <mach/clkdev.h>
#include "nuc900fb.h"
@@ -588,7 +587,7 @@ static int __devinit nuc900fb_probe(struct platform_device *pdev)
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &fbi->pseudo_pal;
- ret = request_irq(irq, nuc900fb_irqhandler, IRQF_DISABLED,
+ ret = request_irq(irq, nuc900fb_irqhandler, 0,
pdev->name, fbinfo);
if (ret) {
dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n",
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig
index 196fa2e7f438..84ff23208c25 100644
--- a/drivers/video/omap/Kconfig
+++ b/drivers/video/omap/Kconfig
@@ -9,35 +9,6 @@ config FB_OMAP
help
Frame buffer driver for OMAP based boards.
-config FB_OMAP_LCD_VGA
- bool "Use LCD in VGA mode"
- depends on MACH_OMAP_3430SDP || MACH_OMAP_LDP
- help
- Set LCD resolution as VGA (640 X 480).
- Default resolution without this option is QVGA(320 X 240).
- Please take a look at drivers/video/omap/lcd_ldp.c file
- for lcd driver code.
-choice
- depends on FB_OMAP && MACH_OVERO
- prompt "Screen resolution"
- default FB_OMAP_079M3R
- help
- Selected desired screen resolution
-
-config FB_OMAP_031M3R
- boolean "640 x 480 @ 60 Hz Reduced blanking"
-
-config FB_OMAP_048M3R
- boolean "800 x 600 @ 60 Hz Reduced blanking"
-
-config FB_OMAP_079M3R
- boolean "1024 x 768 @ 60 Hz Reduced blanking"
-
-config FB_OMAP_092M9R
- boolean "1280 x 720 @ 60 Hz Reduced blanking"
-
-endchoice
-
config FB_OMAP_LCDC_EXTERNAL
bool "External LCD controller support"
depends on FB_OMAP
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile
index 25db55696e14..ef78550917ff 100644
--- a/drivers/video/omap/Makefile
+++ b/drivers/video/omap/Makefile
@@ -17,7 +17,6 @@ objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
-objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o
@@ -26,14 +25,7 @@ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
-objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o
-objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o
-objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o
-objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o
-objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o
-objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o
objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
-objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o
objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o
omapfb-objs := $(objs-yy)
diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c
deleted file mode 100644
index e3eccc9af78e..000000000000
--- a/drivers/video/omap/lcd_2430sdp.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * LCD panel support for the TI 2430SDP board
- *
- * Copyright (C) 2007 MontaVista
- * Author: Hunyue Yau <hyau@mvista.com>
- *
- * Derived from drivers/video/omap/lcd-apollon.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/i2c/twl.h>
-
-#include <plat/mux.h>
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91
-#define SDP2430_LCD_PANEL_ENABLE_GPIO 154
-#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24
-#define SDP3430_LCD_PANEL_ENABLE_GPIO 28
-
-static unsigned backlight_gpio;
-static unsigned enable_gpio;
-
-#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */
-#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
-#define ENABLE_VAUX2_DEDICATED 0x09
-#define ENABLE_VAUX2_DEV_GRP 0x20
-#define ENABLE_VAUX3_DEDICATED 0x03
-#define ENABLE_VAUX3_DEV_GRP 0x20
-
-#define ENABLE_VPLL2_DEDICATED 0x05
-#define ENABLE_VPLL2_DEV_GRP 0xE0
-#define TWL4030_VPLL2_DEV_GRP 0x33
-#define TWL4030_VPLL2_DEDICATED 0x36
-
-#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v)
-
-
-static int sdp2430_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- if (machine_is_omap_3430sdp()) {
- enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO;
- backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO;
- } else {
- enable_gpio = SDP2430_LCD_PANEL_ENABLE_GPIO;
- backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO;
- }
-
- gpio_request(enable_gpio, "LCD enable"); /* LCD panel */
- gpio_request(backlight_gpio, "LCD bl"); /* LCD backlight */
- gpio_direction_output(enable_gpio, 0);
- gpio_direction_output(backlight_gpio, 0);
-
- return 0;
-}
-
-static void sdp2430_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(backlight_gpio);
- gpio_free(enable_gpio);
-}
-
-static int sdp2430_panel_enable(struct lcd_panel *panel)
-{
- u8 ded_val, ded_reg;
- u8 grp_val, grp_reg;
-
- if (machine_is_omap_3430sdp()) {
- ded_reg = TWL4030_VAUX3_DEDICATED;
- ded_val = ENABLE_VAUX3_DEDICATED;
- grp_reg = TWL4030_VAUX3_DEV_GRP;
- grp_val = ENABLE_VAUX3_DEV_GRP;
-
- if (omap_rev() > OMAP3430_REV_ES1_0) {
- t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED,
- TWL4030_VPLL2_DEDICATED);
- t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP,
- TWL4030_VPLL2_DEV_GRP);
- }
- } else {
- ded_reg = TWL4030_VAUX2_DEDICATED;
- ded_val = ENABLE_VAUX2_DEDICATED;
- grp_reg = TWL4030_VAUX2_DEV_GRP;
- grp_val = ENABLE_VAUX2_DEV_GRP;
- }
-
- gpio_set_value(enable_gpio, 1);
- gpio_set_value(backlight_gpio, 1);
-
- if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg))
- return -EIO;
- if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg))
- return -EIO;
-
- return 0;
-}
-
-static void sdp2430_panel_disable(struct lcd_panel *panel)
-{
- gpio_set_value(enable_gpio, 0);
- gpio_set_value(backlight_gpio, 0);
- if (omap_rev() > OMAP3430_REV_ES1_0) {
- t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED);
- t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP);
- msleep(4);
- }
-}
-
-static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-struct lcd_panel sdp2430_panel = {
- .name = "sdp2430",
- .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
- OMAP_LCDC_INV_HSYNC,
-
- .bpp = 16,
- .data_lines = 16,
- .x_res = 240,
- .y_res = 320,
- .hsw = 3, /* hsync_len (4) - 1 */
- .hfp = 3, /* right_margin (4) - 1 */
- .hbp = 39, /* left_margin (40) - 1 */
- .vsw = 1, /* vsync_len (2) - 1 */
- .vfp = 2, /* lower_margin */
- .vbp = 7, /* upper_margin (8) - 1 */
-
- .pixel_clock = LCD_PIXCLOCK_MAX,
-
- .init = sdp2430_panel_init,
- .cleanup = sdp2430_panel_cleanup,
- .enable = sdp2430_panel_enable,
- .disable = sdp2430_panel_disable,
- .get_caps = sdp2430_panel_get_caps,
-};
-
-static int sdp2430_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&sdp2430_panel);
- return 0;
-}
-
-static int sdp2430_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int sdp2430_panel_suspend(struct platform_device *pdev,
- pm_message_t mesg)
-{
- return 0;
-}
-
-static int sdp2430_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver sdp2430_panel_driver = {
- .probe = sdp2430_panel_probe,
- .remove = sdp2430_panel_remove,
- .suspend = sdp2430_panel_suspend,
- .resume = sdp2430_panel_resume,
- .driver = {
- .name = "sdp2430_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init sdp2430_panel_drv_init(void)
-{
- return platform_driver_register(&sdp2430_panel_driver);
-}
-
-static void __exit sdp2430_panel_drv_exit(void)
-{
- platform_driver_unregister(&sdp2430_panel_driver);
-}
-
-module_init(sdp2430_panel_drv_init);
-module_exit(sdp2430_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c
deleted file mode 100644
index 4b24f549f9b9..000000000000
--- a/drivers/video/omap/lcd_apollon.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * LCD panel support for the Samsung OMAP2 Apollon board
- *
- * Copyright (C) 2005,2006 Samsung Electronics
- * Author: Kyungmin Park <kyungmin.park@samsung.com>
- *
- * Derived from drivers/video/omap/lcd-h4.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include <asm/gpio.h>
-
-#include "omapfb.h"
-
-/* #define USE_35INCH_LCD 1 */
-
-static int apollon_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- return 0;
-}
-
-static void apollon_panel_cleanup(struct lcd_panel *panel)
-{
-}
-
-static int apollon_panel_enable(struct lcd_panel *panel)
-{
- return 0;
-}
-
-static void apollon_panel_disable(struct lcd_panel *panel)
-{
-}
-
-static unsigned long apollon_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-struct lcd_panel apollon_panel = {
- .name = "apollon",
- .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
- OMAP_LCDC_INV_HSYNC,
-
- .bpp = 16,
- .data_lines = 18,
-#ifdef USE_35INCH_LCD
- .x_res = 240,
- .y_res = 320,
- .hsw = 2,
- .hfp = 3,
- .hbp = 9,
- .vsw = 4,
- .vfp = 3,
- .vbp = 5,
-#else
- .x_res = 480,
- .y_res = 272,
- .hsw = 41,
- .hfp = 2,
- .hbp = 2,
- .vsw = 10,
- .vfp = 2,
- .vbp = 2,
-#endif
- .pixel_clock = 6250,
-
- .init = apollon_panel_init,
- .cleanup = apollon_panel_cleanup,
- .enable = apollon_panel_enable,
- .disable = apollon_panel_disable,
- .get_caps = apollon_panel_get_caps,
-};
-
-static int apollon_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&apollon_panel);
- return 0;
-}
-
-static int apollon_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int apollon_panel_suspend(struct platform_device *pdev,
- pm_message_t mesg)
-{
- return 0;
-}
-
-static int apollon_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver apollon_panel_driver = {
- .probe = apollon_panel_probe,
- .remove = apollon_panel_remove,
- .suspend = apollon_panel_suspend,
- .resume = apollon_panel_resume,
- .driver = {
- .name = "apollon_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init apollon_panel_drv_init(void)
-{
- return platform_driver_register(&apollon_panel_driver);
-}
-
-static void __exit apollon_panel_drv_exit(void)
-{
- platform_driver_unregister(&apollon_panel_driver);
-}
-
-module_init(apollon_panel_drv_init);
-module_exit(apollon_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c
deleted file mode 100644
index 03a06a982750..000000000000
--- a/drivers/video/omap/lcd_h4.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * LCD panel support for the TI OMAP H4 board
- *
- * Copyright (C) 2004 Nokia Corporation
- * Author: Imre Deak <imre.deak@nokia.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/module.h>
-#include <linux/platform_device.h>
-
-#include "omapfb.h"
-
-static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
-{
- return 0;
-}
-
-static void h4_panel_cleanup(struct lcd_panel *panel)
-{
-}
-
-static int h4_panel_enable(struct lcd_panel *panel)
-{
- return 0;
-}
-
-static void h4_panel_disable(struct lcd_panel *panel)
-{
-}
-
-static unsigned long h4_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-static struct lcd_panel h4_panel = {
- .name = "h4",
- .config = OMAP_LCDC_PANEL_TFT,
-
- .bpp = 16,
- .data_lines = 16,
- .x_res = 240,
- .y_res = 320,
- .pixel_clock = 6250,
- .hsw = 15,
- .hfp = 15,
- .hbp = 60,
- .vsw = 1,
- .vfp = 1,
- .vbp = 1,
-
- .init = h4_panel_init,
- .cleanup = h4_panel_cleanup,
- .enable = h4_panel_enable,
- .disable = h4_panel_disable,
- .get_caps = h4_panel_get_caps,
-};
-
-static int h4_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&h4_panel);
- return 0;
-}
-
-static int h4_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- return 0;
-}
-
-static int h4_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver h4_panel_driver = {
- .probe = h4_panel_probe,
- .remove = h4_panel_remove,
- .suspend = h4_panel_suspend,
- .resume = h4_panel_resume,
- .driver = {
- .name = "lcd_h4",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init h4_panel_drv_init(void)
-{
- return platform_driver_register(&h4_panel_driver);
-}
-
-static void __exit h4_panel_drv_cleanup(void)
-{
- platform_driver_unregister(&h4_panel_driver);
-}
-
-module_init(h4_panel_drv_init);
-module_exit(h4_panel_drv_cleanup);
-
diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c
deleted file mode 100644
index 062466402c0e..000000000000
--- a/drivers/video/omap/lcd_ldp.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * LCD panel support for the TI LDP board
- *
- * Copyright (C) 2007 WindRiver
- * Author: Stanley Miao <stanley.miao@windriver.com>
- *
- * Derived from drivers/video/omap/lcd-2430sdp.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/i2c/twl.h>
-
-#include <asm/gpio.h>
-#include <plat/mux.h>
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES)
-#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES)
-
-#define LCD_PANEL_RESET_GPIO 55
-#define LCD_PANEL_QVGA_GPIO 56
-
-#ifdef CONFIG_FB_OMAP_LCD_VGA
-#define LCD_XRES 480
-#define LCD_YRES 640
-#define LCD_PIXCLOCK_MAX 41700
-#else
-#define LCD_XRES 240
-#define LCD_YRES 320
-#define LCD_PIXCLOCK_MAX 185186
-#endif
-
-#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
-#define ENABLE_VAUX2_DEDICATED 0x09
-#define ENABLE_VAUX2_DEV_GRP 0x20
-#define ENABLE_VAUX3_DEDICATED 0x03
-#define ENABLE_VAUX3_DEV_GRP 0x20
-
-#define ENABLE_VPLL2_DEDICATED 0x05
-#define ENABLE_VPLL2_DEV_GRP 0xE0
-#define TWL4030_VPLL2_DEV_GRP 0x33
-#define TWL4030_VPLL2_DEDICATED 0x36
-
-#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v)
-
-
-static int ldp_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- gpio_request(LCD_PANEL_RESET_GPIO, "lcd reset");
- gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga");
- gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd panel");
- gpio_request(LCD_PANEL_BACKLIGHT_GPIO, "lcd backlight");
-
- gpio_direction_output(LCD_PANEL_QVGA_GPIO, 0);
- gpio_direction_output(LCD_PANEL_RESET_GPIO, 0);
- gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0);
- gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0);
-
-#ifdef CONFIG_FB_OMAP_LCD_VGA
- gpio_set_value(LCD_PANEL_QVGA_GPIO, 0);
-#else
- gpio_set_value(LCD_PANEL_QVGA_GPIO, 1);
-#endif
- gpio_set_value(LCD_PANEL_RESET_GPIO, 1);
-
- return 0;
-}
-
-static void ldp_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(LCD_PANEL_BACKLIGHT_GPIO);
- gpio_free(LCD_PANEL_ENABLE_GPIO);
- gpio_free(LCD_PANEL_QVGA_GPIO);
- gpio_free(LCD_PANEL_RESET_GPIO);
-}
-
-static int ldp_panel_enable(struct lcd_panel *panel)
-{
- if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED,
- TWL4030_VPLL2_DEDICATED))
- return -EIO;
- if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP,
- TWL4030_VPLL2_DEV_GRP))
- return -EIO;
-
- gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1);
- gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 1);
-
- if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED,
- TWL4030_VAUX3_DEDICATED))
- return -EIO;
- if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP,
- TWL4030_VAUX3_DEV_GRP))
- return -EIO;
-
- return 0;
-}
-
-static void ldp_panel_disable(struct lcd_panel *panel)
-{
- gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0);
- gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0);
-
- t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED);
- t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP);
- msleep(4);
-}
-
-static unsigned long ldp_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-struct lcd_panel ldp_panel = {
- .name = "ldp",
- .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
- OMAP_LCDC_INV_HSYNC,
-
- .bpp = 16,
- .data_lines = 18,
- .x_res = LCD_XRES,
- .y_res = LCD_YRES,
- .hsw = 3, /* hsync_len (4) - 1 */
- .hfp = 3, /* right_margin (4) - 1 */
- .hbp = 39, /* left_margin (40) - 1 */
- .vsw = 1, /* vsync_len (2) - 1 */
- .vfp = 2, /* lower_margin */
- .vbp = 7, /* upper_margin (8) - 1 */
-
- .pixel_clock = LCD_PIXCLOCK_MAX,
-
- .init = ldp_panel_init,
- .cleanup = ldp_panel_cleanup,
- .enable = ldp_panel_enable,
- .disable = ldp_panel_disable,
- .get_caps = ldp_panel_get_caps,
-};
-
-static int ldp_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&ldp_panel);
- return 0;
-}
-
-static int ldp_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int ldp_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- return 0;
-}
-
-static int ldp_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver ldp_panel_driver = {
- .probe = ldp_panel_probe,
- .remove = ldp_panel_remove,
- .suspend = ldp_panel_suspend,
- .resume = ldp_panel_resume,
- .driver = {
- .name = "ldp_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init ldp_panel_drv_init(void)
-{
- return platform_driver_register(&ldp_panel_driver);
-}
-
-static void __exit ldp_panel_drv_exit(void)
-{
- platform_driver_unregister(&ldp_panel_driver);
-}
-
-module_init(ldp_panel_drv_init);
-module_exit(ldp_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c
deleted file mode 100644
index d7c6c3e0afc6..000000000000
--- a/drivers/video/omap/lcd_omap3beagle.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * LCD panel support for the TI OMAP3 Beagle board
- *
- * Author: Koen Kooi <koen@openembedded.org>
- *
- * Derived from drivers/video/omap/lcd-omap3evm.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/i2c/twl.h>
-
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define LCD_PANEL_ENABLE_GPIO 170
-
-static int omap3beagle_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable");
- return 0;
-}
-
-static void omap3beagle_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(LCD_PANEL_ENABLE_GPIO);
-}
-
-static int omap3beagle_panel_enable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
- return 0;
-}
-
-static void omap3beagle_panel_disable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
-}
-
-static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-struct lcd_panel omap3beagle_panel = {
- .name = "omap3beagle",
- .config = OMAP_LCDC_PANEL_TFT,
-
- .bpp = 16,
- .data_lines = 24,
- .x_res = 1024,
- .y_res = 768,
- .hsw = 3, /* hsync_len (4) - 1 */
- .hfp = 3, /* right_margin (4) - 1 */
- .hbp = 39, /* left_margin (40) - 1 */
- .vsw = 1, /* vsync_len (2) - 1 */
- .vfp = 2, /* lower_margin */
- .vbp = 7, /* upper_margin (8) - 1 */
-
- .pixel_clock = 64000,
-
- .init = omap3beagle_panel_init,
- .cleanup = omap3beagle_panel_cleanup,
- .enable = omap3beagle_panel_enable,
- .disable = omap3beagle_panel_disable,
- .get_caps = omap3beagle_panel_get_caps,
-};
-
-static int omap3beagle_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&omap3beagle_panel);
- return 0;
-}
-
-static int omap3beagle_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int omap3beagle_panel_suspend(struct platform_device *pdev,
- pm_message_t mesg)
-{
- return 0;
-}
-
-static int omap3beagle_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver omap3beagle_panel_driver = {
- .probe = omap3beagle_panel_probe,
- .remove = omap3beagle_panel_remove,
- .suspend = omap3beagle_panel_suspend,
- .resume = omap3beagle_panel_resume,
- .driver = {
- .name = "omap3beagle_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init omap3beagle_panel_drv_init(void)
-{
- return platform_driver_register(&omap3beagle_panel_driver);
-}
-
-static void __exit omap3beagle_panel_drv_exit(void)
-{
- platform_driver_unregister(&omap3beagle_panel_driver);
-}
-
-module_init(omap3beagle_panel_drv_init);
-module_exit(omap3beagle_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c
deleted file mode 100644
index 06840da0b094..000000000000
--- a/drivers/video/omap/lcd_omap3evm.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * LCD panel support for the TI OMAP3 EVM board
- *
- * Author: Steve Sakoman <steve@sakoman.com>
- *
- * Derived from drivers/video/omap/lcd-apollon.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/i2c/twl.h>
-
-#include <plat/mux.h>
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define LCD_PANEL_ENABLE_GPIO 153
-#define LCD_PANEL_LR 2
-#define LCD_PANEL_UD 3
-#define LCD_PANEL_INI 152
-#define LCD_PANEL_QVGA 154
-#define LCD_PANEL_RESB 155
-
-#define ENABLE_VDAC_DEDICATED 0x03
-#define ENABLE_VDAC_DEV_GRP 0x20
-#define ENABLE_VPLL2_DEDICATED 0x05
-#define ENABLE_VPLL2_DEV_GRP 0xE0
-
-#define TWL_LED_LEDEN 0x00
-#define TWL_PWMA_PWMAON 0x00
-#define TWL_PWMA_PWMAOFF 0x01
-
-static unsigned int bklight_level;
-
-static int omap3evm_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- gpio_request(LCD_PANEL_LR, "LCD lr");
- gpio_request(LCD_PANEL_UD, "LCD ud");
- gpio_request(LCD_PANEL_INI, "LCD ini");
- gpio_request(LCD_PANEL_RESB, "LCD resb");
- gpio_request(LCD_PANEL_QVGA, "LCD qvga");
-
- gpio_direction_output(LCD_PANEL_RESB, 1);
- gpio_direction_output(LCD_PANEL_INI, 1);
- gpio_direction_output(LCD_PANEL_QVGA, 0);
- gpio_direction_output(LCD_PANEL_LR, 1);
- gpio_direction_output(LCD_PANEL_UD, 1);
-
- twl_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
- bklight_level = 100;
-
- return 0;
-}
-
-static void omap3evm_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(LCD_PANEL_QVGA);
- gpio_free(LCD_PANEL_RESB);
- gpio_free(LCD_PANEL_INI);
- gpio_free(LCD_PANEL_UD);
- gpio_free(LCD_PANEL_LR);
-}
-
-static int omap3evm_panel_enable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
- return 0;
-}
-
-static void omap3evm_panel_disable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
-}
-
-static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-static int omap3evm_bklight_setlevel(struct lcd_panel *panel,
- unsigned int level)
-{
- u8 c;
- if ((level >= 0) && (level <= 100)) {
- c = (125 * (100 - level)) / 100 + 2;
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
- bklight_level = level;
- }
- return 0;
-}
-
-static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel)
-{
- return bklight_level;
-}
-
-static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel)
-{
- return 100;
-}
-
-struct lcd_panel omap3evm_panel = {
- .name = "omap3evm",
- .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
- OMAP_LCDC_INV_HSYNC,
-
- .bpp = 16,
- .data_lines = 18,
- .x_res = 480,
- .y_res = 640,
- .hsw = 3, /* hsync_len (4) - 1 */
- .hfp = 3, /* right_margin (4) - 1 */
- .hbp = 39, /* left_margin (40) - 1 */
- .vsw = 1, /* vsync_len (2) - 1 */
- .vfp = 2, /* lower_margin */
- .vbp = 7, /* upper_margin (8) - 1 */
-
- .pixel_clock = 26000,
-
- .init = omap3evm_panel_init,
- .cleanup = omap3evm_panel_cleanup,
- .enable = omap3evm_panel_enable,
- .disable = omap3evm_panel_disable,
- .get_caps = omap3evm_panel_get_caps,
- .set_bklight_level = omap3evm_bklight_setlevel,
- .get_bklight_level = omap3evm_bklight_getlevel,
- .get_bklight_max = omap3evm_bklight_getmaxlevel,
-};
-
-static int omap3evm_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&omap3evm_panel);
- return 0;
-}
-
-static int omap3evm_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int omap3evm_panel_suspend(struct platform_device *pdev,
- pm_message_t mesg)
-{
- return 0;
-}
-
-static int omap3evm_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver omap3evm_panel_driver = {
- .probe = omap3evm_panel_probe,
- .remove = omap3evm_panel_remove,
- .suspend = omap3evm_panel_suspend,
- .resume = omap3evm_panel_resume,
- .driver = {
- .name = "omap3evm_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init omap3evm_panel_drv_init(void)
-{
- return platform_driver_register(&omap3evm_panel_driver);
-}
-
-static void __exit omap3evm_panel_drv_exit(void)
-{
- platform_driver_unregister(&omap3evm_panel_driver);
-}
-
-module_init(omap3evm_panel_drv_init);
-module_exit(omap3evm_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c
deleted file mode 100644
index b8fd5b2ec29c..000000000000
--- a/drivers/video/omap/lcd_overo.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * LCD panel support for the Gumstix Overo
- *
- * Author: Steve Sakoman <steve@sakoman.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/module.h>
-#include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
-
-#include <asm/gpio.h>
-#include <plat/mux.h>
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define LCD_ENABLE 144
-
-static int overo_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- if ((gpio_request(LCD_ENABLE, "LCD_ENABLE") == 0) &&
- (gpio_direction_output(LCD_ENABLE, 1) == 0))
- gpio_export(LCD_ENABLE, 0);
- else
- printk(KERN_ERR "could not obtain gpio for LCD_ENABLE\n");
-
- return 0;
-}
-
-static void overo_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(LCD_ENABLE);
-}
-
-static int overo_panel_enable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_ENABLE, 1);
- return 0;
-}
-
-static void overo_panel_disable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_ENABLE, 0);
-}
-
-static unsigned long overo_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-struct lcd_panel overo_panel = {
- .name = "overo",
- .config = OMAP_LCDC_PANEL_TFT,
- .bpp = 16,
- .data_lines = 24,
-
-#if defined CONFIG_FB_OMAP_031M3R
-
- /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
- .x_res = 640,
- .y_res = 480,
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
- .vfp = 3,
- .vsw = 4,
- .vbp = 7,
- .pixel_clock = 23500,
-
-#elif defined CONFIG_FB_OMAP_048M3R
-
- /* 800 x 600 @ 60 Hz Reduced blanking VESA CVT 0.48M3-R */
- .x_res = 800,
- .y_res = 600,
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
- .vfp = 3,
- .vsw = 4,
- .vbp = 11,
- .pixel_clock = 35500,
-
-#elif defined CONFIG_FB_OMAP_079M3R
-
- /* 1024 x 768 @ 60 Hz Reduced blanking VESA CVT 0.79M3-R */
- .x_res = 1024,
- .y_res = 768,
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
- .vfp = 3,
- .vsw = 4,
- .vbp = 15,
- .pixel_clock = 56000,
-
-#elif defined CONFIG_FB_OMAP_092M9R
-
- /* 1280 x 720 @ 60 Hz Reduced blanking VESA CVT 0.92M9-R */
- .x_res = 1280,
- .y_res = 720,
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
- .vfp = 3,
- .vsw = 5,
- .vbp = 13,
- .pixel_clock = 64000,
-
-#else
-
- /* use 640 x 480 if no config option */
- /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
- .x_res = 640,
- .y_res = 480,
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
- .vfp = 3,
- .vsw = 4,
- .vbp = 7,
- .pixel_clock = 23500,
-
-#endif
-
- .init = overo_panel_init,
- .cleanup = overo_panel_cleanup,
- .enable = overo_panel_enable,
- .disable = overo_panel_disable,
- .get_caps = overo_panel_get_caps,
-};
-
-static int overo_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&overo_panel);
- return 0;
-}
-
-static int overo_panel_remove(struct platform_device *pdev)
-{
- /* omapfb does not have unregister_panel */
- return 0;
-}
-
-static struct platform_driver overo_panel_driver = {
- .probe = overo_panel_probe,
- .remove = overo_panel_remove,
- .driver = {
- .name = "overo_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init overo_panel_drv_init(void)
-{
- return platform_driver_register(&overo_panel_driver);
-}
-
-static void __exit overo_panel_drv_exit(void)
-{
- platform_driver_unregister(&overo_panel_driver);
-}
-
-module_init(overo_panel_drv_init);
-module_exit(overo_panel_drv_exit);
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index 609a28073178..8d8e1fe1901c 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -10,6 +10,13 @@ config PANEL_GENERIC_DPI
Supports LCD Panel used in TI SDP3430 and EVM boards,
OMAP3517 EVM boards and CM-T35.
+config PANEL_DVI
+ tristate "DVI output"
+ depends on OMAP2_DSS_DPI
+ help
+ Driver for external monitors, connected via DVI. The driver uses i2c
+ to read EDID information from the monitor.
+
config PANEL_LGPHILIPS_LB035Q02
tristate "LG.Philips LB035Q02 LCD Panel"
depends on OMAP2_DSS_DPI && SPI
@@ -19,20 +26,30 @@ config PANEL_LGPHILIPS_LB035Q02
config PANEL_SHARP_LS037V7DW01
tristate "Sharp LS037V7DW01 LCD Panel"
depends on OMAP2_DSS_DPI
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
help
LCD Panel used in TI's SDP3430 and EVM boards
config PANEL_NEC_NL8048HL11_01B
tristate "NEC NL8048HL11-01B Panel"
depends on OMAP2_DSS_DPI
+ depends on SPI
+ depends on BACKLIGHT_CLASS_DEVICE
help
This NEC NL8048HL11-01B panel is TFT LCD
used in the Zoom2/3/3630 sdp boards.
+config PANEL_PICODLP
+ tristate "TI PICO DLP mini-projector"
+ depends on OMAP2_DSS && I2C
+ help
+ A mini-projector used in TI's SDP4430 and EVM boards
+ For more info please visit http://www.dlp.com/projector/
+
config PANEL_TAAL
tristate "Taal DSI Panel"
depends on OMAP2_DSS_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
help
Taal DSI command mode panel from TPO.
@@ -45,7 +62,14 @@ config PANEL_TPO_TD043MTEA1
config PANEL_ACX565AKM
tristate "ACX565AKM Panel"
depends on OMAP2_DSS_SDI && SPI
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N900
+
+config PANEL_N8X0
+ tristate "N8X0 Panel"
+ depends on OMAP2_DSS_RFBI && SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ This is the LCD panel used on Nokia N8x0
endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index 0f601ab3abf4..fbfafc6eebb4 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -1,8 +1,11 @@
obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o
+obj-$(CONFIG_PANEL_DVI) += panel-dvi.o
obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
+obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
+obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o
diff --git a/drivers/video/omap2/displays/panel-dvi.c b/drivers/video/omap2/displays/panel-dvi.c
new file mode 100644
index 000000000000..03eb14af33e0
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-dvi.c
@@ -0,0 +1,363 @@
+/*
+ * DVI output support
+ *
+ * Copyright (C) 2011 Texas Instruments Inc
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <video/omapdss.h>
+#include <linux/i2c.h>
+#include <drm/drm_edid.h>
+
+#include <video/omap-panel-dvi.h>
+
+static const struct omap_video_timings panel_dvi_default_timings = {
+ .x_res = 640,
+ .y_res = 480,
+
+ .pixel_clock = 23500,
+
+ .hfp = 48,
+ .hsw = 32,
+ .hbp = 80,
+
+ .vfp = 3,
+ .vsw = 4,
+ .vbp = 7,
+};
+
+struct panel_drv_data {
+ struct omap_dss_device *dssdev;
+
+ struct mutex lock;
+};
+
+static inline struct panel_dvi_platform_data
+*get_pdata(const struct omap_dss_device *dssdev)
+{
+ return dssdev->data;
+}
+
+static int panel_dvi_power_on(struct omap_dss_device *dssdev)
+{
+ struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
+ int r;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
+ r = omapdss_dpi_display_enable(dssdev);
+ if (r)
+ goto err0;
+
+ if (pdata->platform_enable) {
+ r = pdata->platform_enable(dssdev);
+ if (r)
+ goto err1;
+ }
+
+ return 0;
+err1:
+ omapdss_dpi_display_disable(dssdev);
+err0:
+ return r;
+}
+
+static void panel_dvi_power_off(struct omap_dss_device *dssdev)
+{
+ struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
+ if (pdata->platform_disable)
+ pdata->platform_disable(dssdev);
+
+ omapdss_dpi_display_disable(dssdev);
+}
+
+static int panel_dvi_probe(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata;
+
+ ddata = kzalloc(sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ dssdev->panel.timings = panel_dvi_default_timings;
+ dssdev->panel.config = OMAP_DSS_LCD_TFT;
+
+ ddata->dssdev = dssdev;
+ mutex_init(&ddata->lock);
+
+ dev_set_drvdata(&dssdev->dev, ddata);
+
+ return 0;
+}
+
+static void __exit panel_dvi_remove(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&ddata->lock);
+
+ dev_set_drvdata(&dssdev->dev, NULL);
+
+ mutex_unlock(&ddata->lock);
+
+ kfree(ddata);
+}
+
+static int panel_dvi_enable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&ddata->lock);
+
+ r = panel_dvi_power_on(dssdev);
+ if (r == 0)
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ mutex_unlock(&ddata->lock);
+
+ return r;
+}
+
+static void panel_dvi_disable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&ddata->lock);
+
+ panel_dvi_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ mutex_unlock(&ddata->lock);
+}
+
+static int panel_dvi_suspend(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&ddata->lock);
+
+ panel_dvi_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static int panel_dvi_resume(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&ddata->lock);
+
+ r = panel_dvi_power_on(dssdev);
+ if (r == 0)
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ mutex_unlock(&ddata->lock);
+
+ return r;
+}
+
+static void panel_dvi_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&ddata->lock);
+ dpi_set_timings(dssdev, timings);
+ mutex_unlock(&ddata->lock);
+}
+
+static void panel_dvi_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&ddata->lock);
+ *timings = dssdev->panel.timings;
+ mutex_unlock(&ddata->lock);
+}
+
+static int panel_dvi_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&ddata->lock);
+ r = dpi_check_timings(dssdev, timings);
+ mutex_unlock(&ddata->lock);
+
+ return r;
+}
+
+
+static int panel_dvi_ddc_read(struct i2c_adapter *adapter,
+ unsigned char *buf, u16 count, u8 offset)
+{
+ int r, retries;
+
+ for (retries = 3; retries > 0; retries--) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &offset,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = count,
+ .buf = buf,
+ }
+ };
+
+ r = i2c_transfer(adapter, msgs, 2);
+ if (r == 2)
+ return 0;
+
+ if (r != -EAGAIN)
+ break;
+ }
+
+ return r < 0 ? r : -EIO;
+}
+
+static int panel_dvi_read_edid(struct omap_dss_device *dssdev,
+ u8 *edid, int len)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+ struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
+ struct i2c_adapter *adapter;
+ int r, l, bytes_read;
+
+ mutex_lock(&ddata->lock);
+
+ if (pdata->i2c_bus_num == 0) {
+ r = -ENODEV;
+ goto err;
+ }
+
+ adapter = i2c_get_adapter(pdata->i2c_bus_num);
+ if (!adapter) {
+ dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
+ pdata->i2c_bus_num);
+ r = -EINVAL;
+ goto err;
+ }
+
+ l = min(EDID_LENGTH, len);
+ r = panel_dvi_ddc_read(adapter, edid, l, 0);
+ if (r)
+ goto err;
+
+ bytes_read = l;
+
+ /* if there are extensions, read second block */
+ if (len > EDID_LENGTH && edid[0x7e] > 0) {
+ l = min(EDID_LENGTH, len - EDID_LENGTH);
+
+ r = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH,
+ l, EDID_LENGTH);
+ if (r)
+ goto err;
+
+ bytes_read += l;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return bytes_read;
+
+err:
+ mutex_unlock(&ddata->lock);
+ return r;
+}
+
+static bool panel_dvi_detect(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+ struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
+ struct i2c_adapter *adapter;
+ unsigned char out;
+ int r;
+
+ mutex_lock(&ddata->lock);
+
+ if (pdata->i2c_bus_num == 0)
+ goto out;
+
+ adapter = i2c_get_adapter(pdata->i2c_bus_num);
+ if (!adapter)
+ goto out;
+
+ r = panel_dvi_ddc_read(adapter, &out, 1, 0);
+
+ mutex_unlock(&ddata->lock);
+
+ return r == 0;
+
+out:
+ mutex_unlock(&ddata->lock);
+ return true;
+}
+
+static struct omap_dss_driver panel_dvi_driver = {
+ .probe = panel_dvi_probe,
+ .remove = __exit_p(panel_dvi_remove),
+
+ .enable = panel_dvi_enable,
+ .disable = panel_dvi_disable,
+ .suspend = panel_dvi_suspend,
+ .resume = panel_dvi_resume,
+
+ .set_timings = panel_dvi_set_timings,
+ .get_timings = panel_dvi_get_timings,
+ .check_timings = panel_dvi_check_timings,
+
+ .read_edid = panel_dvi_read_edid,
+ .detect = panel_dvi_detect,
+
+ .driver = {
+ .name = "dvi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init panel_dvi_init(void)
+{
+ return omap_dss_register_driver(&panel_dvi_driver);
+}
+
+static void __exit panel_dvi_exit(void)
+{
+ omap_dss_unregister_driver(&panel_dvi_driver);
+}
+
+module_init(panel_dvi_init);
+module_exit(panel_dvi_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index 9c90f75653fb..519c47d2057f 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -58,30 +58,6 @@ struct panel_config {
/* Panel configurations */
static struct panel_config generic_dpi_panels[] = {
- /* Generic Panel */
- {
- {
- .x_res = 640,
- .y_res = 480,
-
- .pixel_clock = 23500,
-
- .hfp = 48,
- .hsw = 32,
- .hbp = 80,
-
- .vfp = 3,
- .vsw = 4,
- .vbp = 7,
- },
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT,
- .power_on_delay = 0,
- .power_off_delay = 0,
- .name = "generic",
- },
-
/* Sharp LQ043T1DG01 */
{
{
@@ -232,6 +208,95 @@ static struct panel_config generic_dpi_panels[] = {
.power_off_delay = 0,
.name = "powertip_ph480272t",
},
+
+ /* Innolux AT070TN83 */
+ {
+ {
+ .x_res = 800,
+ .y_res = 480,
+
+ .pixel_clock = 40000,
+
+ .hsw = 48,
+ .hfp = 1,
+ .hbp = 1,
+
+ .vsw = 3,
+ .vfp = 12,
+ .vbp = 25,
+ },
+ .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",
+ },
+
+ /* NEC NL2432DR22-11B */
+ {
+ {
+ .x_res = 240,
+ .y_res = 320,
+
+ .pixel_clock = 5400,
+
+ .hsw = 3,
+ .hfp = 3,
+ .hbp = 39,
+
+ .vsw = 1,
+ .vfp = 2,
+ .vbp = 7,
+ },
+ .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+ OMAP_DSS_LCD_IHS,
+ .name = "nec_nl2432dr22-11b",
+ },
+
+ /* Unknown panel used in OMAP H4 */
+ {
+ {
+ .x_res = 240,
+ .y_res = 320,
+
+ .pixel_clock = 6250,
+
+ .hsw = 15,
+ .hfp = 15,
+ .hbp = 60,
+
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 1,
+ },
+ .config = OMAP_DSS_LCD_TFT,
+
+ .name = "h4",
+ },
+
+ /* Unknown panel used in Samsung OMAP2 Apollon */
+ {
+ {
+ .x_res = 480,
+ .y_res = 272,
+
+ .pixel_clock = 6250,
+
+ .hsw = 41,
+ .hfp = 2,
+ .hbp = 2,
+
+ .vsw = 10,
+ .vfp = 2,
+ .vbp = 2,
+ },
+ .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+ OMAP_DSS_LCD_IHS,
+
+ .name = "apollon",
+ },
};
struct panel_drv_data {
diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c
new file mode 100644
index 000000000000..150e8bae35a1
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-n8x0.c
@@ -0,0 +1,747 @@
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-n8x0.h>
+
+#define BLIZZARD_REV_CODE 0x00
+#define BLIZZARD_CONFIG 0x02
+#define BLIZZARD_PLL_DIV 0x04
+#define BLIZZARD_PLL_LOCK_RANGE 0x06
+#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08
+#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a
+#define BLIZZARD_PLL_MODE 0x0c
+#define BLIZZARD_CLK_SRC 0x0e
+#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10
+#define BLIZZARD_MEM_BANK0_STATUS 0x14
+#define BLIZZARD_PANEL_CONFIGURATION 0x28
+#define BLIZZARD_HDISP 0x2a
+#define BLIZZARD_HNDP 0x2c
+#define BLIZZARD_VDISP0 0x2e
+#define BLIZZARD_VDISP1 0x30
+#define BLIZZARD_VNDP 0x32
+#define BLIZZARD_HSW 0x34
+#define BLIZZARD_VSW 0x38
+#define BLIZZARD_DISPLAY_MODE 0x68
+#define BLIZZARD_INPUT_WIN_X_START_0 0x6c
+#define BLIZZARD_DATA_SOURCE_SELECT 0x8e
+#define BLIZZARD_DISP_MEM_DATA_PORT 0x90
+#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92
+#define BLIZZARD_POWER_SAVE 0xE6
+#define BLIZZARD_NDISP_CTRL_STATUS 0xE8
+
+/* Data source select */
+/* For S1D13745 */
+#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00
+#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01
+#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04
+#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05
+/* For S1D13744 */
+#define BLIZZARD_SRC_WRITE_LCD 0x00
+#define BLIZZARD_SRC_BLT_LCD 0x06
+
+#define BLIZZARD_COLOR_RGB565 0x01
+#define BLIZZARD_COLOR_YUV420 0x09
+
+#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */
+#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */
+
+#define MIPID_CMD_READ_DISP_ID 0x04
+#define MIPID_CMD_READ_RED 0x06
+#define MIPID_CMD_READ_GREEN 0x07
+#define MIPID_CMD_READ_BLUE 0x08
+#define MIPID_CMD_READ_DISP_STATUS 0x09
+#define MIPID_CMD_RDDSDR 0x0F
+#define MIPID_CMD_SLEEP_IN 0x10
+#define MIPID_CMD_SLEEP_OUT 0x11
+#define MIPID_CMD_DISP_OFF 0x28
+#define MIPID_CMD_DISP_ON 0x29
+
+static struct panel_drv_data {
+ struct mutex lock;
+
+ struct omap_dss_device *dssdev;
+ struct spi_device *spidev;
+ struct backlight_device *bldev;
+
+ int blizzard_ver;
+} s_drv_data;
+
+
+static inline
+struct panel_n8x0_data *get_board_data(const struct omap_dss_device *dssdev)
+{
+ return dssdev->data;
+}
+
+static inline
+struct panel_drv_data *get_drv_data(const struct omap_dss_device *dssdev)
+{
+ return &s_drv_data;
+}
+
+
+static inline void blizzard_cmd(u8 cmd)
+{
+ omap_rfbi_write_command(&cmd, 1);
+}
+
+static inline void blizzard_write(u8 cmd, const u8 *buf, int len)
+{
+ omap_rfbi_write_command(&cmd, 1);
+ omap_rfbi_write_data(buf, len);
+}
+
+static inline void blizzard_read(u8 cmd, u8 *buf, int len)
+{
+ omap_rfbi_write_command(&cmd, 1);
+ omap_rfbi_read_data(buf, len);
+}
+
+static u8 blizzard_read_reg(u8 cmd)
+{
+ u8 data;
+ blizzard_read(cmd, &data, 1);
+ return data;
+}
+
+static void blizzard_ctrl_setup_update(struct omap_dss_device *dssdev,
+ int x, int y, int w, int h)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ u8 tmp[18];
+ int x_end, y_end;
+
+ x_end = x + w - 1;
+ y_end = y + h - 1;
+
+ tmp[0] = x;
+ tmp[1] = x >> 8;
+ tmp[2] = y;
+ tmp[3] = y >> 8;
+ tmp[4] = x_end;
+ tmp[5] = x_end >> 8;
+ tmp[6] = y_end;
+ tmp[7] = y_end >> 8;
+
+ /* scaling? */
+ tmp[8] = x;
+ tmp[9] = x >> 8;
+ tmp[10] = y;
+ tmp[11] = y >> 8;
+ tmp[12] = x_end;
+ tmp[13] = x_end >> 8;
+ tmp[14] = y_end;
+ tmp[15] = y_end >> 8;
+
+ tmp[16] = BLIZZARD_COLOR_RGB565;
+
+ if (ddata->blizzard_ver == BLIZZARD_VERSION_S1D13745)
+ tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
+ else
+ tmp[17] = ddata->blizzard_ver == BLIZZARD_VERSION_S1D13744 ?
+ BLIZZARD_SRC_WRITE_LCD :
+ BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
+
+ omap_rfbi_configure(dssdev, 16, 8);
+
+ blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18);
+
+ omap_rfbi_configure(dssdev, 16, 16);
+}
+
+static void mipid_transfer(struct spi_device *spi, int cmd, const u8 *wbuf,
+ int wlen, u8 *rbuf, int rlen)
+{
+ struct spi_message m;
+ struct spi_transfer *x, xfer[4];
+ u16 w;
+ int r;
+
+ spi_message_init(&m);
+
+ memset(xfer, 0, sizeof(xfer));
+ x = &xfer[0];
+
+ cmd &= 0xff;
+ x->tx_buf = &cmd;
+ x->bits_per_word = 9;
+ x->len = 2;
+ spi_message_add_tail(x, &m);
+
+ if (wlen) {
+ x++;
+ x->tx_buf = wbuf;
+ x->len = wlen;
+ x->bits_per_word = 9;
+ spi_message_add_tail(x, &m);
+ }
+
+ if (rlen) {
+ x++;
+ x->rx_buf = &w;
+ x->len = 1;
+ spi_message_add_tail(x, &m);
+
+ if (rlen > 1) {
+ /* Arrange for the extra clock before the first
+ * data bit.
+ */
+ x->bits_per_word = 9;
+ x->len = 2;
+
+ x++;
+ x->rx_buf = &rbuf[1];
+ x->len = rlen - 1;
+ spi_message_add_tail(x, &m);
+ }
+ }
+
+ r = spi_sync(spi, &m);
+ if (r < 0)
+ dev_dbg(&spi->dev, "spi_sync %d\n", r);
+
+ if (rlen)
+ rbuf[0] = w & 0xff;
+}
+
+static inline void mipid_cmd(struct spi_device *spi, int cmd)
+{
+ mipid_transfer(spi, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void mipid_write(struct spi_device *spi,
+ int reg, const u8 *buf, int len)
+{
+ mipid_transfer(spi, reg, buf, len, NULL, 0);
+}
+
+static inline void mipid_read(struct spi_device *spi,
+ int reg, u8 *buf, int len)
+{
+ mipid_transfer(spi, reg, NULL, 0, buf, len);
+}
+
+static void set_data_lines(struct spi_device *spi, int data_lines)
+{
+ u16 par;
+
+ switch (data_lines) {
+ case 16:
+ par = 0x150;
+ break;
+ case 18:
+ par = 0x160;
+ break;
+ case 24:
+ par = 0x170;
+ break;
+ }
+
+ mipid_write(spi, 0x3a, (u8 *)&par, 2);
+}
+
+static void send_init_string(struct spi_device *spi)
+{
+ u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
+ mipid_write(spi, 0xc2, (u8 *)initpar, sizeof(initpar));
+}
+
+static void send_display_on(struct spi_device *spi)
+{
+ mipid_cmd(spi, MIPID_CMD_DISP_ON);
+}
+
+static void send_display_off(struct spi_device *spi)
+{
+ mipid_cmd(spi, MIPID_CMD_DISP_OFF);
+}
+
+static void send_sleep_out(struct spi_device *spi)
+{
+ mipid_cmd(spi, MIPID_CMD_SLEEP_OUT);
+ msleep(120);
+}
+
+static void send_sleep_in(struct spi_device *spi)
+{
+ mipid_cmd(spi, MIPID_CMD_SLEEP_IN);
+ msleep(50);
+}
+
+static int n8x0_panel_power_on(struct omap_dss_device *dssdev)
+{
+ int r;
+ struct panel_n8x0_data *bdata = get_board_data(dssdev);
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ struct spi_device *spi = ddata->spidev;
+ u8 rev, conf;
+ u8 display_id[3];
+ const char *panel_name;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
+ gpio_direction_output(bdata->ctrl_pwrdown, 1);
+
+ if (bdata->platform_enable) {
+ r = bdata->platform_enable(dssdev);
+ if (r)
+ goto err_plat_en;
+ }
+
+ r = omapdss_rfbi_display_enable(dssdev);
+ if (r)
+ goto err_rfbi_en;
+
+ rev = blizzard_read_reg(BLIZZARD_REV_CODE);
+ conf = blizzard_read_reg(BLIZZARD_CONFIG);
+
+ switch (rev & 0xfc) {
+ case 0x9c:
+ ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744;
+ dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d "
+ "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
+ break;
+ case 0xa4:
+ ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745;
+ dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d "
+ "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
+ break;
+ default:
+ dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev);
+ r = -ENODEV;
+ goto err_inv_chip;
+ }
+
+ /* panel */
+
+ gpio_direction_output(bdata->panel_reset, 1);
+
+ mipid_read(spi, MIPID_CMD_READ_DISP_ID, display_id, 3);
+ dev_dbg(&spi->dev, "MIPI display ID: %02x%02x%02x\n",
+ display_id[0], display_id[1], display_id[2]);
+
+ switch (display_id[0]) {
+ case 0x45:
+ panel_name = "lph8923";
+ break;
+ case 0x83:
+ panel_name = "ls041y3";
+ break;
+ default:
+ dev_err(&dssdev->dev, "invalid display ID 0x%x\n",
+ display_id[0]);
+ r = -ENODEV;
+ goto err_inv_panel;
+ }
+
+ dev_info(&dssdev->dev, "%s rev %02x LCD detected\n",
+ panel_name, display_id[1]);
+
+ send_sleep_out(spi);
+ send_init_string(spi);
+ set_data_lines(spi, 24);
+ send_display_on(spi);
+
+ return 0;
+
+err_inv_panel:
+ /*
+ * HACK: we should turn off the panel here, but there is some problem
+ * with the initialization sequence, and we fail to init the panel if we
+ * have turned it off
+ */
+ /* gpio_direction_output(bdata->panel_reset, 0); */
+err_inv_chip:
+ omapdss_rfbi_display_disable(dssdev);
+err_rfbi_en:
+ if (bdata->platform_disable)
+ bdata->platform_disable(dssdev);
+err_plat_en:
+ gpio_direction_output(bdata->ctrl_pwrdown, 0);
+ return r;
+}
+
+static void n8x0_panel_power_off(struct omap_dss_device *dssdev)
+{
+ struct panel_n8x0_data *bdata = get_board_data(dssdev);
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ struct spi_device *spi = ddata->spidev;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
+ send_display_off(spi);
+ send_sleep_in(spi);
+
+ if (bdata->platform_disable)
+ bdata->platform_disable(dssdev);
+
+ /*
+ * HACK: we should turn off the panel here, but there is some problem
+ * with the initialization sequence, and we fail to init the panel if we
+ * have turned it off
+ */
+ /* gpio_direction_output(bdata->panel_reset, 0); */
+ gpio_direction_output(bdata->ctrl_pwrdown, 0);
+ omapdss_rfbi_display_disable(dssdev);
+}
+
+static const struct rfbi_timings n8x0_panel_timings = {
+ .cs_on_time = 0,
+
+ .we_on_time = 9000,
+ .we_off_time = 18000,
+ .we_cycle_time = 36000,
+
+ .re_on_time = 9000,
+ .re_off_time = 27000,
+ .re_cycle_time = 36000,
+
+ .access_time = 27000,
+ .cs_off_time = 36000,
+
+ .cs_pulse_width = 0,
+};
+
+static int n8x0_bl_update_status(struct backlight_device *dev)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
+ struct panel_n8x0_data *bdata = get_board_data(dssdev);
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ int r;
+ int level;
+
+ mutex_lock(&ddata->lock);
+
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ level = dev->props.brightness;
+ else
+ level = 0;
+
+ dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
+
+ if (!bdata->set_backlight)
+ r = -EINVAL;
+ else
+ r = bdata->set_backlight(dssdev, level);
+
+ mutex_unlock(&ddata->lock);
+
+ return r;
+}
+
+static int n8x0_bl_get_intensity(struct backlight_device *dev)
+{
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ return dev->props.brightness;
+
+ return 0;
+}
+
+static const struct backlight_ops n8x0_bl_ops = {
+ .get_brightness = n8x0_bl_get_intensity,
+ .update_status = n8x0_bl_update_status,
+};
+
+static int n8x0_panel_probe(struct omap_dss_device *dssdev)
+{
+ struct panel_n8x0_data *bdata = get_board_data(dssdev);
+ struct panel_drv_data *ddata;
+ struct backlight_device *bldev;
+ struct backlight_properties props;
+ int r;
+
+ dev_dbg(&dssdev->dev, "probe\n");
+
+ if (!bdata)
+ return -EINVAL;
+
+ s_drv_data.dssdev = dssdev;
+
+ ddata = &s_drv_data;
+
+ 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;
+ dssdev->ctrl.rfbi_timings = n8x0_panel_timings;
+
+ memset(&props, 0, sizeof(props));
+ props.max_brightness = 127;
+ props.type = BACKLIGHT_PLATFORM;
+ bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev,
+ dssdev, &n8x0_bl_ops, &props);
+ if (IS_ERR(bldev)) {
+ r = PTR_ERR(bldev);
+ dev_err(&dssdev->dev, "register backlight failed\n");
+ return r;
+ }
+
+ ddata->bldev = bldev;
+
+ bldev->props.fb_blank = FB_BLANK_UNBLANK;
+ bldev->props.power = FB_BLANK_UNBLANK;
+ bldev->props.brightness = 127;
+
+ n8x0_bl_update_status(bldev);
+
+ return 0;
+}
+
+static void n8x0_panel_remove(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ struct backlight_device *bldev;
+
+ dev_dbg(&dssdev->dev, "remove\n");
+
+ bldev = ddata->bldev;
+ bldev->props.power = FB_BLANK_POWERDOWN;
+ n8x0_bl_update_status(bldev);
+ backlight_device_unregister(bldev);
+
+ dev_set_drvdata(&dssdev->dev, NULL);
+}
+
+static int n8x0_panel_enable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ mutex_lock(&ddata->lock);
+
+ rfbi_bus_lock();
+
+ r = n8x0_panel_power_on(dssdev);
+
+ rfbi_bus_unlock();
+
+ if (r) {
+ mutex_unlock(&ddata->lock);
+ return r;
+ }
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static void n8x0_panel_disable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ mutex_lock(&ddata->lock);
+
+ rfbi_bus_lock();
+
+ n8x0_panel_power_off(dssdev);
+
+ rfbi_bus_unlock();
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ mutex_unlock(&ddata->lock);
+}
+
+static int n8x0_panel_suspend(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+
+ dev_dbg(&dssdev->dev, "suspend\n");
+
+ mutex_lock(&ddata->lock);
+
+ rfbi_bus_lock();
+
+ n8x0_panel_power_off(dssdev);
+
+ rfbi_bus_unlock();
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static int n8x0_panel_resume(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "resume\n");
+
+ mutex_lock(&ddata->lock);
+
+ rfbi_bus_lock();
+
+ r = n8x0_panel_power_on(dssdev);
+
+ rfbi_bus_unlock();
+
+ if (r) {
+ mutex_unlock(&ddata->lock);
+ return r;
+ }
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static void n8x0_panel_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+}
+
+static void update_done(void *data)
+{
+ rfbi_bus_unlock();
+}
+
+static int n8x0_panel_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+
+ dev_dbg(&dssdev->dev, "update\n");
+
+ mutex_lock(&ddata->lock);
+ rfbi_bus_lock();
+
+ omap_rfbi_prepare_update(dssdev, &x, &y, &w, &h);
+
+ blizzard_ctrl_setup_update(dssdev, x, y, w, h);
+
+ omap_rfbi_update(dssdev, x, y, w, h, update_done, NULL);
+
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static int n8x0_panel_sync(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *ddata = get_drv_data(dssdev);
+
+ dev_dbg(&dssdev->dev, "sync\n");
+
+ mutex_lock(&ddata->lock);
+ rfbi_bus_lock();
+ rfbi_bus_unlock();
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static struct omap_dss_driver n8x0_panel_driver = {
+ .probe = n8x0_panel_probe,
+ .remove = n8x0_panel_remove,
+
+ .enable = n8x0_panel_enable,
+ .disable = n8x0_panel_disable,
+ .suspend = n8x0_panel_suspend,
+ .resume = n8x0_panel_resume,
+
+ .update = n8x0_panel_update,
+ .sync = n8x0_panel_sync,
+
+ .get_resolution = n8x0_panel_get_resolution,
+ .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+ .get_timings = n8x0_panel_get_timings,
+
+ .driver = {
+ .name = "n8x0_panel",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* PANEL */
+
+static int mipid_spi_probe(struct spi_device *spi)
+{
+ dev_dbg(&spi->dev, "mipid_spi_probe\n");
+
+ spi->mode = SPI_MODE_0;
+
+ s_drv_data.spidev = spi;
+
+ return 0;
+}
+
+static int mipid_spi_remove(struct spi_device *spi)
+{
+ dev_dbg(&spi->dev, "mipid_spi_remove\n");
+ return 0;
+}
+
+static struct spi_driver mipid_spi_driver = {
+ .driver = {
+ .name = "lcd_mipid",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mipid_spi_probe,
+ .remove = __devexit_p(mipid_spi_remove),
+};
+
+static int __init n8x0_panel_drv_init(void)
+{
+ int r;
+
+ r = spi_register_driver(&mipid_spi_driver);
+ if (r) {
+ pr_err("n8x0_panel: spi driver registration failed\n");
+ return r;
+ }
+
+ r = omap_dss_register_driver(&n8x0_panel_driver);
+ if (r) {
+ pr_err("n8x0_panel: dss driver registration failed\n");
+ spi_unregister_driver(&mipid_spi_driver);
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit n8x0_panel_drv_exit(void)
+{
+ spi_unregister_driver(&mipid_spi_driver);
+
+ omap_dss_unregister_driver(&n8x0_panel_driver);
+}
+
+module_init(n8x0_panel_drv_init);
+module_exit(n8x0_panel_drv_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c
new file mode 100644
index 000000000000..98ebdaddab5a
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-picodlp.c
@@ -0,0 +1,594 @@
+/*
+ * picodlp panel driver
+ * picodlp_i2c_driver: i2c_client driver
+ *
+ * Copyright (C) 2009-2011 Texas Instruments
+ * Author: Mythri P K <mythripk@ti.com>
+ * Mayuresh Janorkar <mayur@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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/module.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-picodlp.h>
+
+#include "panel-picodlp.h"
+
+struct picodlp_data {
+ struct mutex lock;
+ struct i2c_client *picodlp_i2c_client;
+};
+
+static struct i2c_board_info picodlp_i2c_board_info = {
+ I2C_BOARD_INFO("picodlp_i2c_driver", 0x1b),
+};
+
+struct picodlp_i2c_data {
+ struct mutex xfer_lock;
+};
+
+static struct i2c_device_id picodlp_i2c_id[] = {
+ { "picodlp_i2c_driver", 0 },
+};
+
+struct picodlp_i2c_command {
+ u8 reg;
+ u32 value;
+};
+
+static struct omap_video_timings pico_ls_timings = {
+ .x_res = 864,
+ .y_res = 480,
+ .hsw = 7,
+ .hfp = 11,
+ .hbp = 7,
+
+ .pixel_clock = 19200,
+
+ .vsw = 2,
+ .vfp = 3,
+ .vbp = 14,
+};
+
+static inline struct picodlp_panel_data
+ *get_panel_data(const struct omap_dss_device *dssdev)
+{
+ return (struct picodlp_panel_data *) dssdev->data;
+}
+
+static u32 picodlp_i2c_read(struct i2c_client *client, u8 reg)
+{
+ u8 read_cmd[] = {READ_REG_SELECT, reg}, data[4];
+ struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client);
+ struct i2c_msg msg[2];
+
+ mutex_lock(&picodlp_i2c_data->xfer_lock);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = read_cmd;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 4;
+ msg[1].buf = data;
+
+ i2c_transfer(client->adapter, msg, 2);
+ mutex_unlock(&picodlp_i2c_data->xfer_lock);
+ return (data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24));
+}
+
+static int picodlp_i2c_write_block(struct i2c_client *client,
+ u8 *data, int len)
+{
+ struct i2c_msg msg;
+ int i, r, msg_count = 1;
+
+ struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client);
+
+ if (len < 1 || len > 32) {
+ dev_err(&client->dev,
+ "too long syn_write_block len %d\n", len);
+ return -EIO;
+ }
+ mutex_lock(&picodlp_i2c_data->xfer_lock);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = data;
+ r = i2c_transfer(client->adapter, &msg, msg_count);
+ mutex_unlock(&picodlp_i2c_data->xfer_lock);
+
+ /*
+ * i2c_transfer returns:
+ * number of messages sent in case of success
+ * a negative error number in case of failure
+ */
+ if (r != msg_count)
+ goto err;
+
+ /* In case of success */
+ for (i = 0; i < len; i++)
+ dev_dbg(&client->dev,
+ "addr %x bw 0x%02x[%d]: 0x%02x\n",
+ client->addr, data[0] + i, i, data[i]);
+
+ return 0;
+err:
+ dev_err(&client->dev, "picodlp_i2c_write error\n");
+ return r;
+}
+
+static int picodlp_i2c_write(struct i2c_client *client, u8 reg, u32 value)
+{
+ u8 data[5];
+ int i;
+
+ data[0] = reg;
+ for (i = 1; i < 5; i++)
+ data[i] = (value >> (32 - (i) * 8)) & 0xFF;
+
+ return picodlp_i2c_write_block(client, data, 5);
+}
+
+static int picodlp_i2c_write_array(struct i2c_client *client,
+ const struct picodlp_i2c_command commands[],
+ int count)
+{
+ int i, r = 0;
+ for (i = 0; i < count; i++) {
+ r = picodlp_i2c_write(client, commands[i].reg,
+ commands[i].value);
+ if (r)
+ return r;
+ }
+ return r;
+}
+
+static int picodlp_wait_for_dma_done(struct i2c_client *client)
+{
+ u8 trial = 100;
+
+ do {
+ msleep(1);
+ if (!trial--)
+ return -ETIMEDOUT;
+ } while (picodlp_i2c_read(client, MAIN_STATUS) & DMA_STATUS);
+
+ return 0;
+}
+
+/**
+ * picodlp_i2c_init: i2c_initialization routine
+ * client: i2c_client for communication
+ *
+ * return
+ * 0 : Success, no error
+ * error code : Failure
+ */
+static int picodlp_i2c_init(struct i2c_client *client)
+{
+ int r;
+ static const struct picodlp_i2c_command init_cmd_set1[] = {
+ {SOFT_RESET, 1},
+ {DMD_PARK_TRIGGER, 1},
+ {MISC_REG, 5},
+ {SEQ_CONTROL, 0},
+ {SEQ_VECTOR, 0x100},
+ {DMD_BLOCK_COUNT, 7},
+ {DMD_VCC_CONTROL, 0x109},
+ {DMD_PARK_PULSE_COUNT, 0xA},
+ {DMD_PARK_PULSE_WIDTH, 0xB},
+ {DMD_PARK_DELAY, 0x2ED},
+ {DMD_SHADOW_ENABLE, 0},
+ {FLASH_OPCODE, 0xB},
+ {FLASH_DUMMY_BYTES, 1},
+ {FLASH_ADDR_BYTES, 3},
+ {PBC_CONTROL, 0},
+ {FLASH_START_ADDR, CMT_LUT_0_START_ADDR},
+ {FLASH_READ_BYTES, CMT_LUT_0_SIZE},
+ {CMT_SPLASH_LUT_START_ADDR, 0},
+ {CMT_SPLASH_LUT_DEST_SELECT, CMT_LUT_ALL},
+ {PBC_CONTROL, 1},
+ };
+
+ static const struct picodlp_i2c_command init_cmd_set2[] = {
+ {PBC_CONTROL, 0},
+ {CMT_SPLASH_LUT_DEST_SELECT, 0},
+ {PBC_CONTROL, 0},
+ {FLASH_START_ADDR, SEQUENCE_0_START_ADDR},
+ {FLASH_READ_BYTES, SEQUENCE_0_SIZE},
+ {SEQ_RESET_LUT_START_ADDR, 0},
+ {SEQ_RESET_LUT_DEST_SELECT, SEQ_SEQ_LUT},
+ {PBC_CONTROL, 1},
+ };
+
+ static const struct picodlp_i2c_command init_cmd_set3[] = {
+ {PBC_CONTROL, 0},
+ {SEQ_RESET_LUT_DEST_SELECT, 0},
+ {PBC_CONTROL, 0},
+ {FLASH_START_ADDR, DRC_TABLE_0_START_ADDR},
+ {FLASH_READ_BYTES, DRC_TABLE_0_SIZE},
+ {SEQ_RESET_LUT_START_ADDR, 0},
+ {SEQ_RESET_LUT_DEST_SELECT, SEQ_DRC_LUT_ALL},
+ {PBC_CONTROL, 1},
+ };
+
+ static const struct picodlp_i2c_command init_cmd_set4[] = {
+ {PBC_CONTROL, 0},
+ {SEQ_RESET_LUT_DEST_SELECT, 0},
+ {SDC_ENABLE, 1},
+ {AGC_CTRL, 7},
+ {CCA_C1A, 0x100},
+ {CCA_C1B, 0x0},
+ {CCA_C1C, 0x0},
+ {CCA_C2A, 0x0},
+ {CCA_C2B, 0x100},
+ {CCA_C2C, 0x0},
+ {CCA_C3A, 0x0},
+ {CCA_C3B, 0x0},
+ {CCA_C3C, 0x100},
+ {CCA_C7A, 0x100},
+ {CCA_C7B, 0x100},
+ {CCA_C7C, 0x100},
+ {CCA_ENABLE, 1},
+ {CPU_IF_MODE, 1},
+ {SHORT_FLIP, 1},
+ {CURTAIN_CONTROL, 0},
+ {DMD_PARK_TRIGGER, 0},
+ {R_DRIVE_CURRENT, 0x298},
+ {G_DRIVE_CURRENT, 0x298},
+ {B_DRIVE_CURRENT, 0x298},
+ {RGB_DRIVER_ENABLE, 7},
+ {SEQ_CONTROL, 0},
+ {ACTGEN_CONTROL, 0x10},
+ {SEQUENCE_MODE, SEQ_LOCK},
+ {DATA_FORMAT, RGB888},
+ {INPUT_RESOLUTION, WVGA_864_LANDSCAPE},
+ {INPUT_SOURCE, PARALLEL_RGB},
+ {CPU_IF_SYNC_METHOD, 1},
+ {SEQ_CONTROL, 1}
+ };
+
+ r = picodlp_i2c_write_array(client, init_cmd_set1,
+ ARRAY_SIZE(init_cmd_set1));
+ if (r)
+ return r;
+
+ r = picodlp_wait_for_dma_done(client);
+ if (r)
+ return r;
+
+ r = picodlp_i2c_write_array(client, init_cmd_set2,
+ ARRAY_SIZE(init_cmd_set2));
+ if (r)
+ return r;
+
+ r = picodlp_wait_for_dma_done(client);
+ if (r)
+ return r;
+
+ r = picodlp_i2c_write_array(client, init_cmd_set3,
+ ARRAY_SIZE(init_cmd_set3));
+ if (r)
+ return r;
+
+ r = picodlp_wait_for_dma_done(client);
+ if (r)
+ return r;
+
+ r = picodlp_i2c_write_array(client, init_cmd_set4,
+ ARRAY_SIZE(init_cmd_set4));
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int picodlp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct picodlp_i2c_data *picodlp_i2c_data;
+
+ picodlp_i2c_data = kzalloc(sizeof(struct picodlp_i2c_data), GFP_KERNEL);
+
+ if (!picodlp_i2c_data)
+ return -ENOMEM;
+
+ mutex_init(&picodlp_i2c_data->xfer_lock);
+ i2c_set_clientdata(client, picodlp_i2c_data);
+
+ return 0;
+}
+
+static int picodlp_i2c_remove(struct i2c_client *client)
+{
+ struct picodlp_i2c_data *picodlp_i2c_data =
+ i2c_get_clientdata(client);
+ kfree(picodlp_i2c_data);
+ return 0;
+}
+
+static struct i2c_driver picodlp_i2c_driver = {
+ .driver = {
+ .name = "picodlp_i2c_driver",
+ },
+ .probe = picodlp_i2c_probe,
+ .remove = picodlp_i2c_remove,
+ .id_table = picodlp_i2c_id,
+};
+
+static int picodlp_panel_power_on(struct omap_dss_device *dssdev)
+{
+ int r, trial = 100;
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+ struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
+
+ if (dssdev->platform_enable) {
+ r = dssdev->platform_enable(dssdev);
+ if (r)
+ return r;
+ }
+
+ gpio_set_value(picodlp_pdata->pwrgood_gpio, 0);
+ msleep(1);
+ gpio_set_value(picodlp_pdata->pwrgood_gpio, 1);
+
+ while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) {
+ if (!trial--) {
+ dev_err(&dssdev->dev, "emu_done signal not"
+ " going high\n");
+ return -ETIMEDOUT;
+ }
+ msleep(5);
+ }
+ /*
+ * As per dpp2600 programming guide,
+ * it is required to sleep for 1000ms after emu_done signal goes high
+ * then only i2c commands can be successfully sent to dpp2600
+ */
+ msleep(1000);
+ r = omapdss_dpi_display_enable(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DPI\n");
+ goto err1;
+ }
+
+ r = picodlp_i2c_init(picod->picodlp_i2c_client);
+ if (r)
+ goto err;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return r;
+err:
+ omapdss_dpi_display_disable(dssdev);
+err1:
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ return r;
+}
+
+static void picodlp_panel_power_off(struct omap_dss_device *dssdev)
+{
+ struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
+
+ omapdss_dpi_display_disable(dssdev);
+
+ gpio_set_value(picodlp_pdata->emu_done_gpio, 0);
+ gpio_set_value(picodlp_pdata->pwrgood_gpio, 0);
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+}
+
+static int picodlp_panel_probe(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod;
+ struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
+ struct i2c_adapter *adapter;
+ 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);
+ if (!picod)
+ return -ENOMEM;
+
+ mutex_init(&picod->lock);
+
+ picodlp_adapter_id = picodlp_pdata->picodlp_adapter_id;
+
+ adapter = i2c_get_adapter(picodlp_adapter_id);
+ if (!adapter) {
+ dev_err(&dssdev->dev, "can't get i2c adapter\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info);
+ if (!picodlp_i2c_client) {
+ dev_err(&dssdev->dev, "can't add i2c device::"
+ " picodlp_i2c_client is NULL\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ picod->picodlp_i2c_client = picodlp_i2c_client;
+
+ dev_set_drvdata(&dssdev->dev, picod);
+ return r;
+err:
+ kfree(picod);
+ return r;
+}
+
+static void picodlp_panel_remove(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+
+ i2c_unregister_device(picod->picodlp_i2c_client);
+ dev_set_drvdata(&dssdev->dev, NULL);
+ dev_dbg(&dssdev->dev, "removing picodlp panel\n");
+
+ kfree(picod);
+}
+
+static int picodlp_panel_enable(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "enabling picodlp panel\n");
+
+ mutex_lock(&picod->lock);
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ mutex_unlock(&picod->lock);
+ return -EINVAL;
+ }
+
+ r = picodlp_panel_power_on(dssdev);
+ mutex_unlock(&picod->lock);
+
+ return r;
+}
+
+static void picodlp_panel_disable(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&picod->lock);
+ /* Turn off DLP Power */
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ picodlp_panel_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+ mutex_unlock(&picod->lock);
+
+ dev_dbg(&dssdev->dev, "disabling picodlp panel\n");
+}
+
+static int picodlp_panel_suspend(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&picod->lock);
+ /* Turn off DLP Power */
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ mutex_unlock(&picod->lock);
+ dev_err(&dssdev->dev, "unable to suspend picodlp panel,"
+ " panel is not ACTIVE\n");
+ return -EINVAL;
+ }
+
+ picodlp_panel_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+ mutex_unlock(&picod->lock);
+
+ dev_dbg(&dssdev->dev, "suspending picodlp panel\n");
+ return 0;
+}
+
+static int picodlp_panel_resume(struct omap_dss_device *dssdev)
+{
+ struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&picod->lock);
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ mutex_unlock(&picod->lock);
+ dev_err(&dssdev->dev, "unable to resume picodlp panel,"
+ " panel is not ACTIVE\n");
+ return -EINVAL;
+ }
+
+ r = picodlp_panel_power_on(dssdev);
+ mutex_unlock(&picod->lock);
+ dev_dbg(&dssdev->dev, "resuming picodlp panel\n");
+ return r;
+}
+
+static void picodlp_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+}
+
+static struct omap_dss_driver picodlp_driver = {
+ .probe = picodlp_panel_probe,
+ .remove = picodlp_panel_remove,
+
+ .enable = picodlp_panel_enable,
+ .disable = picodlp_panel_disable,
+
+ .get_resolution = picodlp_get_resolution,
+
+ .suspend = picodlp_panel_suspend,
+ .resume = picodlp_panel_resume,
+
+ .driver = {
+ .name = "picodlp_panel",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init picodlp_init(void)
+{
+ int r = 0;
+
+ r = i2c_add_driver(&picodlp_i2c_driver);
+ if (r) {
+ printk(KERN_WARNING "picodlp_i2c_driver" \
+ " registration failed\n");
+ return r;
+ }
+
+ r = omap_dss_register_driver(&picodlp_driver);
+ if (r)
+ i2c_del_driver(&picodlp_i2c_driver);
+
+ return r;
+}
+
+static void __exit picodlp_exit(void)
+{
+ i2c_del_driver(&picodlp_i2c_driver);
+ omap_dss_unregister_driver(&picodlp_driver);
+}
+
+module_init(picodlp_init);
+module_exit(picodlp_exit);
+
+MODULE_AUTHOR("Mythri P K <mythripk@ti.com>");
+MODULE_DESCRIPTION("picodlp driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-picodlp.h b/drivers/video/omap2/displays/panel-picodlp.h
new file mode 100644
index 000000000000..a34b431a7267
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-picodlp.h
@@ -0,0 +1,288 @@
+/*
+ * Header file required by picodlp panel driver
+ *
+ * Copyright (C) 2009-2011 Texas Instruments
+ * Author: Mythri P K <mythripk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __OMAP2_DISPLAY_PANEL_PICODLP_H
+#define __OMAP2_DISPLAY_PANEL_PICODLP_H
+
+/* Commands used for configuring picodlp panel */
+
+#define MAIN_STATUS 0x03
+#define PBC_CONTROL 0x08
+#define INPUT_SOURCE 0x0B
+#define INPUT_RESOLUTION 0x0C
+#define DATA_FORMAT 0x0D
+#define IMG_ROTATION 0x0E
+#define LONG_FLIP 0x0F
+#define SHORT_FLIP 0x10
+#define TEST_PAT_SELECT 0x11
+#define R_DRIVE_CURRENT 0x12
+#define G_DRIVE_CURRENT 0x13
+#define B_DRIVE_CURRENT 0x14
+#define READ_REG_SELECT 0x15
+#define RGB_DRIVER_ENABLE 0x16
+
+#define CPU_IF_MODE 0x18
+#define FRAME_RATE 0x19
+#define CPU_IF_SYNC_METHOD 0x1A
+#define CPU_IF_SOF 0x1B
+#define CPU_IF_EOF 0x1C
+#define CPU_IF_SLEEP 0x1D
+
+#define SEQUENCE_MODE 0x1E
+#define SOFT_RESET 0x1F
+#define FRONT_END_RESET 0x21
+#define AUTO_PWR_ENABLE 0x22
+
+#define VSYNC_LINE_DELAY 0x23
+#define CPU_PI_HORIZ_START 0x24
+#define CPU_PI_VERT_START 0x25
+#define CPU_PI_HORIZ_WIDTH 0x26
+#define CPU_PI_VERT_HEIGHT 0x27
+
+#define PIXEL_MASK_CROP 0x28
+#define CROP_FIRST_LINE 0x29
+#define CROP_LAST_LINE 0x2A
+#define CROP_FIRST_PIXEL 0x2B
+#define CROP_LAST_PIXEL 0x2C
+#define DMD_PARK_TRIGGER 0x2D
+
+#define MISC_REG 0x30
+
+/* AGC registers */
+#define AGC_CTRL 0x50
+#define AGC_CLIPPED_PIXS 0x55
+#define AGC_BRIGHT_PIXS 0x56
+#define AGC_BG_PIXS 0x57
+#define AGC_SAFETY_MARGIN 0x17
+
+/* Color Coordinate Adjustment registers */
+#define CCA_ENABLE 0x5E
+#define CCA_C1A 0x5F
+#define CCA_C1B 0x60
+#define CCA_C1C 0x61
+#define CCA_C2A 0x62
+#define CCA_C2B 0x63
+#define CCA_C2C 0x64
+#define CCA_C3A 0x65
+#define CCA_C3B 0x66
+#define CCA_C3C 0x67
+#define CCA_C7A 0x71
+#define CCA_C7B 0x72
+#define CCA_C7C 0x73
+
+/**
+ * DLP Pico Processor 2600 comes with flash
+ * We can do DMA operations from flash for accessing Look Up Tables
+ */
+#define DMA_STATUS 0x100
+#define FLASH_ADDR_BYTES 0x74
+#define FLASH_DUMMY_BYTES 0x75
+#define FLASH_WRITE_BYTES 0x76
+#define FLASH_READ_BYTES 0x77
+#define FLASH_OPCODE 0x78
+#define FLASH_START_ADDR 0x79
+#define FLASH_DUMMY2 0x7A
+#define FLASH_WRITE_DATA 0x7B
+
+#define TEMPORAL_DITH_DISABLE 0x7E
+#define SEQ_CONTROL 0x82
+#define SEQ_VECTOR 0x83
+
+/* DMD is Digital Micromirror Device */
+#define DMD_BLOCK_COUNT 0x84
+#define DMD_VCC_CONTROL 0x86
+#define DMD_PARK_PULSE_COUNT 0x87
+#define DMD_PARK_PULSE_WIDTH 0x88
+#define DMD_PARK_DELAY 0x89
+#define DMD_SHADOW_ENABLE 0x8E
+#define SEQ_STATUS 0x8F
+#define FLASH_CLOCK_CONTROL 0x98
+#define DMD_PARK 0x2D
+
+#define SDRAM_BIST_ENABLE 0x46
+#define DDR_DRIVER_STRENGTH 0x9A
+#define SDC_ENABLE 0x9D
+#define SDC_BUFF_SWAP_DISABLE 0xA3
+#define CURTAIN_CONTROL 0xA6
+#define DDR_BUS_SWAP_ENABLE 0xA7
+#define DMD_TRC_ENABLE 0xA8
+#define DMD_BUS_SWAP_ENABLE 0xA9
+
+#define ACTGEN_ENABLE 0xAE
+#define ACTGEN_CONTROL 0xAF
+#define ACTGEN_HORIZ_BP 0xB0
+#define ACTGEN_VERT_BP 0xB1
+
+/* Look Up Table access */
+#define CMT_SPLASH_LUT_START_ADDR 0xFA
+#define CMT_SPLASH_LUT_DEST_SELECT 0xFB
+#define CMT_SPLASH_LUT_DATA 0xFC
+#define SEQ_RESET_LUT_START_ADDR 0xFD
+#define SEQ_RESET_LUT_DEST_SELECT 0xFE
+#define SEQ_RESET_LUT_DATA 0xFF
+
+/* Input source definitions */
+#define PARALLEL_RGB 0
+#define INT_TEST_PATTERN 1
+#define SPLASH_SCREEN 2
+#define CPU_INTF 3
+#define BT656 4
+
+/* Standard input resolution definitions */
+#define QWVGA_LANDSCAPE 3 /* (427h*240v) */
+#define WVGA_864_LANDSCAPE 21 /* (864h*480v) */
+#define WVGA_DMD_OPTICAL_TEST 35 /* (608h*684v) */
+
+/* Standard data format definitions */
+#define RGB565 0
+#define RGB666 1
+#define RGB888 2
+
+/* Test Pattern definitions */
+#define TPG_CHECKERBOARD 0
+#define TPG_BLACK 1
+#define TPG_WHITE 2
+#define TPG_RED 3
+#define TPG_BLUE 4
+#define TPG_GREEN 5
+#define TPG_VLINES_BLACK 6
+#define TPG_HLINES_BLACK 7
+#define TPG_VLINES_ALT 8
+#define TPG_HLINES_ALT 9
+#define TPG_DIAG_LINES 10
+#define TPG_GREYRAMP_VERT 11
+#define TPG_GREYRAMP_HORIZ 12
+#define TPG_ANSI_CHECKERBOARD 13
+
+/* sequence mode definitions */
+#define SEQ_FREE_RUN 0
+#define SEQ_LOCK 1
+
+/* curtain color definitions */
+#define CURTAIN_BLACK 0
+#define CURTAIN_RED 1
+#define CURTAIN_GREEN 2
+#define CURTAIN_BLUE 3
+#define CURTAIN_YELLOW 4
+#define CURTAIN_MAGENTA 5
+#define CURTAIN_CYAN 6
+#define CURTAIN_WHITE 7
+
+/* LUT definitions */
+#define CMT_LUT_NONE 0
+#define CMT_LUT_GREEN 1
+#define CMT_LUT_RED 2
+#define CMT_LUT_BLUE 3
+#define CMT_LUT_ALL 4
+#define SPLASH_LUT 5
+
+#define SEQ_LUT_NONE 0
+#define SEQ_DRC_LUT_0 1
+#define SEQ_DRC_LUT_1 2
+#define SEQ_DRC_LUT_2 3
+#define SEQ_DRC_LUT_3 4
+#define SEQ_SEQ_LUT 5
+#define SEQ_DRC_LUT_ALL 6
+#define WPC_PROGRAM_LUT 7
+
+#define BITSTREAM_START_ADDR 0x00000000
+#define BITSTREAM_SIZE 0x00040000
+
+#define WPC_FW_0_START_ADDR 0x00040000
+#define WPC_FW_0_SIZE 0x00000ce8
+
+#define SEQUENCE_0_START_ADDR 0x00044000
+#define SEQUENCE_0_SIZE 0x00001000
+
+#define SEQUENCE_1_START_ADDR 0x00045000
+#define SEQUENCE_1_SIZE 0x00000d10
+
+#define SEQUENCE_2_START_ADDR 0x00046000
+#define SEQUENCE_2_SIZE 0x00000d10
+
+#define SEQUENCE_3_START_ADDR 0x00047000
+#define SEQUENCE_3_SIZE 0x00000d10
+
+#define SEQUENCE_4_START_ADDR 0x00048000
+#define SEQUENCE_4_SIZE 0x00000d10
+
+#define SEQUENCE_5_START_ADDR 0x00049000
+#define SEQUENCE_5_SIZE 0x00000d10
+
+#define SEQUENCE_6_START_ADDR 0x0004a000
+#define SEQUENCE_6_SIZE 0x00000d10
+
+#define CMT_LUT_0_START_ADDR 0x0004b200
+#define CMT_LUT_0_SIZE 0x00000600
+
+#define CMT_LUT_1_START_ADDR 0x0004b800
+#define CMT_LUT_1_SIZE 0x00000600
+
+#define CMT_LUT_2_START_ADDR 0x0004be00
+#define CMT_LUT_2_SIZE 0x00000600
+
+#define CMT_LUT_3_START_ADDR 0x0004c400
+#define CMT_LUT_3_SIZE 0x00000600
+
+#define CMT_LUT_4_START_ADDR 0x0004ca00
+#define CMT_LUT_4_SIZE 0x00000600
+
+#define CMT_LUT_5_START_ADDR 0x0004d000
+#define CMT_LUT_5_SIZE 0x00000600
+
+#define CMT_LUT_6_START_ADDR 0x0004d600
+#define CMT_LUT_6_SIZE 0x00000600
+
+#define DRC_TABLE_0_START_ADDR 0x0004dc00
+#define DRC_TABLE_0_SIZE 0x00000100
+
+#define SPLASH_0_START_ADDR 0x0004dd00
+#define SPLASH_0_SIZE 0x00032280
+
+#define SEQUENCE_7_START_ADDR 0x00080000
+#define SEQUENCE_7_SIZE 0x00000d10
+
+#define SEQUENCE_8_START_ADDR 0x00081800
+#define SEQUENCE_8_SIZE 0x00000d10
+
+#define SEQUENCE_9_START_ADDR 0x00083000
+#define SEQUENCE_9_SIZE 0x00000d10
+
+#define CMT_LUT_7_START_ADDR 0x0008e000
+#define CMT_LUT_7_SIZE 0x00000600
+
+#define CMT_LUT_8_START_ADDR 0x0008e800
+#define CMT_LUT_8_SIZE 0x00000600
+
+#define CMT_LUT_9_START_ADDR 0x0008f000
+#define CMT_LUT_9_SIZE 0x00000600
+
+#define SPLASH_1_START_ADDR 0x0009a000
+#define SPLASH_1_SIZE 0x00032280
+
+#define SPLASH_2_START_ADDR 0x000cd000
+#define SPLASH_2_SIZE 0x00032280
+
+#define SPLASH_3_START_ADDR 0x00100000
+#define SPLASH_3_SIZE 0x00032280
+
+#define OPT_SPLASH_0_START_ADDR 0x00134000
+#define OPT_SPLASH_0_SIZE 0x000cb100
+
+#endif
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 4e888ac09b3f..80c3f6ab1a94 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -35,26 +35,12 @@
#include <video/omapdss.h>
#include <video/omap-panel-nokia-dsi.h>
+#include <video/mipi_display.h>
/* DSI Virtual channel. Hardcoded for now. */
#define TCH 0
#define DCS_READ_NUM_ERRORS 0x05
-#define DCS_READ_POWER_MODE 0x0a
-#define DCS_READ_MADCTL 0x0b
-#define DCS_READ_PIXEL_FORMAT 0x0c
-#define DCS_RDDSDR 0x0f
-#define DCS_SLEEP_IN 0x10
-#define DCS_SLEEP_OUT 0x11
-#define DCS_DISPLAY_OFF 0x28
-#define DCS_DISPLAY_ON 0x29
-#define DCS_COLUMN_ADDR 0x2a
-#define DCS_PAGE_ADDR 0x2b
-#define DCS_MEMORY_WRITE 0x2c
-#define DCS_TEAR_OFF 0x34
-#define DCS_TEAR_ON 0x35
-#define DCS_MEM_ACC_CTRL 0x36
-#define DCS_PIXEL_FORMAT 0x3a
#define DCS_BRIGHTNESS 0x51
#define DCS_CTRL_DISPLAY 0x53
#define DCS_WRITE_CABC 0x55
@@ -222,8 +208,6 @@ struct taal_data {
struct delayed_work te_timeout_work;
- bool use_dsi_bl;
-
bool cabc_broken;
unsigned cabc_mode;
@@ -302,7 +286,7 @@ static int taal_sleep_in(struct taal_data *td)
hw_guard_wait(td);
- cmd = DCS_SLEEP_IN;
+ cmd = MIPI_DCS_ENTER_SLEEP_MODE;
r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1);
if (r)
return r;
@@ -321,7 +305,7 @@ static int taal_sleep_out(struct taal_data *td)
hw_guard_wait(td);
- r = taal_dcs_write_0(td, DCS_SLEEP_OUT);
+ r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE);
if (r)
return r;
@@ -356,7 +340,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
u8 mode;
int b5, b6, b7;
- r = taal_dcs_read_1(td, DCS_READ_MADCTL, &mode);
+ r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode);
if (r)
return r;
@@ -390,7 +374,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
mode &= ~((1<<7) | (1<<6) | (1<<5));
mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
- return taal_dcs_write_1(td, DCS_MEM_ACC_CTRL, mode);
+ return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode);
}
static int taal_set_update_window(struct taal_data *td,
@@ -403,7 +387,7 @@ static int taal_set_update_window(struct taal_data *td,
u16 y2 = y + h - 1;
u8 buf[5];
- buf[0] = DCS_COLUMN_ADDR;
+ buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
buf[1] = (x1 >> 8) & 0xff;
buf[2] = (x1 >> 0) & 0xff;
buf[3] = (x2 >> 8) & 0xff;
@@ -413,7 +397,7 @@ static int taal_set_update_window(struct taal_data *td,
if (r)
return r;
- buf[0] = DCS_PAGE_ADDR;
+ buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
buf[1] = (y1 >> 8) & 0xff;
buf[2] = (y1 >> 0) & 0xff;
buf[3] = (y2 >> 8) & 0xff;
@@ -555,7 +539,6 @@ static int taal_bl_update_status(struct backlight_device *dev)
{
struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
- struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r;
int level;
@@ -569,23 +552,16 @@ static int taal_bl_update_status(struct backlight_device *dev)
mutex_lock(&td->lock);
- if (td->use_dsi_bl) {
- if (td->enabled) {
- dsi_bus_lock(dssdev);
+ if (td->enabled) {
+ dsi_bus_lock(dssdev);
- r = taal_wake_up(dssdev);
- if (!r)
- r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
+ r = taal_wake_up(dssdev);
+ if (!r)
+ r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
- dsi_bus_unlock(dssdev);
- } else {
- r = 0;
- }
+ dsi_bus_unlock(dssdev);
} else {
- if (!panel_data->set_backlight)
- r = -EINVAL;
- else
- r = panel_data->set_backlight(dssdev, level);
+ r = 0;
}
mutex_unlock(&td->lock);
@@ -964,7 +940,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
{
struct backlight_properties props;
struct taal_data *td;
- struct backlight_device *bldev;
+ struct backlight_device *bldev = NULL;
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
struct panel_config *panel_config = NULL;
int r, i;
@@ -990,7 +966,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings = panel_config->timings;
- dssdev->ctrl.pixel_size = 24;
+ dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
td = kzalloc(sizeof(*td), GFP_KERNEL);
if (!td) {
@@ -1025,35 +1001,26 @@ static int taal_probe(struct omap_dss_device *dssdev)
taal_hw_reset(dssdev);
- /* if no platform set_backlight() defined, presume DSI backlight
- * control */
- memset(&props, 0, sizeof(struct backlight_properties));
- if (!panel_data->set_backlight)
- td->use_dsi_bl = true;
-
- if (td->use_dsi_bl)
+ if (panel_data->use_dsi_backlight) {
+ memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 255;
- else
- props.max_brightness = 127;
-
- props.type = BACKLIGHT_RAW;
- bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev,
- dssdev, &taal_bl_ops, &props);
- if (IS_ERR(bldev)) {
- r = PTR_ERR(bldev);
- goto err_bl;
- }
- td->bldev = bldev;
+ props.type = BACKLIGHT_RAW;
+ bldev = backlight_device_register(dev_name(&dssdev->dev),
+ &dssdev->dev, dssdev, &taal_bl_ops, &props);
+ if (IS_ERR(bldev)) {
+ r = PTR_ERR(bldev);
+ goto err_bl;
+ }
+
+ td->bldev = bldev;
- bldev->props.fb_blank = FB_BLANK_UNBLANK;
- bldev->props.power = FB_BLANK_UNBLANK;
- if (td->use_dsi_bl)
+ bldev->props.fb_blank = FB_BLANK_UNBLANK;
+ bldev->props.power = FB_BLANK_UNBLANK;
bldev->props.brightness = 255;
- else
- bldev->props.brightness = 127;
- taal_bl_update_status(bldev);
+ taal_bl_update_status(bldev);
+ }
if (panel_data->use_ext_te) {
int gpio = panel_data->ext_te_gpio;
@@ -1067,7 +1034,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
gpio_direction_input(gpio);
r = request_irq(gpio_to_irq(gpio), taal_te_isr,
- IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_RISING,
"taal vsync", dssdev);
if (r) {
@@ -1111,7 +1078,8 @@ err_irq:
if (panel_data->use_ext_te)
gpio_free(panel_data->ext_te_gpio);
err_gpio:
- backlight_device_unregister(bldev);
+ if (bldev != NULL)
+ backlight_device_unregister(bldev);
err_bl:
destroy_workqueue(td->workqueue);
err_wq:
@@ -1140,9 +1108,11 @@ static void __exit taal_remove(struct omap_dss_device *dssdev)
}
bldev = td->bldev;
- bldev->props.power = FB_BLANK_POWERDOWN;
- taal_bl_update_status(bldev);
- backlight_device_unregister(bldev);
+ if (bldev != NULL) {
+ bldev->props.power = FB_BLANK_POWERDOWN;
+ taal_bl_update_status(bldev);
+ backlight_device_unregister(bldev);
+ }
taal_cancel_ulps_work(dssdev);
taal_cancel_esd_work(dssdev);
@@ -1195,7 +1165,8 @@ static int taal_power_on(struct omap_dss_device *dssdev)
if (r)
goto err;
- r = taal_dcs_write_1(td, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
+ r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT,
+ MIPI_DCS_PIXEL_FMT_24BIT);
if (r)
goto err;
@@ -1209,7 +1180,7 @@ static int taal_power_on(struct omap_dss_device *dssdev)
goto err;
}
- r = taal_dcs_write_0(td, DCS_DISPLAY_ON);
+ r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON);
if (r)
goto err;
@@ -1246,7 +1217,7 @@ static void taal_power_off(struct omap_dss_device *dssdev)
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
int r;
- r = taal_dcs_write_0(td, DCS_DISPLAY_OFF);
+ r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF);
if (!r)
r = taal_sleep_in(td);
@@ -1529,9 +1500,9 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
int r;
if (enable)
- r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
+ r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
else
- r = taal_dcs_write_0(td, DCS_TEAR_OFF);
+ r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF);
if (!panel_data->use_ext_te)
omapdss_dsi_enable_te(dssdev, enable);
@@ -1851,7 +1822,7 @@ static void taal_esd_work(struct work_struct *work)
goto err;
}
- r = taal_dcs_read_1(td, DCS_RDDSDR, &state1);
+ r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
if (r) {
dev_err(&dssdev->dev, "failed to read Taal status\n");
goto err;
@@ -1864,7 +1835,7 @@ static void taal_esd_work(struct work_struct *work)
goto err;
}
- r = taal_dcs_read_1(td, DCS_RDDSDR, &state2);
+ r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
if (r) {
dev_err(&dssdev->dev, "failed to read Taal status\n");
goto err;
@@ -1880,7 +1851,7 @@ static void taal_esd_work(struct work_struct *work)
/* Self-diagnostics result is also shown on TE GPIO line. We need
* to re-enable TE after self diagnostics */
if (td->te_enabled && panel_data->use_ext_te) {
- r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
+ r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
if (r)
goto err;
}
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 0d12524db14b..7be7c06a249e 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -1,5 +1,5 @@
menuconfig OMAP2_DSS
- tristate "OMAP2+ Display Subsystem support (EXPERIMENTAL)"
+ tristate "OMAP2+ Display Subsystem support"
depends on ARCH_OMAP2PLUS
help
OMAP2+ Display Subsystem support.
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 10d9d3bb3e24..bd34ac5b2026 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -6,4 +6,4 @@ omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \
- hdmi_omap4_panel.o
+ hdmi_panel.o ti_hdmi_4xxx_ip.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index 76821fefce9a..86ec12e16c7c 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -145,6 +145,10 @@ static int dss_initialize_debugfs(void)
debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir,
&venc_dump_regs, &dss_debug_fops);
#endif
+#ifdef CONFIG_OMAP4_DSS_HDMI
+ debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir,
+ &hdmi_dump_regs, &dss_debug_fops);
+#endif
return 0;
}
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 0f3961a1ce26..6892cfd2e3b7 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -106,7 +106,7 @@ static struct {
int irq;
struct clk *dss_clk;
- u32 fifo_size[3];
+ u32 fifo_size[MAX_DSS_OVERLAYS];
spinlock_t irq_lock;
u32 irq_error_mask;
@@ -171,172 +171,98 @@ static int dispc_get_ctx_loss_count(void)
static void dispc_save_context(void)
{
- int i;
+ int i, j;
DSSDBG("dispc_save_context\n");
SR(IRQENABLE);
SR(CONTROL);
SR(CONFIG);
- SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD));
- SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT));
- SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD));
- SR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT));
SR(LINE_NUMBER);
- SR(TIMING_H(OMAP_DSS_CHANNEL_LCD));
- SR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
- SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
- SR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
SR(GLOBAL_ALPHA);
- SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
- SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
SR(CONTROL2);
- SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2));
- SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2));
- SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2));
- SR(TIMING_H(OMAP_DSS_CHANNEL_LCD2));
- SR(TIMING_V(OMAP_DSS_CHANNEL_LCD2));
- SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2));
- SR(DIVISORo(OMAP_DSS_CHANNEL_LCD2));
SR(CONFIG2);
}
- SR(OVL_BA0(OMAP_DSS_GFX));
- SR(OVL_BA1(OMAP_DSS_GFX));
- SR(OVL_POSITION(OMAP_DSS_GFX));
- SR(OVL_SIZE(OMAP_DSS_GFX));
- SR(OVL_ATTRIBUTES(OMAP_DSS_GFX));
- SR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX));
- SR(OVL_ROW_INC(OMAP_DSS_GFX));
- SR(OVL_PIXEL_INC(OMAP_DSS_GFX));
- SR(OVL_WINDOW_SKIP(OMAP_DSS_GFX));
- SR(OVL_TABLE_BA(OMAP_DSS_GFX));
+ for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
+ SR(DEFAULT_COLOR(i));
+ SR(TRANS_COLOR(i));
+ SR(SIZE_MGR(i));
+ if (i == OMAP_DSS_CHANNEL_DIGIT)
+ continue;
+ SR(TIMING_H(i));
+ SR(TIMING_V(i));
+ SR(POL_FREQ(i));
+ SR(DIVISORo(i));
- SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD));
- SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
- SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
+ SR(DATA_CYCLE1(i));
+ SR(DATA_CYCLE2(i));
+ SR(DATA_CYCLE3(i));
- if (dss_has_feature(FEAT_CPR)) {
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
- }
- if (dss_has_feature(FEAT_MGR_LCD2)) {
if (dss_has_feature(FEAT_CPR)) {
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ SR(CPR_COEF_R(i));
+ SR(CPR_COEF_G(i));
+ SR(CPR_COEF_B(i));
}
-
- SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
- SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
- SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
}
- if (dss_has_feature(FEAT_PRELOAD))
- SR(OVL_PRELOAD(OMAP_DSS_GFX));
-
- /* VID1 */
- SR(OVL_BA0(OMAP_DSS_VIDEO1));
- SR(OVL_BA1(OMAP_DSS_VIDEO1));
- SR(OVL_POSITION(OMAP_DSS_VIDEO1));
- SR(OVL_SIZE(OMAP_DSS_VIDEO1));
- SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- SR(OVL_ROW_INC(OMAP_DSS_VIDEO1));
- SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- SR(OVL_FIR(OMAP_DSS_VIDEO1));
- SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU0(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
- }
-
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- SR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
- SR(OVL_BA1_UV(OMAP_DSS_VIDEO1));
- SR(OVL_FIR2(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU2_1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
-
- if (dss_has_feature(FEAT_PRELOAD))
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
-
- /* VID2 */
- SR(OVL_BA0(OMAP_DSS_VIDEO2));
- SR(OVL_BA1(OMAP_DSS_VIDEO2));
- SR(OVL_POSITION(OMAP_DSS_VIDEO2));
- SR(OVL_SIZE(OMAP_DSS_VIDEO2));
- SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- SR(OVL_ROW_INC(OMAP_DSS_VIDEO2));
- SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- SR(OVL_FIR(OMAP_DSS_VIDEO2));
- SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU0(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU1(OMAP_DSS_VIDEO2));
+ for (i = 0; i < dss_feat_get_num_ovls(); i++) {
+ SR(OVL_BA0(i));
+ SR(OVL_BA1(i));
+ SR(OVL_POSITION(i));
+ SR(OVL_SIZE(i));
+ SR(OVL_ATTRIBUTES(i));
+ SR(OVL_FIFO_THRESHOLD(i));
+ SR(OVL_ROW_INC(i));
+ SR(OVL_PIXEL_INC(i));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(i));
+ if (i == OMAP_DSS_GFX) {
+ SR(OVL_WINDOW_SKIP(i));
+ SR(OVL_TABLE_BA(i));
+ continue;
+ }
+ SR(OVL_FIR(i));
+ SR(OVL_PICTURE_SIZE(i));
+ SR(OVL_ACCU0(i));
+ SR(OVL_ACCU1(i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_H(i, j));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_HV(i, j));
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 5; j++)
+ SR(OVL_CONV_COEF(i, j));
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
- }
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_V(i, j));
+ }
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- SR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
- SR(OVL_BA1_UV(OMAP_DSS_VIDEO2));
- SR(OVL_FIR2(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU2_1(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ SR(OVL_BA0_UV(i));
+ SR(OVL_BA1_UV(i));
+ SR(OVL_FIR2(i));
+ SR(OVL_ACCU2_0(i));
+ SR(OVL_ACCU2_1(i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_H2(i, j));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_HV2(i, j));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ SR(OVL_FIR_COEF_V2(i, j));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ SR(OVL_ATTRIBUTES2(i));
}
- if (dss_has_feature(FEAT_ATTR2))
- SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
-
- if (dss_has_feature(FEAT_PRELOAD))
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
@@ -349,7 +275,7 @@ static void dispc_save_context(void)
static void dispc_restore_context(void)
{
- int i, ctx;
+ int i, j, ctx;
DSSDBG("dispc_restore_context\n");
@@ -367,165 +293,89 @@ static void dispc_restore_context(void)
/*RR(IRQENABLE);*/
/*RR(CONTROL);*/
RR(CONFIG);
- RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD));
- RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT));
- RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD));
- RR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT));
RR(LINE_NUMBER);
- RR(TIMING_H(OMAP_DSS_CHANNEL_LCD));
- RR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
- RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
- RR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
RR(GLOBAL_ALPHA);
- RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
- RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2));
- RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2));
- RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2));
- RR(TIMING_H(OMAP_DSS_CHANNEL_LCD2));
- RR(TIMING_V(OMAP_DSS_CHANNEL_LCD2));
- RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2));
- RR(DIVISORo(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONFIG2);
- }
-
- RR(OVL_BA0(OMAP_DSS_GFX));
- RR(OVL_BA1(OMAP_DSS_GFX));
- RR(OVL_POSITION(OMAP_DSS_GFX));
- RR(OVL_SIZE(OMAP_DSS_GFX));
- RR(OVL_ATTRIBUTES(OMAP_DSS_GFX));
- RR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX));
- RR(OVL_ROW_INC(OMAP_DSS_GFX));
- RR(OVL_PIXEL_INC(OMAP_DSS_GFX));
- RR(OVL_WINDOW_SKIP(OMAP_DSS_GFX));
- RR(OVL_TABLE_BA(OMAP_DSS_GFX));
-
- RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD));
- RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
- RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
+ for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
+ RR(DEFAULT_COLOR(i));
+ RR(TRANS_COLOR(i));
+ RR(SIZE_MGR(i));
+ if (i == OMAP_DSS_CHANNEL_DIGIT)
+ continue;
+ RR(TIMING_H(i));
+ RR(TIMING_V(i));
+ RR(POL_FREQ(i));
+ RR(DIVISORo(i));
- if (dss_has_feature(FEAT_CPR)) {
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
- }
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
- RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
- RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
+ RR(DATA_CYCLE1(i));
+ RR(DATA_CYCLE2(i));
+ RR(DATA_CYCLE3(i));
if (dss_has_feature(FEAT_CPR)) {
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ RR(CPR_COEF_R(i));
+ RR(CPR_COEF_G(i));
+ RR(CPR_COEF_B(i));
}
}
- if (dss_has_feature(FEAT_PRELOAD))
- RR(OVL_PRELOAD(OMAP_DSS_GFX));
-
- /* VID1 */
- RR(OVL_BA0(OMAP_DSS_VIDEO1));
- RR(OVL_BA1(OMAP_DSS_VIDEO1));
- RR(OVL_POSITION(OMAP_DSS_VIDEO1));
- RR(OVL_SIZE(OMAP_DSS_VIDEO1));
- RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- RR(OVL_ROW_INC(OMAP_DSS_VIDEO1));
- RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- RR(OVL_FIR(OMAP_DSS_VIDEO1));
- RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU0(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 5; i++)
- RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
- }
-
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- RR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
- RR(OVL_BA1_UV(OMAP_DSS_VIDEO1));
- RR(OVL_FIR2(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU2_1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
-
- if (dss_has_feature(FEAT_PRELOAD))
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
-
- /* VID2 */
- RR(OVL_BA0(OMAP_DSS_VIDEO2));
- RR(OVL_BA1(OMAP_DSS_VIDEO2));
- RR(OVL_POSITION(OMAP_DSS_VIDEO2));
- RR(OVL_SIZE(OMAP_DSS_VIDEO2));
- RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- RR(OVL_ROW_INC(OMAP_DSS_VIDEO2));
- RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- RR(OVL_FIR(OMAP_DSS_VIDEO2));
- RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU0(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU1(OMAP_DSS_VIDEO2));
+ for (i = 0; i < dss_feat_get_num_ovls(); i++) {
+ RR(OVL_BA0(i));
+ RR(OVL_BA1(i));
+ RR(OVL_POSITION(i));
+ RR(OVL_SIZE(i));
+ RR(OVL_ATTRIBUTES(i));
+ RR(OVL_FIFO_THRESHOLD(i));
+ RR(OVL_ROW_INC(i));
+ RR(OVL_PIXEL_INC(i));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(i));
+ if (i == OMAP_DSS_GFX) {
+ RR(OVL_WINDOW_SKIP(i));
+ RR(OVL_TABLE_BA(i));
+ continue;
+ }
+ RR(OVL_FIR(i));
+ RR(OVL_PICTURE_SIZE(i));
+ RR(OVL_ACCU0(i));
+ RR(OVL_ACCU1(i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_H(i, j));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_HV(i, j));
- for (i = 0; i < 5; i++)
- RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 5; j++)
+ RR(OVL_CONV_COEF(i, j));
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
- }
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_V(i, j));
+ }
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- RR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
- RR(OVL_BA1_UV(OMAP_DSS_VIDEO2));
- RR(OVL_FIR2(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU2_1(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ RR(OVL_BA0_UV(i));
+ RR(OVL_BA1_UV(i));
+ RR(OVL_FIR2(i));
+ RR(OVL_ACCU2_0(i));
+ RR(OVL_ACCU2_1(i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_H2(i, j));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_HV2(i, j));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i));
+ for (j = 0; j < 8; j++)
+ RR(OVL_FIR_COEF_V2(i, j));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ RR(OVL_ATTRIBUTES2(i));
}
- if (dss_has_feature(FEAT_ATTR2))
- RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
-
- if (dss_has_feature(FEAT_PRELOAD))
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
if (dss_has_feature(FEAT_CORE_CLK_DIV))
RR(DIVISOR);
@@ -570,13 +420,28 @@ void dispc_runtime_put(void)
WARN_ON(r < 0);
}
+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;
+}
+
+static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel)
+{
+ struct omap_overlay_manager *mgr =
+ omap_dss_get_overlay_manager(channel);
-bool dispc_go_busy(enum omap_channel channel)
+ return mgr ? mgr->device : NULL;
+}
+
+bool dispc_mgr_go_busy(enum omap_channel channel)
{
int bit;
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
+ if (dispc_mgr_is_lcd(channel))
bit = 5; /* GOLCD */
else
bit = 6; /* GODIGIT */
@@ -587,13 +452,12 @@ bool dispc_go_busy(enum omap_channel channel)
return REG_GET(DISPC_CONTROL, bit, bit) == 1;
}
-void dispc_go(enum omap_channel channel)
+void dispc_mgr_go(enum omap_channel channel)
{
int bit;
bool enable_bit, go_bit;
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
+ if (dispc_mgr_is_lcd(channel))
bit = 0; /* LCDENABLE */
else
bit = 1; /* DIGITALENABLE */
@@ -607,8 +471,7 @@ void dispc_go(enum omap_channel channel)
if (!enable_bit)
return;
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
+ if (dispc_mgr_is_lcd(channel))
bit = 5; /* GOLCD */
else
bit = 6; /* GODIGIT */
@@ -632,43 +495,44 @@ void dispc_go(enum omap_channel channel)
REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
}
-static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
{
dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value);
}
-static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
{
dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value);
}
-static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firv_reg(enum omap_plane plane, int reg, u32 value)
{
dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value);
}
-static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firh2_reg(enum omap_plane plane, int reg, u32 value)
{
BUG_ON(plane == OMAP_DSS_GFX);
dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value);
}
-static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firhv2_reg(enum omap_plane plane, int reg,
+ u32 value)
{
BUG_ON(plane == OMAP_DSS_GFX);
dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value);
}
-static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
+static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
{
BUG_ON(plane == OMAP_DSS_GFX);
dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value);
}
-static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
+static void dispc_ovl_set_scale_coef(enum omap_plane plane, int hscaleup,
int vscaleup, int five_taps,
enum omap_color_component color_comp)
{
@@ -769,11 +633,11 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
| FLD_VAL(v_coef[i].vc2, 31, 24);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
- _dispc_write_firh_reg(plane, i, h);
- _dispc_write_firhv_reg(plane, i, hv);
+ dispc_ovl_write_firh_reg(plane, i, h);
+ dispc_ovl_write_firhv_reg(plane, i, hv);
} else {
- _dispc_write_firh2_reg(plane, i, h);
- _dispc_write_firhv2_reg(plane, i, hv);
+ dispc_ovl_write_firh2_reg(plane, i, h);
+ dispc_ovl_write_firhv2_reg(plane, i, hv);
}
}
@@ -784,15 +648,16 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
v = FLD_VAL(v_coef[i].vc00, 7, 0)
| FLD_VAL(v_coef[i].vc22, 15, 8);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
- _dispc_write_firv_reg(plane, i, v);
+ dispc_ovl_write_firv_reg(plane, i, v);
else
- _dispc_write_firv2_reg(plane, i, v);
+ dispc_ovl_write_firv2_reg(plane, i, v);
}
}
}
static void _dispc_setup_color_conv_coef(void)
{
+ int i;
const struct color_conv_coef {
int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
int full_range;
@@ -806,65 +671,54 @@ static void _dispc_setup_color_conv_coef(void)
ct = &ctbl_bt601_5;
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0),
- CVAL(ct->rcr, ct->ry));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1),
- CVAL(ct->gy, ct->rcb));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2),
- CVAL(ct->gcb, ct->gcr));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3),
- CVAL(ct->bcr, ct->by));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4),
- CVAL(0, ct->bcb));
-
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0),
- CVAL(ct->rcr, ct->ry));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1),
- CVAL(ct->gy, ct->rcb));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2),
- CVAL(ct->gcb, ct->gcr));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3),
- CVAL(ct->bcr, ct->by));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4),
- CVAL(0, ct->bcb));
+ for (i = 1; i < dss_feat_get_num_ovls(); i++) {
+ dispc_write_reg(DISPC_OVL_CONV_COEF(i, 0),
+ CVAL(ct->rcr, ct->ry));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(i, 1),
+ CVAL(ct->gy, ct->rcb));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(i, 2),
+ CVAL(ct->gcb, ct->gcr));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(i, 3),
+ CVAL(ct->bcr, ct->by));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(i, 4),
+ CVAL(0, ct->bcb));
-#undef CVAL
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), ct->full_range,
+ 11, 11);
+ }
- REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1),
- ct->full_range, 11, 11);
- REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2),
- ct->full_range, 11, 11);
+#undef CVAL
}
-static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr)
+static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr)
{
dispc_write_reg(DISPC_OVL_BA0(plane), paddr);
}
-static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr)
+static void dispc_ovl_set_ba1(enum omap_plane plane, u32 paddr)
{
dispc_write_reg(DISPC_OVL_BA1(plane), paddr);
}
-static void _dispc_set_plane_ba0_uv(enum omap_plane plane, u32 paddr)
+static void dispc_ovl_set_ba0_uv(enum omap_plane plane, u32 paddr)
{
dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr);
}
-static void _dispc_set_plane_ba1_uv(enum omap_plane plane, u32 paddr)
+static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr)
{
dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr);
}
-static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y)
+static void dispc_ovl_set_pos(enum omap_plane plane, int x, int y)
{
u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
dispc_write_reg(DISPC_OVL_POSITION(plane), val);
}
-static void _dispc_set_pic_size(enum omap_plane plane, int width, int height)
+static void dispc_ovl_set_pic_size(enum omap_plane plane, int width, int height)
{
u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
@@ -874,7 +728,7 @@ static void _dispc_set_pic_size(enum omap_plane plane, int width, int height)
dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
}
-static void _dispc_set_vid_size(enum omap_plane plane, int width, int height)
+static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height)
{
u32 val;
@@ -885,44 +739,61 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height)
dispc_write_reg(DISPC_OVL_SIZE(plane), val);
}
-static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable)
+static void dispc_ovl_set_zorder(enum omap_plane plane, u8 zorder)
{
- if (!dss_has_feature(FEAT_PRE_MULT_ALPHA))
+ struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
return;
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
- plane == OMAP_DSS_VIDEO1)
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26);
+}
+
+static void dispc_ovl_enable_zorder_planes(void)
+{
+ int i;
+
+ if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
return;
- REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
+ for (i = 0; i < dss_feat_get_num_ovls(); i++)
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25);
}
-static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
+static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, bool enable)
{
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
return;
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
- plane == OMAP_DSS_VIDEO1)
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
+}
+
+static void dispc_ovl_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
+{
+ static const unsigned shifts[] = { 0, 8, 16, 24, };
+ int shift;
+ struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
return;
- if (plane == OMAP_DSS_GFX)
- REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0);
- else if (plane == OMAP_DSS_VIDEO2)
- REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16);
+ shift = shifts[plane];
+ REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift);
}
-static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc)
+static void dispc_ovl_set_pix_inc(enum omap_plane plane, s32 inc)
{
dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc);
}
-static void _dispc_set_row_inc(enum omap_plane plane, s32 inc)
+static void dispc_ovl_set_row_inc(enum omap_plane plane, s32 inc)
{
dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc);
}
-static void _dispc_set_color_mode(enum omap_plane plane,
+static void dispc_ovl_set_color_mode(enum omap_plane plane,
enum omap_color_mode color_mode)
{
u32 m = 0;
@@ -1003,7 +874,7 @@ static void _dispc_set_color_mode(enum omap_plane plane,
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}
-void dispc_set_channel_out(enum omap_plane plane,
+static void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel)
{
int shift;
@@ -1016,6 +887,7 @@ void dispc_set_channel_out(enum omap_plane plane,
break;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
shift = 16;
break;
default:
@@ -1050,24 +922,13 @@ void dispc_set_channel_out(enum omap_plane plane,
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
}
-static void dispc_set_burst_size(enum omap_plane plane,
+static void dispc_ovl_set_burst_size(enum omap_plane plane,
enum omap_burst_size burst_size)
{
+ static const unsigned shifts[] = { 6, 14, 14, 14, };
int shift;
- switch (plane) {
- case OMAP_DSS_GFX:
- shift = 6;
- break;
- case OMAP_DSS_VIDEO1:
- case OMAP_DSS_VIDEO2:
- shift = 14;
- break;
- default:
- BUG();
- return;
- }
-
+ shift = shifts[plane];
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift);
}
@@ -1078,10 +939,10 @@ static void dispc_configure_burst_sizes(void)
/* Configure burst size always to maximum size */
for (i = 0; i < omap_dss_get_num_overlays(); ++i)
- dispc_set_burst_size(i, burst_size);
+ dispc_ovl_set_burst_size(i, burst_size);
}
-u32 dispc_get_burst_size(enum omap_plane plane)
+u32 dispc_ovl_get_burst_size(enum omap_plane plane)
{
unsigned unit = dss_feat_get_burst_size_unit();
/* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
@@ -1102,7 +963,7 @@ void dispc_enable_gamma_table(bool enable)
REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
}
-void dispc_enable_cpr(enum omap_channel channel, bool enable)
+void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
{
u16 reg;
@@ -1116,12 +977,12 @@ void dispc_enable_cpr(enum omap_channel channel, bool enable)
REG_FLD_MOD(reg, enable, 15, 15);
}
-void dispc_set_cpr_coef(enum omap_channel channel,
+void dispc_mgr_set_cpr_coef(enum omap_channel channel,
struct omap_dss_cpr_coefs *coefs)
{
u32 coef_r, coef_g, coef_b;
- if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2)
+ if (!dispc_mgr_is_lcd(channel))
return;
coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
@@ -1136,7 +997,7 @@ void dispc_set_cpr_coef(enum omap_channel channel,
dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
}
-static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
+static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable)
{
u32 val;
@@ -1147,19 +1008,16 @@ static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
}
-void dispc_enable_replication(enum omap_plane plane, bool enable)
+static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable)
{
- int bit;
-
- if (plane == OMAP_DSS_GFX)
- bit = 5;
- else
- bit = 10;
+ static const unsigned shifts[] = { 5, 10, 10, 10 };
+ int shift;
- REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
+ shift = shifts[plane];
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift);
}
-void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height)
+void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height)
{
u32 val;
BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
@@ -1186,19 +1044,20 @@ static void dispc_read_plane_fifo_sizes(void)
dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
- for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
+ for (plane = 0; plane < dss_feat_get_num_ovls(); ++plane) {
size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end);
size *= unit;
dispc.fifo_size[plane] = size;
}
}
-u32 dispc_get_plane_fifo_size(enum omap_plane plane)
+u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
{
return dispc.fifo_size[plane];
}
-void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
+static void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low,
+ u32 high)
{
u8 hi_start, hi_end, lo_start, lo_end;
u32 unit;
@@ -1233,7 +1092,7 @@ void dispc_enable_fifomerge(bool enable)
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
}
-static void _dispc_set_fir(enum omap_plane plane,
+static void dispc_ovl_set_fir(enum omap_plane plane,
int hinc, int vinc,
enum omap_color_component color_comp)
{
@@ -1256,7 +1115,7 @@ static void _dispc_set_fir(enum omap_plane plane,
}
}
-static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
+static void dispc_ovl_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
{
u32 val;
u8 hor_start, hor_end, vert_start, vert_end;
@@ -1270,7 +1129,7 @@ static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
dispc_write_reg(DISPC_OVL_ACCU0(plane), val);
}
-static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
+static void dispc_ovl_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
{
u32 val;
u8 hor_start, hor_end, vert_start, vert_end;
@@ -1284,7 +1143,8 @@ static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
dispc_write_reg(DISPC_OVL_ACCU1(plane), val);
}
-static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu)
+static void dispc_ovl_set_vid_accu2_0(enum omap_plane plane, int haccu,
+ int vaccu)
{
u32 val;
@@ -1292,7 +1152,8 @@ static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu)
dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val);
}
-static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu)
+static void dispc_ovl_set_vid_accu2_1(enum omap_plane plane, int haccu,
+ int vaccu)
{
u32 val;
@@ -1300,7 +1161,7 @@ static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu)
dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val);
}
-static void _dispc_set_scale_param(enum omap_plane plane,
+static void dispc_ovl_set_scale_param(enum omap_plane plane,
u16 orig_width, u16 orig_height,
u16 out_width, u16 out_height,
bool five_taps, u8 rotation,
@@ -1312,15 +1173,16 @@ static void _dispc_set_scale_param(enum omap_plane plane,
hscaleup = orig_width <= out_width;
vscaleup = orig_height <= out_height;
- _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps, color_comp);
+ dispc_ovl_set_scale_coef(plane, hscaleup, vscaleup, five_taps,
+ color_comp);
fir_hinc = 1024 * orig_width / out_width;
fir_vinc = 1024 * orig_height / out_height;
- _dispc_set_fir(plane, fir_hinc, fir_vinc, color_comp);
+ dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp);
}
-static void _dispc_set_scaling_common(enum omap_plane plane,
+static void dispc_ovl_set_scaling_common(enum omap_plane plane,
u16 orig_width, u16 orig_height,
u16 out_width, u16 out_height,
bool ilace, bool five_taps,
@@ -1331,7 +1193,7 @@ static void _dispc_set_scaling_common(enum omap_plane plane,
int accu1 = 0;
u32 l;
- _dispc_set_scale_param(plane, orig_width, orig_height,
+ dispc_ovl_set_scale_param(plane, orig_width, orig_height,
out_width, out_height, five_taps,
rotation, DISPC_COLOR_COMPONENT_RGB_Y);
l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
@@ -1370,11 +1232,11 @@ static void _dispc_set_scaling_common(enum omap_plane plane,
}
}
- _dispc_set_vid_accu0(plane, 0, accu0);
- _dispc_set_vid_accu1(plane, 0, accu1);
+ dispc_ovl_set_vid_accu0(plane, 0, accu0);
+ dispc_ovl_set_vid_accu1(plane, 0, accu1);
}
-static void _dispc_set_scaling_uv(enum omap_plane plane,
+static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
u16 orig_width, u16 orig_height,
u16 out_width, u16 out_height,
bool ilace, bool five_taps,
@@ -1422,7 +1284,7 @@ static void _dispc_set_scaling_uv(enum omap_plane plane,
if (out_height != orig_height)
scale_y = true;
- _dispc_set_scale_param(plane, orig_width, orig_height,
+ dispc_ovl_set_scale_param(plane, orig_width, orig_height,
out_width, out_height, five_taps,
rotation, DISPC_COLOR_COMPONENT_UV);
@@ -1433,11 +1295,11 @@ static void _dispc_set_scaling_uv(enum omap_plane plane,
/* set V scaling */
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6);
- _dispc_set_vid_accu2_0(plane, 0x80, 0);
- _dispc_set_vid_accu2_1(plane, 0x80, 0);
+ dispc_ovl_set_vid_accu2_0(plane, 0x80, 0);
+ dispc_ovl_set_vid_accu2_1(plane, 0x80, 0);
}
-static void _dispc_set_scaling(enum omap_plane plane,
+static void dispc_ovl_set_scaling(enum omap_plane plane,
u16 orig_width, u16 orig_height,
u16 out_width, u16 out_height,
bool ilace, bool five_taps,
@@ -1446,14 +1308,14 @@ static void _dispc_set_scaling(enum omap_plane plane,
{
BUG_ON(plane == OMAP_DSS_GFX);
- _dispc_set_scaling_common(plane,
+ dispc_ovl_set_scaling_common(plane,
orig_width, orig_height,
out_width, out_height,
ilace, five_taps,
fieldmode, color_mode,
rotation);
- _dispc_set_scaling_uv(plane,
+ dispc_ovl_set_scaling_uv(plane,
orig_width, orig_height,
out_width, out_height,
ilace, five_taps,
@@ -1461,7 +1323,7 @@ static void _dispc_set_scaling(enum omap_plane plane,
rotation);
}
-static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
+static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
bool mirroring, enum omap_color_mode color_mode)
{
bool row_repeat = false;
@@ -1789,12 +1651,11 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width,
enum omap_color_mode color_mode)
{
u32 fclk = 0;
- /* FIXME venc pclk? */
- u64 tmp, pclk = dispc_pclk_rate(channel);
+ u64 tmp, pclk = dispc_mgr_pclk_rate(channel);
if (height > out_height) {
- /* FIXME get real display PPL */
- unsigned int ppl = 800;
+ struct omap_dss_device *dssdev = dispc_mgr_get_device(channel);
+ unsigned int ppl = dssdev->panel.timings.x_res;
tmp = pclk * height * out_width;
do_div(tmp, 2 * out_height * ppl);
@@ -1846,114 +1707,120 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
else
vf = 1;
- /* FIXME venc pclk? */
- return dispc_pclk_rate(channel) * vf * hf;
+ return dispc_mgr_pclk_rate(channel) * vf * hf;
}
-int dispc_setup_plane(enum omap_plane plane,
- u32 paddr, u16 screen_width,
- u16 pos_x, u16 pos_y,
- u16 width, u16 height,
+static int dispc_ovl_calc_scaling(enum omap_plane plane,
+ enum omap_channel channel, u16 width, u16 height,
u16 out_width, u16 out_height,
- enum omap_color_mode color_mode,
- bool ilace,
- enum omap_dss_rotation_type rotation_type,
- u8 rotation, bool mirror,
- u8 global_alpha, u8 pre_mult_alpha,
- enum omap_channel channel, u32 puv_addr)
-{
- const int maxdownscale = cpu_is_omap34xx() ? 4 : 2;
- bool five_taps = 0;
- bool fieldmode = 0;
- int cconv = 0;
- unsigned offset0, offset1;
- s32 row_inc;
- s32 pix_inc;
- u16 frame_height = height;
- unsigned int field_offset = 0;
+ enum omap_color_mode color_mode, bool *five_taps)
+{
+ struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+ const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
+ unsigned long fclk = 0;
- DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
- "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n",
- plane, paddr, screen_width, pos_x, pos_y,
- width, height,
- out_width, out_height,
- ilace, color_mode,
- rotation, mirror, channel);
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+ if (width != out_width || height != out_height)
+ return -EINVAL;
+ else
+ return 0;
+ }
- if (paddr == 0)
+ if (out_width < width / maxdownscale ||
+ out_width > width * 8)
return -EINVAL;
- if (ilace && height == out_height)
- fieldmode = 1;
+ if (out_height < height / maxdownscale ||
+ out_height > height * 8)
+ return -EINVAL;
- if (ilace) {
- if (fieldmode)
- height /= 2;
- pos_y /= 2;
- out_height /= 2;
+ /* Must use 5-tap filter? */
+ *five_taps = height > out_height * 2;
- DSSDBG("adjusting for ilace: height %d, pos_y %d, "
- "out_height %d\n",
- height, pos_y, out_height);
+ if (!*five_taps) {
+ fclk = calc_fclk(channel, width, height, out_width,
+ out_height);
+
+ /* Try 5-tap filter if 3-tap fclk is too high */
+ if (cpu_is_omap34xx() && height > out_height &&
+ fclk > dispc_fclk_rate())
+ *five_taps = true;
}
- if (!dss_feat_color_mode_supported(plane, color_mode))
+ if (width > (2048 >> *five_taps)) {
+ DSSERR("failed to set up scaling, fclk too low\n");
return -EINVAL;
+ }
- if (plane == OMAP_DSS_GFX) {
- if (width != out_width || height != out_height)
- return -EINVAL;
- } else {
- /* video plane */
+ if (*five_taps)
+ fclk = calc_fclk_five_taps(channel, width, height,
+ out_width, out_height, color_mode);
- unsigned long fclk = 0;
+ DSSDBG("required fclk rate = %lu Hz\n", fclk);
+ DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
- if (out_width < width / maxdownscale ||
- out_width > width * 8)
- return -EINVAL;
+ if (!fclk || fclk > dispc_fclk_rate()) {
+ DSSERR("failed to set up scaling, "
+ "required fclk rate = %lu Hz, "
+ "current fclk rate = %lu Hz\n",
+ fclk, dispc_fclk_rate());
+ return -EINVAL;
+ }
- if (out_height < height / maxdownscale ||
- out_height > height * 8)
- return -EINVAL;
+ return 0;
+}
- if (color_mode == OMAP_DSS_COLOR_YUV2 ||
- color_mode == OMAP_DSS_COLOR_UYVY ||
- color_mode == OMAP_DSS_COLOR_NV12)
- cconv = 1;
+int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
+ bool ilace, enum omap_channel channel, bool replication,
+ u32 fifo_low, u32 fifo_high)
+{
+ struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+ bool five_taps = false;
+ bool fieldmode = 0;
+ int r, cconv = 0;
+ unsigned offset0, offset1;
+ s32 row_inc;
+ s32 pix_inc;
+ u16 frame_height = oi->height;
+ unsigned int field_offset = 0;
- /* Must use 5-tap filter? */
- five_taps = height > out_height * 2;
+ DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
+ "%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d "
+ "fifo_low %d fifo high %d\n", plane, oi->paddr, oi->p_uv_addr,
+ oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
+ oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
+ oi->mirror, ilace, channel, replication, fifo_low, fifo_high);
- if (!five_taps) {
- fclk = calc_fclk(channel, width, height, out_width,
- out_height);
+ if (oi->paddr == 0)
+ return -EINVAL;
- /* Try 5-tap filter if 3-tap fclk is too high */
- if (cpu_is_omap34xx() && height > out_height &&
- fclk > dispc_fclk_rate())
- five_taps = true;
- }
+ if (ilace && oi->height == oi->out_height)
+ fieldmode = 1;
- if (width > (2048 >> five_taps)) {
- DSSERR("failed to set up scaling, fclk too low\n");
- return -EINVAL;
- }
+ if (ilace) {
+ if (fieldmode)
+ oi->height /= 2;
+ oi->pos_y /= 2;
+ oi->out_height /= 2;
- if (five_taps)
- fclk = calc_fclk_five_taps(channel, width, height,
- out_width, out_height, color_mode);
+ DSSDBG("adjusting for ilace: height %d, pos_y %d, "
+ "out_height %d\n",
+ oi->height, oi->pos_y, oi->out_height);
+ }
- DSSDBG("required fclk rate = %lu Hz\n", fclk);
- DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
+ if (!dss_feat_color_mode_supported(plane, oi->color_mode))
+ return -EINVAL;
- if (!fclk || fclk > dispc_fclk_rate()) {
- DSSERR("failed to set up scaling, "
- "required fclk rate = %lu Hz, "
- "current fclk rate = %lu Hz\n",
- fclk, dispc_fclk_rate());
- return -EINVAL;
- }
- }
+ r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height,
+ oi->out_width, oi->out_height, oi->color_mode,
+ &five_taps);
+ if (r)
+ return r;
+
+ if (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
+ oi->color_mode == OMAP_DSS_COLOR_UYVY ||
+ oi->color_mode == OMAP_DSS_COLOR_NV12)
+ cconv = 1;
if (ilace && !fieldmode) {
/*
@@ -1963,69 +1830,76 @@ int dispc_setup_plane(enum omap_plane plane,
* so the integer part must be added to the base address of the
* bottom field.
*/
- if (!height || height == out_height)
+ if (!oi->height || oi->height == oi->out_height)
field_offset = 0;
else
- field_offset = height / out_height / 2;
+ field_offset = oi->height / oi->out_height / 2;
}
/* Fields are independent but interleaved in memory. */
if (fieldmode)
field_offset = 1;
- if (rotation_type == OMAP_DSS_ROT_DMA)
- calc_dma_rotation_offset(rotation, mirror,
- screen_width, width, frame_height, color_mode,
- fieldmode, field_offset,
+ if (oi->rotation_type == OMAP_DSS_ROT_DMA)
+ calc_dma_rotation_offset(oi->rotation, oi->mirror,
+ oi->screen_width, oi->width, frame_height,
+ oi->color_mode, fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc);
else
- calc_vrfb_rotation_offset(rotation, mirror,
- screen_width, width, frame_height, color_mode,
- fieldmode, field_offset,
+ calc_vrfb_rotation_offset(oi->rotation, oi->mirror,
+ oi->screen_width, oi->width, frame_height,
+ oi->color_mode, fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc);
DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
offset0, offset1, row_inc, pix_inc);
- _dispc_set_color_mode(plane, color_mode);
+ dispc_ovl_set_color_mode(plane, oi->color_mode);
- _dispc_set_plane_ba0(plane, paddr + offset0);
- _dispc_set_plane_ba1(plane, paddr + offset1);
+ dispc_ovl_set_ba0(plane, oi->paddr + offset0);
+ dispc_ovl_set_ba1(plane, oi->paddr + offset1);
- if (OMAP_DSS_COLOR_NV12 == color_mode) {
- _dispc_set_plane_ba0_uv(plane, puv_addr + offset0);
- _dispc_set_plane_ba1_uv(plane, puv_addr + offset1);
+ if (OMAP_DSS_COLOR_NV12 == oi->color_mode) {
+ dispc_ovl_set_ba0_uv(plane, oi->p_uv_addr + offset0);
+ dispc_ovl_set_ba1_uv(plane, oi->p_uv_addr + offset1);
}
- _dispc_set_row_inc(plane, row_inc);
- _dispc_set_pix_inc(plane, pix_inc);
+ dispc_ovl_set_row_inc(plane, row_inc);
+ dispc_ovl_set_pix_inc(plane, pix_inc);
- DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height,
- out_width, out_height);
+ DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width,
+ oi->height, oi->out_width, oi->out_height);
- _dispc_set_plane_pos(plane, pos_x, pos_y);
+ dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y);
- _dispc_set_pic_size(plane, width, height);
+ dispc_ovl_set_pic_size(plane, oi->width, oi->height);
- if (plane != OMAP_DSS_GFX) {
- _dispc_set_scaling(plane, width, height,
- out_width, out_height,
+ if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) {
+ dispc_ovl_set_scaling(plane, oi->width, oi->height,
+ oi->out_width, oi->out_height,
ilace, five_taps, fieldmode,
- color_mode, rotation);
- _dispc_set_vid_size(plane, out_width, out_height);
- _dispc_set_vid_color_conv(plane, cconv);
+ oi->color_mode, oi->rotation);
+ dispc_ovl_set_vid_size(plane, oi->out_width, oi->out_height);
+ dispc_ovl_set_vid_color_conv(plane, cconv);
}
- _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode);
+ dispc_ovl_set_rotation_attrs(plane, oi->rotation, oi->mirror,
+ oi->color_mode);
+
+ dispc_ovl_set_zorder(plane, oi->zorder);
+ dispc_ovl_set_pre_mult_alpha(plane, oi->pre_mult_alpha);
+ dispc_ovl_setup_global_alpha(plane, oi->global_alpha);
- _dispc_set_pre_mult_alpha(plane, pre_mult_alpha);
- _dispc_setup_global_alpha(plane, global_alpha);
+ dispc_ovl_set_channel_out(plane, channel);
+
+ dispc_ovl_enable_replication(plane, replication);
+ dispc_ovl_set_fifo_threshold(plane, fifo_low, fifo_high);
return 0;
}
-int dispc_enable_plane(enum omap_plane plane, bool enable)
+int dispc_ovl_enable(enum omap_plane plane, bool enable)
{
DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
@@ -2048,7 +1922,7 @@ static void _enable_lcd_out(enum omap_channel channel, bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
}
-static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
{
struct completion frame_done_completion;
bool is_on;
@@ -2095,14 +1969,19 @@ static void _enable_digit_out(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
}
-static void dispc_enable_digit_out(bool enable)
+static void dispc_mgr_enable_digit_out(bool enable)
{
struct completion frame_done_completion;
- int r;
+ enum dss_hdmi_venc_clk_source_select src;
+ int r, i;
+ u32 irq_mask;
+ int num_irqs;
if (REG_GET(DISPC_CONTROL, 1, 1) == enable)
return;
+ src = dss_get_hdmi_venc_clk_source();
+
if (enable) {
unsigned long flags;
/* When we enable digit output, we'll get an extra digit
@@ -2119,43 +1998,47 @@ static void dispc_enable_digit_out(bool enable)
* wait for the extra sync losts */
init_completion(&frame_done_completion);
+ if (src == DSS_HDMI_M_PCLK && enable == false) {
+ irq_mask = DISPC_IRQ_FRAMEDONETV;
+ num_irqs = 1;
+ } else {
+ irq_mask = DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
+ /* XXX I understand from TRM that we should only wait for the
+ * current field to complete. But it seems we have to wait for
+ * both fields */
+ num_irqs = 2;
+ }
+
r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion,
- DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ irq_mask);
if (r)
- DSSERR("failed to register EVSYNC isr\n");
+ DSSERR("failed to register %x isr\n", irq_mask);
_enable_digit_out(enable);
- /* XXX I understand from TRM that we should only wait for the
- * current field to complete. But it seems we have to wait
- * for both fields */
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for EVSYNC\n");
-
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for EVSYNC\n");
+ for (i = 0; i < num_irqs; ++i) {
+ if (!wait_for_completion_timeout(&frame_done_completion,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for digit out to %s\n",
+ enable ? "start" : "stop");
+ }
- r = omap_dispc_unregister_isr(dispc_disable_isr,
- &frame_done_completion,
- DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion,
+ irq_mask);
if (r)
- DSSERR("failed to unregister EVSYNC isr\n");
+ DSSERR("failed to unregister %x isr\n", irq_mask);
if (enable) {
unsigned long flags;
spin_lock_irqsave(&dispc.irq_lock, flags);
- dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
- if (dss_has_feature(FEAT_MGR_LCD2))
- dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+ dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST_DIGIT;
dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc.irq_lock, flags);
}
}
-bool dispc_is_channel_enabled(enum omap_channel channel)
+bool dispc_mgr_is_enabled(enum omap_channel channel)
{
if (channel == OMAP_DSS_CHANNEL_LCD)
return !!REG_GET(DISPC_CONTROL, 0, 0);
@@ -2167,13 +2050,12 @@ bool dispc_is_channel_enabled(enum omap_channel channel)
BUG();
}
-void dispc_enable_channel(enum omap_channel channel, bool enable)
+void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
- dispc_enable_lcd_out(channel, enable);
+ if (dispc_mgr_is_lcd(channel))
+ dispc_mgr_enable_lcd_out(channel, enable);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
- dispc_enable_digit_out(enable);
+ dispc_mgr_enable_digit_out(enable);
else
BUG();
}
@@ -2202,7 +2084,7 @@ void dispc_pck_free_enable(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
}
-void dispc_enable_fifohandcheck(enum omap_channel channel, 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);
@@ -2211,7 +2093,7 @@ void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable)
}
-void dispc_set_lcd_display_type(enum omap_channel channel,
+void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
enum omap_lcd_display_type type)
{
int mode;
@@ -2242,12 +2124,12 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode)
}
-void dispc_set_default_color(enum omap_channel channel, u32 color)
+void dispc_mgr_set_default_color(enum omap_channel channel, u32 color)
{
dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
}
-u32 dispc_get_default_color(enum omap_channel channel)
+u32 dispc_mgr_get_default_color(enum omap_channel channel)
{
u32 l;
@@ -2260,7 +2142,7 @@ u32 dispc_get_default_color(enum omap_channel channel)
return l;
}
-void dispc_set_trans_key(enum omap_channel ch,
+void dispc_mgr_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key)
{
@@ -2274,7 +2156,7 @@ void dispc_set_trans_key(enum omap_channel ch,
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
}
-void dispc_get_trans_key(enum omap_channel ch,
+void dispc_mgr_get_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type *type,
u32 *trans_key)
{
@@ -2293,7 +2175,7 @@ void dispc_get_trans_key(enum omap_channel ch,
*trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch));
}
-void dispc_enable_trans_key(enum omap_channel ch, bool enable)
+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);
@@ -2302,39 +2184,36 @@ void dispc_enable_trans_key(enum omap_channel ch, bool enable)
else /* OMAP_DSS_CHANNEL_LCD2 */
REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
}
-void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
+
+void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable)
{
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return;
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18);
}
-bool dispc_alpha_blending_enabled(enum omap_channel ch)
+
+bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch)
{
bool enabled;
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return false;
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
enabled = REG_GET(DISPC_CONFIG, 19, 19);
- else if (ch == OMAP_DSS_CHANNEL_LCD2)
- enabled = REG_GET(DISPC_CONFIG2, 18, 18);
else
BUG();
return enabled;
}
-
-bool dispc_trans_key_enabled(enum omap_channel ch)
+bool dispc_mgr_trans_key_enabled(enum omap_channel ch)
{
bool enabled;
@@ -2351,7 +2230,7 @@ bool dispc_trans_key_enabled(enum omap_channel ch)
}
-void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
+void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
{
int code;
@@ -2379,46 +2258,41 @@ void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
}
-void dispc_set_parallel_interface_mode(enum omap_channel channel,
- enum omap_parallel_interface_mode mode)
+void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
{
u32 l;
- int stallmode;
- int gpout0 = 1;
- int gpout1;
+ int gpout0, gpout1;
switch (mode) {
- case OMAP_DSS_PARALLELMODE_BYPASS:
- stallmode = 0;
- gpout1 = 1;
+ case DSS_IO_PAD_MODE_RESET:
+ gpout0 = 0;
+ gpout1 = 0;
break;
-
- case OMAP_DSS_PARALLELMODE_RFBI:
- stallmode = 1;
+ case DSS_IO_PAD_MODE_RFBI:
+ gpout0 = 1;
gpout1 = 0;
break;
-
- case OMAP_DSS_PARALLELMODE_DSI:
- stallmode = 1;
+ case DSS_IO_PAD_MODE_BYPASS:
+ gpout0 = 1;
gpout1 = 1;
break;
-
default:
BUG();
return;
}
- if (channel == OMAP_DSS_CHANNEL_LCD2) {
- l = dispc_read_reg(DISPC_CONTROL2);
- l = FLD_MOD(l, stallmode, 11, 11);
- dispc_write_reg(DISPC_CONTROL2, l);
- } else {
- l = dispc_read_reg(DISPC_CONTROL);
- l = FLD_MOD(l, stallmode, 11, 11);
- l = FLD_MOD(l, gpout0, 15, 15);
- l = FLD_MOD(l, gpout1, 16, 16);
- dispc_write_reg(DISPC_CONTROL, l);
- }
+ l = dispc_read_reg(DISPC_CONTROL);
+ l = FLD_MOD(l, gpout0, 15, 15);
+ l = FLD_MOD(l, gpout1, 16, 16);
+ dispc_write_reg(DISPC_CONTROL, l);
+}
+
+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);
}
static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
@@ -2452,7 +2326,7 @@ bool dispc_lcd_timings_ok(struct omap_video_timings *timings)
timings->vfp, timings->vbp);
}
-static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw,
+static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
int hfp, int hbp, int vsw, int vfp, int vbp)
{
u32 timing_h, timing_v;
@@ -2476,7 +2350,7 @@ static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw,
}
/* change name to mode? */
-void dispc_set_lcd_timings(enum omap_channel channel,
+void dispc_mgr_set_lcd_timings(enum omap_channel channel,
struct omap_video_timings *timings)
{
unsigned xtot, ytot;
@@ -2487,11 +2361,11 @@ void dispc_set_lcd_timings(enum omap_channel channel,
timings->vfp, timings->vbp))
BUG();
- _dispc_set_lcd_timings(channel, timings->hsw, timings->hfp,
+ _dispc_mgr_set_lcd_timings(channel, timings->hsw, timings->hfp,
timings->hbp, timings->vsw, timings->vfp,
timings->vbp);
- dispc_set_lcd_size(channel, timings->x_res, timings->y_res);
+ dispc_mgr_set_lcd_size(channel, timings->x_res, timings->y_res);
xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp;
ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp;
@@ -2509,17 +2383,17 @@ void dispc_set_lcd_timings(enum omap_channel channel,
DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
}
-static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
+static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
u16 pck_div)
{
BUG_ON(lck_div < 1);
- BUG_ON(pck_div < 2);
+ BUG_ON(pck_div < 1);
dispc_write_reg(DISPC_DIVISORo(channel),
FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
}
-static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div,
+static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div,
int *pck_div)
{
u32 l;
@@ -2552,7 +2426,7 @@ unsigned long dispc_fclk_rate(void)
return r;
}
-unsigned long dispc_lclk_rate(enum omap_channel channel)
+unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
{
struct platform_device *dsidev;
int lcd;
@@ -2582,19 +2456,34 @@ unsigned long dispc_lclk_rate(enum omap_channel channel)
return r / lcd;
}
-unsigned long dispc_pclk_rate(enum omap_channel channel)
+unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
{
- int pcd;
unsigned long r;
- u32 l;
- l = dispc_read_reg(DISPC_DIVISORo(channel));
+ if (dispc_mgr_is_lcd(channel)) {
+ int pcd;
+ u32 l;
- pcd = FLD_GET(l, 7, 0);
+ l = dispc_read_reg(DISPC_DIVISORo(channel));
- r = dispc_lclk_rate(channel);
+ pcd = FLD_GET(l, 7, 0);
- return r / pcd;
+ r = dispc_mgr_lclk_rate(channel);
+
+ return r / pcd;
+ } else {
+ struct omap_dss_device *dssdev =
+ dispc_mgr_get_device(channel);
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_VENC:
+ return venc_get_pixel_clock();
+ case OMAP_DISPLAY_TYPE_HDMI:
+ return hdmi_get_pixel_clock();
+ default:
+ BUG();
+ }
+ }
}
void dispc_dump_clocks(struct seq_file *s)
@@ -2631,12 +2520,12 @@ void dispc_dump_clocks(struct seq_file *s)
dss_get_generic_clk_source_name(lcd_clk_src),
dss_feat_get_clk_source_name(lcd_clk_src));
- dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd);
+ dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd);
seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd);
+ dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd);
seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd);
+ dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd);
if (dss_has_feature(FEAT_MGR_LCD2)) {
seq_printf(s, "- LCD2 -\n");
@@ -2646,12 +2535,12 @@ void dispc_dump_clocks(struct seq_file *s)
dss_get_generic_clk_source_name(lcd_clk_src),
dss_feat_get_clk_source_name(lcd_clk_src));
- dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd);
+ dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd);
seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd);
+ dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd);
seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
+ dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
}
dispc_runtime_put();
@@ -2692,6 +2581,10 @@ void dispc_dump_irqs(struct seq_file *s)
PIS(VID1_END_WIN);
PIS(VID2_FIFO_UNDERFLOW);
PIS(VID2_END_WIN);
+ if (dss_feat_get_num_ovls() > 3) {
+ PIS(VID3_FIFO_UNDERFLOW);
+ PIS(VID3_END_WIN);
+ }
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
PIS(WAKEUP);
@@ -2707,11 +2600,26 @@ void dispc_dump_irqs(struct seq_file *s)
void dispc_dump_regs(struct seq_file *s)
{
+ int i, j;
+ const char *mgr_names[] = {
+ [OMAP_DSS_CHANNEL_LCD] = "LCD",
+ [OMAP_DSS_CHANNEL_DIGIT] = "TV",
+ [OMAP_DSS_CHANNEL_LCD2] = "LCD2",
+ };
+ const char *ovl_names[] = {
+ [OMAP_DSS_GFX] = "GFX",
+ [OMAP_DSS_VIDEO1] = "VID1",
+ [OMAP_DSS_VIDEO2] = "VID2",
+ [OMAP_DSS_VIDEO3] = "VID3",
+ };
+ const char **p_names;
+
#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r))
if (dispc_runtime_get())
return;
+ /* DISPC common registers */
DUMPREG(DISPC_REVISION);
DUMPREG(DISPC_SYSCONFIG);
DUMPREG(DISPC_SYSSTATUS);
@@ -2720,247 +2628,139 @@ void dispc_dump_regs(struct seq_file *s)
DUMPREG(DISPC_CONTROL);
DUMPREG(DISPC_CONFIG);
DUMPREG(DISPC_CAPABLE);
- DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT));
- DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT));
DUMPREG(DISPC_LINE_STATUS);
DUMPREG(DISPC_LINE_NUMBER);
- DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD));
- if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
DUMPREG(DISPC_GLOBAL_ALPHA);
- DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
- DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
DUMPREG(DISPC_CONTROL2);
DUMPREG(DISPC_CONFIG2);
- DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD2));
- }
-
- DUMPREG(DISPC_OVL_BA0(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_BA1(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_WINDOW_SKIP(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_TABLE_BA(OMAP_DSS_GFX));
-
- DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
-
- if (dss_has_feature(FEAT_CPR)) {
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
}
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
+
+#undef DUMPREG
+
+#define DISPC_REG(i, name) name(i)
+#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \
+ 48 - strlen(#r) - strlen(p_names[i]), " ", \
+ dispc_read_reg(DISPC_REG(i, r)))
+
+ p_names = mgr_names;
+
+ /* DISPC channel specific registers */
+ for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
+ DUMPREG(i, DISPC_DEFAULT_COLOR);
+ DUMPREG(i, DISPC_TRANS_COLOR);
+ DUMPREG(i, DISPC_SIZE_MGR);
+
+ if (i == OMAP_DSS_CHANNEL_DIGIT)
+ continue;
+
+ DUMPREG(i, DISPC_DEFAULT_COLOR);
+ DUMPREG(i, DISPC_TRANS_COLOR);
+ DUMPREG(i, DISPC_TIMING_H);
+ DUMPREG(i, DISPC_TIMING_V);
+ DUMPREG(i, DISPC_POL_FREQ);
+ DUMPREG(i, DISPC_DIVISORo);
+ DUMPREG(i, DISPC_SIZE_MGR);
+
+ DUMPREG(i, DISPC_DATA_CYCLE1);
+ DUMPREG(i, DISPC_DATA_CYCLE2);
+ DUMPREG(i, DISPC_DATA_CYCLE3);
if (dss_has_feature(FEAT_CPR)) {
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ DUMPREG(i, DISPC_CPR_COEF_R);
+ DUMPREG(i, DISPC_CPR_COEF_G);
+ DUMPREG(i, DISPC_CPR_COEF_B);
+ }
+ }
+
+ p_names = ovl_names;
+
+ for (i = 0; i < dss_feat_get_num_ovls(); i++) {
+ DUMPREG(i, DISPC_OVL_BA0);
+ DUMPREG(i, DISPC_OVL_BA1);
+ DUMPREG(i, DISPC_OVL_POSITION);
+ DUMPREG(i, DISPC_OVL_SIZE);
+ DUMPREG(i, DISPC_OVL_ATTRIBUTES);
+ DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
+ DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
+ DUMPREG(i, DISPC_OVL_ROW_INC);
+ DUMPREG(i, DISPC_OVL_PIXEL_INC);
+ if (dss_has_feature(FEAT_PRELOAD))
+ DUMPREG(i, DISPC_OVL_PRELOAD);
+
+ if (i == OMAP_DSS_GFX) {
+ DUMPREG(i, DISPC_OVL_WINDOW_SKIP);
+ DUMPREG(i, DISPC_OVL_TABLE_BA);
+ continue;
+ }
+
+ DUMPREG(i, DISPC_OVL_FIR);
+ DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
+ DUMPREG(i, DISPC_OVL_ACCU0);
+ DUMPREG(i, DISPC_OVL_ACCU1);
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ DUMPREG(i, DISPC_OVL_BA0_UV);
+ DUMPREG(i, DISPC_OVL_BA1_UV);
+ DUMPREG(i, DISPC_OVL_FIR2);
+ DUMPREG(i, DISPC_OVL_ACCU2_0);
+ DUMPREG(i, DISPC_OVL_ACCU2_1);
}
+ if (dss_has_feature(FEAT_ATTR2))
+ DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
+ if (dss_has_feature(FEAT_PRELOAD))
+ DUMPREG(i, DISPC_OVL_PRELOAD);
}
- if (dss_has_feature(FEAT_PRELOAD))
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));
-
- DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO2));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 7));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 7));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4));
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7));
- }
-
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO1));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 7));
- }
- if (dss_has_feature(FEAT_ATTR2))
- DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
-
-
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 7));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 7));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7));
- }
-
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO2));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 7));
- }
- if (dss_has_feature(FEAT_ATTR2))
- DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
-
- if (dss_has_feature(FEAT_PRELOAD)) {
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2));
+#undef DISPC_REG
+#undef DUMPREG
+
+#define DISPC_REG(plane, name, i) name(plane, i)
+#define DUMPREG(plane, name, i) \
+ seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \
+ 46 - strlen(#name) - strlen(p_names[plane]), " ", \
+ dispc_read_reg(DISPC_REG(plane, name, i)))
+
+ /* Video pipeline coefficient registers */
+
+ /* start from OMAP_DSS_VIDEO1 */
+ for (i = 1; i < dss_feat_get_num_ovls(); i++) {
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_H, j);
+
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j);
+
+ for (j = 0; j < 5; j++)
+ DUMPREG(i, DISPC_OVL_CONV_COEF, j);
+
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_V, j);
+ }
+
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j);
+
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j);
+
+ for (j = 0; j < 8; j++)
+ DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j);
+ }
}
dispc_runtime_put();
+
+#undef DISPC_REG
#undef DUMPREG
}
-static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf,
- bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi, u8 acb)
+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;
@@ -2979,10 +2779,10 @@ static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf,
dispc_write_reg(DISPC_POL_FREQ(channel), l);
}
-void dispc_set_pol_freq(enum omap_channel channel,
+void dispc_mgr_set_pol_freq(enum omap_channel channel,
enum omap_panel_config config, u8 acbi, u8 acb)
{
- _dispc_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0,
+ _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,
@@ -2995,11 +2795,17 @@ void dispc_set_pol_freq(enum omap_channel channel,
void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
struct dispc_clock_info *cinfo)
{
- u16 pcd_min = is_tft ? 2 : 3;
+ u16 pcd_min, pcd_max;
unsigned long best_pck;
u16 best_ld, cur_ld;
u16 best_pd, cur_pd;
+ 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;
@@ -3007,7 +2813,7 @@ void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
for (cur_ld = 1; cur_ld <= 255; ++cur_ld) {
unsigned long lck = fck / cur_ld;
- for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) {
+ for (cur_pd = pcd_min; cur_pd <= pcd_max; ++cur_pd) {
unsigned long pck = lck / cur_pd;
long old_delta = abs(best_pck - req_pck);
long new_delta = abs(pck - req_pck);
@@ -3042,7 +2848,7 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
{
if (cinfo->lck_div > 255 || cinfo->lck_div == 0)
return -EINVAL;
- if (cinfo->pck_div < 2 || cinfo->pck_div > 255)
+ if (cinfo->pck_div < 1 || cinfo->pck_div > 255)
return -EINVAL;
cinfo->lck = dispc_fclk_rate / cinfo->lck_div;
@@ -3051,18 +2857,18 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
return 0;
}
-int dispc_set_clock_div(enum omap_channel channel,
+int 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_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
+ dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
return 0;
}
-int dispc_get_clock_div(enum omap_channel channel,
+int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo)
{
unsigned long fck;
@@ -3207,6 +3013,8 @@ static void print_irq_status(u32 status)
PIS(OCP_ERR);
PIS(VID1_FIFO_UNDERFLOW);
PIS(VID2_FIFO_UNDERFLOW);
+ if (dss_feat_get_num_ovls() > 3)
+ PIS(VID3_FIFO_UNDERFLOW);
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
if (dss_has_feature(FEAT_MGR_LCD2))
@@ -3300,178 +3108,72 @@ static void dispc_error_worker(struct work_struct *work)
int i;
u32 errors;
unsigned long flags;
+ static const unsigned fifo_underflow_bits[] = {
+ DISPC_IRQ_GFX_FIFO_UNDERFLOW,
+ DISPC_IRQ_VID1_FIFO_UNDERFLOW,
+ DISPC_IRQ_VID2_FIFO_UNDERFLOW,
+ 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;
spin_unlock_irqrestore(&dispc.irq_lock, flags);
- if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) {
- DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n");
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
- if (ovl->id == 0) {
- dispc_enable_plane(ovl->id, 0);
- dispc_go(ovl->manager->id);
- mdelay(50);
- break;
- }
- }
- }
-
- if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) {
- DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n");
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
- if (ovl->id == 1) {
- dispc_enable_plane(ovl->id, 0);
- dispc_go(ovl->manager->id);
- mdelay(50);
- break;
- }
- }
- }
-
- if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) {
- DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n");
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
+ dispc_runtime_get();
- if (ovl->id == 2) {
- dispc_enable_plane(ovl->id, 0);
- dispc_go(ovl->manager->id);
- mdelay(50);
- break;
- }
- }
- }
-
- if (errors & DISPC_IRQ_SYNC_LOST) {
- struct omap_overlay_manager *manager = NULL;
- bool enable = false;
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ unsigned bit;
- DSSERR("SYNC_LOST, disabling LCD\n");
+ ovl = omap_dss_get_overlay(i);
+ bit = fifo_underflow_bits[i];
- for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
- struct omap_overlay_manager *mgr;
- mgr = omap_dss_get_overlay_manager(i);
-
- if (mgr->id == OMAP_DSS_CHANNEL_LCD) {
- manager = mgr;
- enable = mgr->device->state ==
- OMAP_DSS_DISPLAY_ACTIVE;
- mgr->device->driver->disable(mgr->device);
- break;
- }
- }
-
- if (manager) {
- struct omap_dss_device *dssdev = manager->device;
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
- if (ovl->id != 0 && ovl->manager == manager)
- dispc_enable_plane(ovl->id, 0);
- }
-
- dispc_go(manager->id);
+ if (bit & errors) {
+ DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
+ ovl->name);
+ dispc_ovl_enable(ovl->id, false);
+ dispc_mgr_go(ovl->manager->id);
mdelay(50);
- if (enable)
- dssdev->driver->enable(dssdev);
}
}
- if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) {
- struct omap_overlay_manager *manager = NULL;
- bool enable = false;
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ struct omap_overlay_manager *mgr;
+ unsigned bit;
- DSSERR("SYNC_LOST_DIGIT, disabling TV\n");
+ mgr = omap_dss_get_overlay_manager(i);
+ bit = sync_lost_bits[i];
- for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
- struct omap_overlay_manager *mgr;
- mgr = omap_dss_get_overlay_manager(i);
+ if (bit & errors) {
+ struct omap_dss_device *dssdev = mgr->device;
+ bool enable;
- if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) {
- manager = mgr;
- enable = mgr->device->state ==
- OMAP_DSS_DISPLAY_ACTIVE;
- mgr->device->driver->disable(mgr->device);
- break;
- }
- }
+ DSSERR("SYNC_LOST on channel %s, restarting the output "
+ "with video overlays disabled\n",
+ mgr->name);
- if (manager) {
- struct omap_dss_device *dssdev = manager->device;
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
- if (ovl->id != 0 && ovl->manager == manager)
- dispc_enable_plane(ovl->id, 0);
- }
+ enable = dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
+ dssdev->driver->disable(dssdev);
- dispc_go(manager->id);
- mdelay(50);
- if (enable)
- dssdev->driver->enable(dssdev);
- }
- }
-
- if (errors & DISPC_IRQ_SYNC_LOST2) {
- struct omap_overlay_manager *manager = NULL;
- bool enable = false;
-
- DSSERR("SYNC_LOST for LCD2, disabling LCD2\n");
-
- for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
- struct omap_overlay_manager *mgr;
- mgr = omap_dss_get_overlay_manager(i);
-
- if (mgr->id == OMAP_DSS_CHANNEL_LCD2) {
- manager = mgr;
- enable = mgr->device->state ==
- OMAP_DSS_DISPLAY_ACTIVE;
- mgr->device->driver->disable(mgr->device);
- break;
- }
- }
-
- if (manager) {
- struct omap_dss_device *dssdev = manager->device;
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
- if (ovl->id != 0 && ovl->manager == manager)
- dispc_enable_plane(ovl->id, 0);
+ if (ovl->id != OMAP_DSS_GFX &&
+ ovl->manager == mgr)
+ dispc_ovl_enable(ovl->id, false);
}
- dispc_go(manager->id);
+ dispc_mgr_go(mgr->id);
mdelay(50);
+
if (enable)
dssdev->driver->enable(dssdev);
}
@@ -3482,9 +3184,7 @@ static void dispc_error_worker(struct work_struct *work)
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
-
- if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC)
- mgr->device->driver->disable(mgr->device);
+ mgr->device->driver->disable(mgr->device);
}
}
@@ -3492,6 +3192,8 @@ static void dispc_error_worker(struct work_struct *work)
dispc.irq_error_mask |= errors;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+ dispc_runtime_put();
}
int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout)
@@ -3586,6 +3288,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_feat_get_num_ovls() > 3)
+ dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
/* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
* so clear it */
@@ -3635,6 +3339,8 @@ static void _omap_dispc_initial_config(void)
dispc_read_plane_fifo_sizes();
dispc_configure_burst_sizes();
+
+ dispc_ovl_enable_zorder_planes();
}
/* DISPC HW IP initialisation */
@@ -3734,7 +3440,6 @@ static int omap_dispchw_remove(struct platform_device *pdev)
static int dispc_runtime_suspend(struct device *dev)
{
dispc_save_context();
- clk_disable(dispc.dss_clk);
dss_runtime_put();
return 0;
@@ -3748,7 +3453,6 @@ static int dispc_runtime_resume(struct device *dev)
if (r < 0)
return r;
- clk_enable(dispc.dss_clk);
dispc_restore_context();
return 0;
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index 6c9ee0a0efb3..c06efc38983e 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -291,6 +291,8 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
return 0x00BC;
case OMAP_DSS_VIDEO2:
return 0x014C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0300;
default:
BUG();
}
@@ -304,6 +306,8 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0000;
+ case OMAP_DSS_VIDEO3:
+ return 0x0008;
default:
BUG();
}
@@ -316,6 +320,8 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0004;
+ case OMAP_DSS_VIDEO3:
+ return 0x000C;
default:
BUG();
}
@@ -330,6 +336,8 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane)
return 0x0544;
case OMAP_DSS_VIDEO2:
return 0x04BC;
+ case OMAP_DSS_VIDEO3:
+ return 0x0310;
default:
BUG();
}
@@ -344,6 +352,8 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane)
return 0x0548;
case OMAP_DSS_VIDEO2:
return 0x04C0;
+ case OMAP_DSS_VIDEO3:
+ return 0x0314;
default:
BUG();
}
@@ -356,6 +366,8 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0008;
+ case OMAP_DSS_VIDEO3:
+ return 0x009C;
default:
BUG();
}
@@ -368,6 +380,8 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x000C;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A8;
default:
BUG();
}
@@ -381,6 +395,8 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0010;
+ case OMAP_DSS_VIDEO3:
+ return 0x0070;
default:
BUG();
}
@@ -395,6 +411,8 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane)
return 0x0568;
case OMAP_DSS_VIDEO2:
return 0x04DC;
+ case OMAP_DSS_VIDEO3:
+ return 0x032C;
default:
BUG();
}
@@ -408,6 +426,8 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0014;
+ case OMAP_DSS_VIDEO3:
+ return 0x008C;
default:
BUG();
}
@@ -421,6 +441,8 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0018;
+ case OMAP_DSS_VIDEO3:
+ return 0x0088;
default:
BUG();
}
@@ -434,6 +456,8 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x001C;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A4;
default:
BUG();
}
@@ -447,6 +471,8 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0020;
+ case OMAP_DSS_VIDEO3:
+ return 0x0098;
default:
BUG();
}
@@ -459,6 +485,7 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane)
return 0x0034;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
BUG();
default:
BUG();
@@ -472,6 +499,7 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane)
return 0x0038;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
BUG();
default:
BUG();
@@ -486,6 +514,8 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0024;
+ case OMAP_DSS_VIDEO3:
+ return 0x0090;
default:
BUG();
}
@@ -500,6 +530,8 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane)
return 0x0580;
case OMAP_DSS_VIDEO2:
return 0x055C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0424;
default:
BUG();
}
@@ -513,6 +545,8 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0028;
+ case OMAP_DSS_VIDEO3:
+ return 0x0094;
default:
BUG();
}
@@ -527,6 +561,8 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x002C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0000;
default:
BUG();
}
@@ -541,6 +577,8 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane)
return 0x0584;
case OMAP_DSS_VIDEO2:
return 0x0560;
+ case OMAP_DSS_VIDEO3:
+ return 0x0428;
default:
BUG();
}
@@ -554,6 +592,8 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0030;
+ case OMAP_DSS_VIDEO3:
+ return 0x0004;
default:
BUG();
}
@@ -568,6 +608,8 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane)
return 0x0588;
case OMAP_DSS_VIDEO2:
return 0x0564;
+ case OMAP_DSS_VIDEO3:
+ return 0x042C;
default:
BUG();
}
@@ -582,6 +624,8 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0034 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0010 + i * 0x8;
default:
BUG();
}
@@ -597,6 +641,8 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i)
return 0x058C + i * 0x8;
case OMAP_DSS_VIDEO2:
return 0x0568 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0430 + i * 0x8;
default:
BUG();
}
@@ -611,6 +657,8 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0038 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0014 + i * 0x8;
default:
BUG();
}
@@ -626,6 +674,8 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i)
return 0x0590 + i * 8;
case OMAP_DSS_VIDEO2:
return 0x056C + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0434 + i * 0x8;
default:
BUG();
}
@@ -639,6 +689,7 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i)
BUG();
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
return 0x0074 + i * 0x4;
default:
BUG();
@@ -655,6 +706,8 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i)
return 0x0124 + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x00B4 + i * 0x4;
+ case OMAP_DSS_VIDEO3:
+ return 0x0050 + i * 0x4;
default:
BUG();
}
@@ -670,6 +723,8 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i)
return 0x05CC + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x05A8 + i * 0x4;
+ case OMAP_DSS_VIDEO3:
+ return 0x0470 + i * 0x4;
default:
BUG();
}
@@ -684,6 +739,8 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane)
return 0x0174;
case OMAP_DSS_VIDEO2:
return 0x00E8;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A0;
default:
BUG();
}
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 94495e45ec5a..be331dc5a61b 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -45,14 +45,13 @@ static ssize_t display_enabled_store(struct device *dev,
const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
- int r, enabled;
+ int r;
+ bool enabled;
- r = kstrtoint(buf, 0, &enabled);
+ r = strtobool(buf, &enabled);
if (r)
return r;
- enabled = !!enabled;
-
if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
if (enabled) {
r = dssdev->driver->enable(dssdev);
@@ -79,17 +78,16 @@ static ssize_t display_tear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
- int te, r;
+ int r;
+ bool te;
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
return -ENOENT;
- r = kstrtoint(buf, 0, &te);
+ r = strtobool(buf, &te);
if (r)
return r;
- te = !!te;
-
r = dssdev->driver->enable_te(dssdev, te);
if (r)
return r;
@@ -195,17 +193,16 @@ static ssize_t display_mirror_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
- int mirror, r;
+ int r;
+ bool mirror;
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -ENOENT;
- r = kstrtoint(buf, 0, &mirror);
+ r = strtobool(buf, &mirror);
if (r)
return r;
- mirror = !!mirror;
-
r = dssdev->driver->set_mirror(dssdev, mirror);
if (r)
return r;
@@ -302,11 +299,15 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
return 16;
case OMAP_DISPLAY_TYPE_DBI:
- case OMAP_DISPLAY_TYPE_DSI:
if (dssdev->ctrl.pixel_size == 24)
return 24;
else
return 16;
+ case OMAP_DISPLAY_TYPE_DSI:
+ if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
+ return 24;
+ else
+ return 16;
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_SDI:
case OMAP_DISPLAY_TYPE_HDMI:
@@ -342,9 +343,11 @@ bool dss_use_replication(struct omap_dss_device *dssdev,
bpp = 24;
break;
case OMAP_DISPLAY_TYPE_DBI:
- case OMAP_DISPLAY_TYPE_DSI:
bpp = dssdev->ctrl.pixel_size;
break;
+ case OMAP_DISPLAY_TYPE_DSI:
+ bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ break;
default:
BUG();
}
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index f053b180ecd7..483888a85cfd 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -82,9 +82,11 @@ 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_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r)
+ 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;
+ }
*fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
*lck_div = dispc_cinfo.lck_div;
@@ -109,7 +111,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
if (r)
return r;
- r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);
+ r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
if (r)
return r;
@@ -129,7 +131,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
bool is_tft;
int r = 0;
- dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
+ 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;
@@ -153,7 +155,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
t->pixel_clock = pck;
}
- dispc_set_lcd_timings(dssdev->manager->id, t);
+ dispc_mgr_set_lcd_timings(dssdev->manager->id, t);
return 0;
}
@@ -164,11 +166,12 @@ static void dpi_basic_init(struct omap_dss_device *dssdev)
is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
- dispc_set_parallel_interface_mode(dssdev->manager->id,
- OMAP_DSS_PARALLELMODE_BYPASS);
- dispc_set_lcd_display_type(dssdev->manager->id, is_tft ?
+ dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
+ dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+
+ dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ?
OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
- dispc_set_tft_data_lines(dssdev->manager->id,
+ dispc_mgr_set_tft_data_lines(dssdev->manager->id,
dssdev->phy.dpi.data_lines);
}
@@ -176,6 +179,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
{
int r;
+ if (dssdev->manager == NULL) {
+ DSSERR("failed to enable display: no manager\n");
+ return -ENODEV;
+ }
+
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
@@ -277,7 +285,7 @@ void dpi_set_timings(struct omap_dss_device *dssdev,
}
dpi_set_mode(dssdev);
- dispc_go(dssdev->manager->id);
+ dispc_mgr_go(dssdev->manager->id);
dispc_runtime_put();
dss_runtime_put();
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 7adbbeb84334..43c04a9889c4 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -39,6 +39,7 @@
#include <linux/pm_runtime.h>
#include <video/omapdss.h>
+#include <video/mipi_display.h>
#include <plat/clock.h>
#include "dss.h"
@@ -131,7 +132,7 @@ struct dsi_reg { u16 idx; };
#define DSI_IRQ_TA_TIMEOUT (1 << 20)
#define DSI_IRQ_ERROR_MASK \
(DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
- DSI_IRQ_TA_TIMEOUT)
+ DSI_IRQ_TA_TIMEOUT | DSI_IRQ_SYNC_LOST)
#define DSI_IRQ_CHANNEL_MASK 0xf
/* Virtual channel interrupts */
@@ -198,18 +199,6 @@ struct dsi_reg { u16 idx; };
DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \
DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5)
-#define DSI_DT_DCS_SHORT_WRITE_0 0x05
-#define DSI_DT_DCS_SHORT_WRITE_1 0x15
-#define DSI_DT_DCS_READ 0x06
-#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37
-#define DSI_DT_NULL_PACKET 0x09
-#define DSI_DT_DCS_LONG_WRITE 0x39
-
-#define DSI_DT_RX_ACK_WITH_ERR 0x02
-#define DSI_DT_RX_DCS_LONG_READ 0x1c
-#define DSI_DT_RX_SHORT_READ_1 0x21
-#define DSI_DT_RX_SHORT_READ_2 0x22
-
typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
#define DSI_MAX_NR_ISRS 2
@@ -228,9 +217,9 @@ enum fifo_size {
DSI_FIFO_SIZE_128 = 4,
};
-enum dsi_vc_mode {
- DSI_VC_MODE_L4 = 0,
- DSI_VC_MODE_VP,
+enum dsi_vc_source {
+ DSI_VC_SOURCE_L4 = 0,
+ DSI_VC_SOURCE_VP,
};
enum dsi_lane {
@@ -274,7 +263,8 @@ struct dsi_data {
struct clk *dss_clk;
struct clk *sys_clk;
- void (*dsi_mux_pads)(bool enable);
+ int (*enable_pads)(int dsi_id, unsigned lane_mask);
+ void (*disable_pads)(int dsi_id, unsigned lane_mask);
struct dsi_clock_info current_cinfo;
@@ -282,7 +272,7 @@ struct dsi_data {
struct regulator *vdds_dsi_reg;
struct {
- enum dsi_vc_mode mode;
+ enum dsi_vc_source source;
struct omap_dss_device *dssdev;
enum fifo_size fifo_size;
int vc_id;
@@ -368,14 +358,9 @@ struct platform_device *dsi_get_dsidev_from_id(int module)
return dsi_pdev_map[module];
}
-static int dsi_get_dsidev_id(struct platform_device *dsidev)
+static inline int dsi_get_dsidev_id(struct platform_device *dsidev)
{
- /* TEMP: Pass 0 as the dsi module index till the time the dsi platform
- * device names aren't changed to the form "omapdss_dsi.0",
- * "omapdss_dsi.1" and so on */
- BUG_ON(dsidev->id != -1);
-
- return 0;
+ return dsidev->id;
}
static inline void dsi_write_reg(struct platform_device *dsidev,
@@ -437,6 +422,21 @@ static inline int wait_for_bit_change(struct platform_device *dsidev,
return value;
}
+u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
+{
+ switch (fmt) {
+ case OMAP_DSS_DSI_FMT_RGB888:
+ case OMAP_DSS_DSI_FMT_RGB666:
+ return 24;
+ case OMAP_DSS_DSI_FMT_RGB666_PACKED:
+ return 18;
+ case OMAP_DSS_DSI_FMT_RGB565:
+ return 16;
+ default:
+ BUG();
+ }
+}
+
#ifdef DEBUG
static void dsi_perf_mark_setup(struct platform_device *dsidev)
{
@@ -453,6 +453,7 @@ static void dsi_perf_mark_start(struct platform_device *dsidev)
static void dsi_perf_show(struct platform_device *dsidev, const char *name)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct omap_dss_device *dssdev = dsi->update_region.device;
ktime_t t, setup_time, trans_time;
u32 total_bytes;
u32 setup_us, trans_us, total_us;
@@ -476,7 +477,7 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name)
total_bytes = dsi->update_region.w *
dsi->update_region.h *
- dsi->update_region.device->ctrl.pixel_size / 8;
+ dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
"%u bytes, %u kbytes/sec\n",
@@ -1287,7 +1288,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev,
* with DSS_SYS_CLK source also */
cinfo->highfreq = 0;
} else {
- cinfo->clkin = dispc_pclk_rate(dssdev->manager->id);
+ cinfo->clkin = dispc_mgr_pclk_rate(dssdev->manager->id);
if (cinfo->clkin < 32000000)
cinfo->highfreq = 0;
@@ -2360,6 +2361,24 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev)
return 0;
}
+static unsigned dsi_get_lane_mask(struct omap_dss_device *dssdev)
+{
+ unsigned lanes = 0;
+
+ if (dssdev->phy.dsi.clk_lane != 0)
+ lanes |= 1 << (dssdev->phy.dsi.clk_lane - 1);
+ if (dssdev->phy.dsi.data1_lane != 0)
+ lanes |= 1 << (dssdev->phy.dsi.data1_lane - 1);
+ if (dssdev->phy.dsi.data2_lane != 0)
+ lanes |= 1 << (dssdev->phy.dsi.data2_lane - 1);
+ if (dssdev->phy.dsi.data3_lane != 0)
+ lanes |= 1 << (dssdev->phy.dsi.data3_lane - 1);
+ if (dssdev->phy.dsi.data4_lane != 0)
+ lanes |= 1 << (dssdev->phy.dsi.data4_lane - 1);
+
+ return lanes;
+}
+
static int dsi_cio_init(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -2370,8 +2389,9 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
DSSDBGF();
- if (dsi->dsi_mux_pads)
- dsi->dsi_mux_pads(true);
+ r = dsi->enable_pads(dsidev->id, dsi_get_lane_mask(dssdev));
+ if (r)
+ return r;
dsi_enable_scp_clk(dsidev);
@@ -2452,6 +2472,12 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
dsi_cio_timings(dsidev);
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
+ /* DDR_CLK_ALWAYS_ON */
+ REG_FLD_MOD(dsidev, DSI_CLK_CTRL,
+ dssdev->panel.dsi_vm_data.ddr_clk_always_on, 13, 13);
+ }
+
dsi->ulps_enabled = false;
DSSDBG("CIO init done\n");
@@ -2467,19 +2493,21 @@ err_cio_pwr:
dsi_cio_disable_lane_override(dsidev);
err_scp_clk_dom:
dsi_disable_scp_clk(dsidev);
- if (dsi->dsi_mux_pads)
- dsi->dsi_mux_pads(false);
+ dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev));
return r;
}
-static void dsi_cio_uninit(struct platform_device *dsidev)
+static void dsi_cio_uninit(struct omap_dss_device *dssdev)
{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ /* DDR_CLK_ALWAYS_ON */
+ REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
+
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
dsi_disable_scp_clk(dsidev);
- if (dsi->dsi_mux_pads)
- dsi->dsi_mux_pads(false);
+ dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev));
}
static void dsi_config_tx_fifo(struct platform_device *dsidev,
@@ -2669,10 +2697,10 @@ static int dsi_sync_vc(struct platform_device *dsidev, int channel)
if (!dsi_vc_is_enabled(dsidev, channel))
return 0;
- switch (dsi->vc[channel].mode) {
- case DSI_VC_MODE_VP:
+ switch (dsi->vc[channel].source) {
+ case DSI_VC_SOURCE_VP:
return dsi_sync_vc_vp(dsidev, channel);
- case DSI_VC_MODE_L4:
+ case DSI_VC_SOURCE_L4:
return dsi_sync_vc_l4(dsidev, channel);
default:
BUG();
@@ -2726,43 +2754,12 @@ static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r);
}
-static int dsi_vc_config_l4(struct platform_device *dsidev, int channel)
+static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
+ enum dsi_vc_source source)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
- if (dsi->vc[channel].mode == DSI_VC_MODE_L4)
- return 0;
-
- DSSDBGF("%d", channel);
-
- dsi_sync_vc(dsidev, channel);
-
- dsi_vc_enable(dsidev, channel, 0);
-
- /* VC_BUSY */
- if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) {
- DSSERR("vc(%d) busy when trying to config for L4\n", channel);
- return -EIO;
- }
-
- REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */
-
- /* DCS_CMD_ENABLE */
- if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC))
- REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 30, 30);
-
- dsi_vc_enable(dsidev, channel, 1);
-
- dsi->vc[channel].mode = DSI_VC_MODE_L4;
-
- return 0;
-}
-
-static int dsi_vc_config_vp(struct platform_device *dsidev, int channel)
-{
- struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-
- if (dsi->vc[channel].mode == DSI_VC_MODE_VP)
+ if (dsi->vc[channel].source == source)
return 0;
DSSDBGF("%d", channel);
@@ -2777,21 +2774,22 @@ static int dsi_vc_config_vp(struct platform_device *dsidev, int channel)
return -EIO;
}
- /* SOURCE, 1 = video port */
- REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 1, 1);
+ /* SOURCE, 0 = L4, 1 = video port */
+ REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1);
/* DCS_CMD_ENABLE */
- if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC))
- REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 30, 30);
+ if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
+ bool enable = source == DSI_VC_SOURCE_VP;
+ REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30);
+ }
dsi_vc_enable(dsidev, channel, 1);
- dsi->vc[channel].mode = DSI_VC_MODE_VP;
+ dsi->vc[channel].source = source;
return 0;
}
-
void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
bool enable)
{
@@ -2810,6 +2808,10 @@ void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
dsi_if_enable(dsidev, 1);
dsi_force_tx_stop_mode_io(dsidev);
+
+ /* start the DDR clock by sending a NULL packet */
+ if (dssdev->panel.dsi_vm_data.ddr_clk_always_on && enable)
+ dsi_vc_send_null(dssdev, channel);
}
EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs);
@@ -2873,16 +2875,16 @@ static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
DSSERR("\trawval %#08x\n", val);
dt = FLD_GET(val, 5, 0);
- if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
u16 err = FLD_GET(val, 23, 8);
dsi_show_rx_ack_with_err(err);
- } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
DSSERR("\tDCS short response, 1 byte: %#x\n",
FLD_GET(val, 23, 8));
- } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
DSSERR("\tDCS short response, 2 byte: %#x\n",
FLD_GET(val, 23, 8));
- } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
DSSERR("\tDCS long response, len %d\n",
FLD_GET(val, 23, 8));
dsi_vc_flush_long_data(dsidev, channel);
@@ -3007,7 +3009,7 @@ static int dsi_vc_send_long(struct platform_device *dsidev, int channel,
return -EINVAL;
}
- dsi_vc_config_l4(dsidev, channel);
+ dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc);
@@ -3066,7 +3068,7 @@ static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
channel,
data_type, data & 0xff, (data >> 8) & 0xff);
- dsi_vc_config_l4(dsidev, channel);
+ dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) {
DSSERR("ERROR FIFO FULL, aborting transfer\n");
@@ -3085,44 +3087,66 @@ static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- u8 nullpkg[] = {0, 0, 0, 0};
- return dsi_vc_send_long(dsidev, channel, DSI_DT_NULL_PACKET, nullpkg,
- 4, 0);
+ return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL,
+ 0, 0);
}
EXPORT_SYMBOL(dsi_vc_send_null);
-int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
- u8 *data, int len)
+static int dsi_vc_write_nosync_common(struct omap_dss_device *dssdev,
+ int channel, u8 *data, int len, enum dss_dsi_content_type type)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
int r;
- BUG_ON(len == 0);
-
- if (len == 1) {
- r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_0,
- data[0], 0);
+ if (len == 0) {
+ BUG_ON(type == DSS_DSI_CONTENT_DCS);
+ r = dsi_vc_send_short(dsidev, channel,
+ MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0);
+ } else if (len == 1) {
+ r = dsi_vc_send_short(dsidev, channel,
+ type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
+ MIPI_DSI_DCS_SHORT_WRITE, data[0], 0);
} else if (len == 2) {
- r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_1,
+ r = dsi_vc_send_short(dsidev, channel,
+ type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM,
data[0] | (data[1] << 8), 0);
} else {
- /* 0x39 = DCS Long Write */
- r = dsi_vc_send_long(dsidev, channel, DSI_DT_DCS_LONG_WRITE,
- data, len, 0);
+ r = dsi_vc_send_long(dsidev, channel,
+ type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_GENERIC_LONG_WRITE :
+ MIPI_DSI_DCS_LONG_WRITE, data, len, 0);
}
return r;
}
+
+int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
+ u8 *data, int len)
+{
+ return dsi_vc_write_nosync_common(dssdev, channel, data, len,
+ DSS_DSI_CONTENT_DCS);
+}
EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
-int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
- int len)
+int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel,
+ u8 *data, int len)
+{
+ return dsi_vc_write_nosync_common(dssdev, channel, data, len,
+ DSS_DSI_CONTENT_GENERIC);
+}
+EXPORT_SYMBOL(dsi_vc_generic_write_nosync);
+
+static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel,
+ u8 *data, int len, enum dss_dsi_content_type type)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
int r;
- r = dsi_vc_dcs_write_nosync(dssdev, channel, data, len);
+ r = dsi_vc_write_nosync_common(dssdev, channel, data, len, type);
if (r)
goto err;
@@ -3140,18 +3164,39 @@ int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
return 0;
err:
- DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n",
+ DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n",
channel, data[0], len);
return r;
}
+
+int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
+ int len)
+{
+ return dsi_vc_write_common(dssdev, channel, data, len,
+ DSS_DSI_CONTENT_DCS);
+}
EXPORT_SYMBOL(dsi_vc_dcs_write);
+int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data,
+ int len)
+{
+ return dsi_vc_write_common(dssdev, channel, data, len,
+ DSS_DSI_CONTENT_GENERIC);
+}
+EXPORT_SYMBOL(dsi_vc_generic_write);
+
int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd)
{
return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1);
}
EXPORT_SYMBOL(dsi_vc_dcs_write_0);
+int dsi_vc_generic_write_0(struct omap_dss_device *dssdev, int channel)
+{
+ return dsi_vc_generic_write(dssdev, channel, NULL, 0);
+}
+EXPORT_SYMBOL(dsi_vc_generic_write_0);
+
int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
u8 param)
{
@@ -3162,25 +3207,87 @@ int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
}
EXPORT_SYMBOL(dsi_vc_dcs_write_1);
-int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
- u8 *buf, int buflen)
+int dsi_vc_generic_write_1(struct omap_dss_device *dssdev, int channel,
+ u8 param)
+{
+ return dsi_vc_generic_write(dssdev, channel, &param, 1);
+}
+EXPORT_SYMBOL(dsi_vc_generic_write_1);
+
+int dsi_vc_generic_write_2(struct omap_dss_device *dssdev, int channel,
+ u8 param1, u8 param2)
+{
+ u8 buf[2];
+ buf[0] = param1;
+ buf[1] = param2;
+ return dsi_vc_generic_write(dssdev, channel, buf, 2);
+}
+EXPORT_SYMBOL(dsi_vc_generic_write_2);
+
+static int dsi_vc_dcs_send_read_request(struct omap_dss_device *dssdev,
+ int channel, u8 dcs_cmd)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
- u32 val;
- u8 dt;
int r;
if (dsi->debug_read)
- DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd);
+ DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n",
+ channel, dcs_cmd);
- r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_READ, dcs_cmd, 0);
- if (r)
- goto err;
+ r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0);
+ if (r) {
+ DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)"
+ " failed\n", channel, dcs_cmd);
+ return r;
+ }
- r = dsi_vc_send_bta_sync(dssdev, channel);
- if (r)
- goto err;
+ return 0;
+}
+
+static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev,
+ int channel, u8 *reqdata, int reqlen)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ u16 data;
+ u8 data_type;
+ int r;
+
+ if (dsi->debug_read)
+ DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n",
+ channel, reqlen);
+
+ if (reqlen == 0) {
+ data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
+ data = 0;
+ } else if (reqlen == 1) {
+ data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
+ data = reqdata[0];
+ } else if (reqlen == 2) {
+ data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
+ data = reqdata[0] | (reqdata[1] << 8);
+ } else {
+ BUG();
+ }
+
+ r = dsi_vc_send_short(dsidev, channel, data_type, data, 0);
+ if (r) {
+ DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)"
+ " failed\n", channel, reqlen);
+ return r;
+ }
+
+ return 0;
+}
+
+static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel,
+ u8 *buf, int buflen, enum dss_dsi_content_type type)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ u32 val;
+ u8 dt;
+ int r;
/* RX_FIFO_NOT_EMPTY */
if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) {
@@ -3193,16 +3300,20 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
if (dsi->debug_read)
DSSDBG("\theader: %08x\n", val);
dt = FLD_GET(val, 5, 0);
- if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
u16 err = FLD_GET(val, 23, 8);
dsi_show_rx_ack_with_err(err);
r = -EIO;
goto err;
- } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
u8 data = FLD_GET(val, 15, 8);
if (dsi->debug_read)
- DSSDBG("\tDCS short response, 1 byte: %02x\n", data);
+ DSSDBG("\t%s short response, 1 byte: %02x\n",
+ type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
+ "DCS", data);
if (buflen < 1) {
r = -EIO;
@@ -3212,10 +3323,14 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
buf[0] = data;
return 1;
- } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
u16 data = FLD_GET(val, 23, 8);
if (dsi->debug_read)
- DSSDBG("\tDCS short response, 2 byte: %04x\n", data);
+ DSSDBG("\t%s short response, 2 byte: %04x\n",
+ type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
+ "DCS", data);
if (buflen < 2) {
r = -EIO;
@@ -3226,11 +3341,15 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
buf[1] = (data >> 8) & 0xff;
return 2;
- } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
+ MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
+ MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
int w;
int len = FLD_GET(val, 23, 8);
if (dsi->debug_read)
- DSSDBG("\tDCS long response, len %d\n", len);
+ DSSDBG("\t%s long response, len %d\n",
+ type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
+ "DCS", len);
if (len > buflen) {
r = -EIO;
@@ -3266,58 +3385,126 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
BUG();
err:
- DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n",
- channel, dcs_cmd);
+ DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel,
+ type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
+
return r;
+}
+int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
+ u8 *buf, int buflen)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ int r;
+
+ r = dsi_vc_dcs_send_read_request(dssdev, channel, dcs_cmd);
+ if (r)
+ goto err;
+
+ r = dsi_vc_send_bta_sync(dssdev, channel);
+ if (r)
+ goto err;
+
+ r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
+ DSS_DSI_CONTENT_DCS);
+ if (r < 0)
+ goto err;
+
+ if (r != buflen) {
+ r = -EIO;
+ goto err;
+ }
+
+ return 0;
+err:
+ DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd);
+ return r;
}
EXPORT_SYMBOL(dsi_vc_dcs_read);
-int dsi_vc_dcs_read_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
- u8 *data)
+static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel,
+ u8 *reqdata, int reqlen, u8 *buf, int buflen)
{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
int r;
- r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, data, 1);
+ r = dsi_vc_generic_send_read_request(dssdev, channel, reqdata, reqlen);
+ if (r)
+ return r;
+
+ r = dsi_vc_send_bta_sync(dssdev, channel);
+ if (r)
+ return r;
+ r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
+ DSS_DSI_CONTENT_GENERIC);
if (r < 0)
return r;
- if (r != 1)
- return -EIO;
+ if (r != buflen) {
+ r = -EIO;
+ return r;
+ }
return 0;
}
-EXPORT_SYMBOL(dsi_vc_dcs_read_1);
-int dsi_vc_dcs_read_2(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
- u8 *data1, u8 *data2)
+int dsi_vc_generic_read_0(struct omap_dss_device *dssdev, int channel, u8 *buf,
+ int buflen)
{
- u8 buf[2];
int r;
- r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, buf, 2);
+ r = dsi_vc_generic_read(dssdev, channel, NULL, 0, buf, buflen);
+ if (r) {
+ DSSERR("dsi_vc_generic_read_0(ch %d) failed\n", channel);
+ return r;
+ }
- if (r < 0)
+ return 0;
+}
+EXPORT_SYMBOL(dsi_vc_generic_read_0);
+
+int dsi_vc_generic_read_1(struct omap_dss_device *dssdev, int channel, u8 param,
+ u8 *buf, int buflen)
+{
+ int r;
+
+ r = dsi_vc_generic_read(dssdev, channel, &param, 1, buf, buflen);
+ if (r) {
+ DSSERR("dsi_vc_generic_read_1(ch %d) failed\n", channel);
return r;
+ }
- if (r != 2)
- return -EIO;
+ return 0;
+}
+EXPORT_SYMBOL(dsi_vc_generic_read_1);
- *data1 = buf[0];
- *data2 = buf[1];
+int dsi_vc_generic_read_2(struct omap_dss_device *dssdev, int channel,
+ u8 param1, u8 param2, u8 *buf, int buflen)
+{
+ int r;
+ u8 reqdata[2];
+
+ reqdata[0] = param1;
+ reqdata[1] = param2;
+
+ r = dsi_vc_generic_read(dssdev, channel, reqdata, 2, buf, buflen);
+ if (r) {
+ DSSERR("dsi_vc_generic_read_2(ch %d) failed\n", channel);
+ return r;
+ }
return 0;
}
-EXPORT_SYMBOL(dsi_vc_dcs_read_2);
+EXPORT_SYMBOL(dsi_vc_generic_read_2);
int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
u16 len)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- return dsi_vc_send_short(dsidev, channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
- len, 0);
+ return dsi_vc_send_short(dsidev, channel,
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
}
EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
@@ -3508,6 +3695,75 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
}
+
+static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ int num_line_buffers;
+
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
+ int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ unsigned line_buf_size = dsi_get_line_buf_size(dsidev);
+ struct omap_video_timings *timings = &dssdev->panel.timings;
+ /*
+ * Don't use line buffers if width is greater than the video
+ * port's line buffer size
+ */
+ if (line_buf_size <= timings->x_res * bpp / 8)
+ num_line_buffers = 0;
+ else
+ num_line_buffers = 2;
+ } else {
+ /* Use maximum number of line buffers in command mode */
+ num_line_buffers = 2;
+ }
+
+ /* LINE_BUFFER */
+ REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12);
+}
+
+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, 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 */
+ r = FLD_MOD(r, hsync_end, 18, 18); /* VP_HSYNC_END */
+ dsi_write_reg(dsidev, DSI_CTRL, r);
+}
+
+static void dsi_config_blanking_modes(struct omap_dss_device *dssdev)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ int blanking_mode = dssdev->panel.dsi_vm_data.blanking_mode;
+ int hfp_blanking_mode = dssdev->panel.dsi_vm_data.hfp_blanking_mode;
+ int hbp_blanking_mode = dssdev->panel.dsi_vm_data.hbp_blanking_mode;
+ int hsa_blanking_mode = dssdev->panel.dsi_vm_data.hsa_blanking_mode;
+ u32 r;
+
+ /*
+ * 0 = TX FIFO packets sent or LPS in corresponding blanking periods
+ * 1 = Long blanking packets are sent in corresponding blanking periods
+ */
+ r = dsi_read_reg(dsidev, DSI_CTRL);
+ r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */
+ r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */
+ r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */
+ r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */
+ dsi_write_reg(dsidev, DSI_CTRL, r);
+}
+
static int dsi_proto_config(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -3530,7 +3786,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
- switch (dssdev->ctrl.pixel_size) {
+ switch (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)) {
case 16:
buswidth = 0;
break;
@@ -3551,7 +3807,6 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
- r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */
r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
@@ -3562,6 +3817,13 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
dsi_write_reg(dsidev, DSI_CTRL, r);
+ dsi_config_vp_num_line_buffers(dssdev);
+
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
+ dsi_config_vp_sync_events(dssdev);
+ dsi_config_blanking_modes(dssdev);
+ }
+
dsi_vc_initial_config(dsidev, 0);
dsi_vc_initial_config(dsidev, 1);
dsi_vc_initial_config(dsidev, 2);
@@ -3580,6 +3842,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
unsigned ddr_clk_pre, ddr_clk_post;
unsigned enter_hs_mode_lat, exit_hs_mode_lat;
unsigned ths_eot;
+ int ndl = dsi_get_num_data_lanes_dssdev(dssdev);
u32 r;
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
@@ -3602,7 +3865,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
/* min 60ns + 52*UI */
tclk_post = ns2ddr(dsidev, 60) + 26;
- ths_eot = DIV_ROUND_UP(4, dsi_get_num_data_lanes_dssdev(dssdev));
+ ths_eot = DIV_ROUND_UP(4, ndl);
ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
4);
@@ -3632,162 +3895,114 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
enter_hs_mode_lat, exit_hs_mode_lat);
-}
-
-
-#define DSI_DECL_VARS \
- int __dsi_cb = 0; u32 __dsi_cv = 0;
-#define DSI_FLUSH(dsidev, ch) \
- if (__dsi_cb > 0) { \
- /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \
- dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \
- __dsi_cb = __dsi_cv = 0; \
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
+ /* TODO: Implement a video mode check_timings function */
+ int hsa = dssdev->panel.dsi_vm_data.hsa;
+ int hfp = dssdev->panel.dsi_vm_data.hfp;
+ int hbp = dssdev->panel.dsi_vm_data.hbp;
+ int vsa = dssdev->panel.dsi_vm_data.vsa;
+ int vfp = dssdev->panel.dsi_vm_data.vfp;
+ int vbp = dssdev->panel.dsi_vm_data.vbp;
+ int window_sync = dssdev->panel.dsi_vm_data.window_sync;
+ bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
+ struct omap_video_timings *timings = &dssdev->panel.timings;
+ int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ int tl, t_he, width_bytes;
+
+ t_he = hsync_end ?
+ ((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
+
+ width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
+
+ /* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
+ tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
+ DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
+
+ DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
+ hfp, hsync_end ? hsa : 0, tl);
+ DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
+ vsa, timings->y_res);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
+ r = FLD_MOD(r, hbp, 11, 0); /* HBP */
+ r = FLD_MOD(r, hfp, 23, 12); /* HFP */
+ r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */
+ dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
+ r = FLD_MOD(r, vbp, 7, 0); /* VBP */
+ r = FLD_MOD(r, vfp, 15, 8); /* VFP */
+ r = FLD_MOD(r, vsa, 23, 16); /* VSA */
+ r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */
+ dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
+ r = FLD_MOD(r, timings->y_res, 14, 0); /* VACT */
+ r = FLD_MOD(r, tl, 31, 16); /* TL */
+ dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
}
+}
-#define DSI_PUSH(dsidev, ch, data) \
- do { \
- __dsi_cv |= (data) << (__dsi_cb * 8); \
- /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \
- if (++__dsi_cb > 3) \
- DSI_FLUSH(dsidev, ch); \
- } while (0)
-
-static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
- int x, int y, int w, int h)
+int dsi_video_mode_enable(struct omap_dss_device *dssdev, int channel)
{
- /* Note: supports only 24bit colors in 32bit container */
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
- int first = 1;
- int fifo_stalls = 0;
- int max_dsi_packet_size;
- int max_data_per_packet;
- int max_pixels_per_packet;
- int pixels_left;
- int bytespp = dssdev->ctrl.pixel_size / 8;
- int scr_width;
- u32 __iomem *data;
- int start_offset;
- int horiz_inc;
- int current_x;
- struct omap_overlay *ovl;
-
- debug_irq = 0;
-
- DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n",
- x, y, w, h);
+ int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ u8 data_type;
+ u16 word_count;
- ovl = dssdev->manager->overlays[0];
-
- if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U)
- return -EINVAL;
-
- if (dssdev->ctrl.pixel_size != 24)
- return -EINVAL;
-
- scr_width = ovl->info.screen_width;
- data = ovl->info.vaddr;
-
- start_offset = scr_width * y + x;
- horiz_inc = scr_width - w;
- current_x = x;
-
- /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes
- * in fifo */
-
- /* When using CPU, max long packet size is TX buffer size */
- max_dsi_packet_size = dsi->vc[0].fifo_size * 32 * 4;
-
- /* we seem to get better perf if we divide the tx fifo to half,
- and while the other half is being sent, we fill the other half
- max_dsi_packet_size /= 2; */
-
- max_data_per_packet = max_dsi_packet_size - 4 - 1;
-
- max_pixels_per_packet = max_data_per_packet / bytespp;
-
- DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet);
-
- pixels_left = w * h;
+ switch (dssdev->panel.dsi_pix_fmt) {
+ case OMAP_DSS_DSI_FMT_RGB888:
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ break;
+ case OMAP_DSS_DSI_FMT_RGB666:
+ data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ break;
+ case OMAP_DSS_DSI_FMT_RGB666_PACKED:
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ break;
+ case OMAP_DSS_DSI_FMT_RGB565:
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ break;
+ default:
+ BUG();
+ };
- DSSDBG("total pixels %d\n", pixels_left);
+ dsi_if_enable(dsidev, false);
+ dsi_vc_enable(dsidev, channel, false);
- data += start_offset;
+ /* MODE, 1 = video mode */
+ REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
- while (pixels_left > 0) {
- /* 0x2c = write_memory_start */
- /* 0x3c = write_memory_continue */
- u8 dcs_cmd = first ? 0x2c : 0x3c;
- int pixels;
- DSI_DECL_VARS;
- first = 0;
+ word_count = DIV_ROUND_UP(dssdev->panel.timings.x_res * bpp, 8);
-#if 1
- /* using fifo not empty */
- /* TX_FIFO_NOT_EMPTY */
- while (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(0)), 5, 5)) {
- fifo_stalls++;
- if (fifo_stalls > 0xfffff) {
- DSSERR("fifo stalls overflow, pixels left %d\n",
- pixels_left);
- dsi_if_enable(dsidev, 0);
- return -EIO;
- }
- udelay(1);
- }
-#elif 1
- /* using fifo emptiness */
- while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 <
- max_dsi_packet_size) {
- fifo_stalls++;
- if (fifo_stalls > 0xfffff) {
- DSSERR("fifo stalls overflow, pixels left %d\n",
- pixels_left);
- dsi_if_enable(dsidev, 0);
- return -EIO;
- }
- }
-#else
- while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS,
- 7, 0) + 1) * 4 == 0) {
- fifo_stalls++;
- if (fifo_stalls > 0xfffff) {
- DSSERR("fifo stalls overflow, pixels left %d\n",
- pixels_left);
- dsi_if_enable(dsidev, 0);
- return -EIO;
- }
- }
-#endif
- pixels = min(max_pixels_per_packet, pixels_left);
+ dsi_vc_write_long_header(dsidev, channel, data_type, word_count, 0);
- pixels_left -= pixels;
+ dsi_vc_enable(dsidev, channel, true);
+ dsi_if_enable(dsidev, true);
- dsi_vc_write_long_header(dsidev, 0, DSI_DT_DCS_LONG_WRITE,
- 1 + pixels * bytespp, 0);
+ dssdev->manager->enable(dssdev->manager);
- DSI_PUSH(dsidev, 0, dcs_cmd);
+ return 0;
+}
+EXPORT_SYMBOL(dsi_video_mode_enable);
- while (pixels-- > 0) {
- u32 pix = __raw_readl(data++);
+void dsi_video_mode_disable(struct omap_dss_device *dssdev, int channel)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- DSI_PUSH(dsidev, 0, (pix >> 16) & 0xff);
- DSI_PUSH(dsidev, 0, (pix >> 8) & 0xff);
- DSI_PUSH(dsidev, 0, (pix >> 0) & 0xff);
+ dsi_if_enable(dsidev, false);
+ dsi_vc_enable(dsidev, channel, false);
- current_x++;
- if (current_x == x+w) {
- current_x = x;
- data += horiz_inc;
- }
- }
+ /* MODE, 0 = command mode */
+ REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
- DSI_FLUSH(dsidev, 0);
- }
+ dsi_vc_enable(dsidev, channel, true);
+ dsi_if_enable(dsidev, true);
- return 0;
+ dssdev->manager->disable(dssdev->manager);
}
+EXPORT_SYMBOL(dsi_video_mode_disable);
static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
@@ -3808,9 +4023,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
x, y, w, h);
- dsi_vc_config_vp(dsidev, channel);
+ dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
- bytespp = dssdev->ctrl.pixel_size / 8;
+ bytespp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
bytespl = w * bytespp;
bytespf = bytespl * h;
@@ -3831,7 +4046,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
- dsi_vc_write_long_header(dsidev, channel, DSI_DT_DCS_LONG_WRITE,
+ dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE,
packet_len, 0);
if (dsi->te_enabled)
@@ -3956,11 +4171,9 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
dsi_perf_mark_setup(dsidev);
- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
- dss_setup_partial_planes(dssdev, x, y, w, h,
- enlarge_update_area);
- dispc_set_lcd_size(dssdev->manager->id, *w, *h);
- }
+ dss_setup_partial_planes(dssdev, x, y, w, h,
+ enlarge_update_area);
+ dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h);
return 0;
}
@@ -3982,27 +4195,16 @@ int omap_dsi_update(struct omap_dss_device *dssdev,
* see rather obscure HW error happening, as DSS halts. */
BUG_ON(x % 2 == 1);
- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
- dsi->framedone_callback = callback;
- dsi->framedone_data = data;
-
- dsi->update_region.x = x;
- dsi->update_region.y = y;
- dsi->update_region.w = w;
- dsi->update_region.h = h;
- dsi->update_region.device = dssdev;
-
- dsi_update_screen_dispc(dssdev, x, y, w, h);
- } else {
- int r;
+ dsi->framedone_callback = callback;
+ dsi->framedone_data = data;
- r = dsi_update_screen_l4(dssdev, x, y, w, h);
- if (r)
- return r;
+ dsi->update_region.x = x;
+ dsi->update_region.y = y;
+ dsi->update_region.w = w;
+ dsi->update_region.h = h;
+ dsi->update_region.device = dssdev;
- dsi_perf_show(dsidev, "L4");
- callback(0, data);
- }
+ dsi_update_screen_dispc(dssdev, x, y, w, h);
return 0;
}
@@ -4013,28 +4215,9 @@ EXPORT_SYMBOL(omap_dsi_update);
static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
{
int r;
- u32 irq;
-
- irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
- DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
- r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev,
- irq);
- if (r) {
- DSSERR("can't get FRAMEDONE irq\n");
- return r;
- }
-
- dispc_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
-
- dispc_set_parallel_interface_mode(dssdev->manager->id,
- OMAP_DSS_PARALLELMODE_DSI);
- dispc_enable_fifohandcheck(dssdev->manager->id, 1);
-
- dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
-
- {
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
+ u32 irq;
struct omap_video_timings timings = {
.hsw = 1,
.hfp = 1,
@@ -4044,21 +4227,46 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
.vbp = 0,
};
- dispc_set_lcd_timings(dssdev->manager->id, &timings);
+ irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
+ DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+
+ r = omap_dispc_register_isr(dsi_framedone_irq_callback,
+ (void *) dssdev, irq);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+ return r;
+ }
+
+ dispc_mgr_enable_stallmode(dssdev->manager->id, true);
+ dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
+
+ dispc_mgr_set_lcd_timings(dssdev->manager->id, &timings);
+ } else {
+ dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+ dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
+
+ dispc_mgr_set_lcd_timings(dssdev->manager->id,
+ &dssdev->panel.timings);
}
+ 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));
return 0;
}
static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
{
- u32 irq;
+ 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 = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
+ DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
- omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev,
- irq);
+ omap_dispc_unregister_isr(dsi_framedone_irq_callback,
+ (void *) dssdev, irq);
+ }
}
static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
@@ -4106,7 +4314,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
return r;
}
- r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);
+ r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
if (r) {
DSSERR("Failed to set dispc clocks\n");
return r;
@@ -4166,10 +4374,12 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
return 0;
err3:
- dsi_cio_uninit(dsidev);
+ dsi_cio_uninit(dssdev);
err2:
dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK);
+ dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK);
+
err1:
dsi_pll_uninit(dsidev, true);
err0:
@@ -4195,7 +4405,8 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev,
dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK);
- dsi_cio_uninit(dsidev);
+ dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK);
+ dsi_cio_uninit(dssdev);
dsi_pll_uninit(dsidev, disconnect_lanes);
}
@@ -4211,6 +4422,12 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
mutex_lock(&dsi->lock);
+ if (dssdev->manager == NULL) {
+ DSSERR("failed to enable display: no manager\n");
+ r = -ENODEV;
+ goto err_start_dev;
+ }
+
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
@@ -4307,9 +4524,10 @@ int dsi_init_display(struct omap_dss_device *dssdev)
DSSDBG("DSI init\n");
- /* XXX these should be figured out dynamically */
- dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
- OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
+ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+ OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+ }
if (dsi->vdds_dsi_reg == NULL) {
struct regulator *vdds_dsi;
@@ -4435,10 +4653,7 @@ static int dsi_get_clocks(struct platform_device *dsidev)
dsi->dss_clk = clk;
- if (cpu_is_omap34xx() || cpu_is_omap3630())
- clk = clk_get(&dsidev->dev, "dss2_alwon_fck");
- else
- clk = clk_get(&dsidev->dev, "sys_clk");
+ clk = clk_get(&dsidev->dev, "sys_clk");
if (IS_ERR(clk)) {
DSSERR("can't get sys_clk\n");
clk_put(dsi->dss_clk);
@@ -4462,7 +4677,7 @@ static void dsi_put_clocks(struct platform_device *dsidev)
}
/* DSI1 HW IP initialisation */
-static int omap_dsi1hw_probe(struct platform_device *dsidev)
+static int omap_dsihw_probe(struct platform_device *dsidev)
{
struct omap_display_platform_data *dss_plat_data;
struct omap_dss_board_info *board_info;
@@ -4483,7 +4698,8 @@ static int omap_dsi1hw_probe(struct platform_device *dsidev)
dss_plat_data = dsidev->dev.platform_data;
board_info = dss_plat_data->board_data;
- dsi->dsi_mux_pads = board_info->dsi_mux_pads;
+ dsi->enable_pads = board_info->dsi_enable_pads;
+ dsi->disable_pads = board_info->dsi_disable_pads;
spin_lock_init(&dsi->irq_lock);
spin_lock_init(&dsi->errors_lock);
@@ -4539,7 +4755,7 @@ static int omap_dsi1hw_probe(struct platform_device *dsidev)
/* DSI VCs initialization */
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
- dsi->vc[i].mode = DSI_VC_MODE_L4;
+ dsi->vc[i].source = DSI_VC_SOURCE_L4;
dsi->vc[i].dssdev = NULL;
dsi->vc[i].vc_id = 0;
}
@@ -4572,7 +4788,7 @@ err_alloc:
return r;
}
-static int omap_dsi1hw_remove(struct platform_device *dsidev)
+static int omap_dsihw_remove(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
@@ -4602,10 +4818,6 @@ static int omap_dsi1hw_remove(struct platform_device *dsidev)
static int dsi_runtime_suspend(struct device *dev)
{
- struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev));
-
- clk_disable(dsi->dss_clk);
-
dispc_runtime_put();
dss_runtime_put();
@@ -4614,7 +4826,6 @@ static int dsi_runtime_suspend(struct device *dev)
static int dsi_runtime_resume(struct device *dev)
{
- struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev));
int r;
r = dss_runtime_get();
@@ -4625,8 +4836,6 @@ static int dsi_runtime_resume(struct device *dev)
if (r)
goto err_get_dispc;
- clk_enable(dsi->dss_clk);
-
return 0;
err_get_dispc:
@@ -4640,11 +4849,11 @@ static const struct dev_pm_ops dsi_pm_ops = {
.runtime_resume = dsi_runtime_resume,
};
-static struct platform_driver omap_dsi1hw_driver = {
- .probe = omap_dsi1hw_probe,
- .remove = omap_dsi1hw_remove,
+static struct platform_driver omap_dsihw_driver = {
+ .probe = omap_dsihw_probe,
+ .remove = omap_dsihw_remove,
.driver = {
- .name = "omapdss_dsi1",
+ .name = "omapdss_dsi",
.owner = THIS_MODULE,
.pm = &dsi_pm_ops,
},
@@ -4652,10 +4861,10 @@ static struct platform_driver omap_dsi1hw_driver = {
int dsi_init_platform_driver(void)
{
- return platform_driver_register(&omap_dsi1hw_driver);
+ return platform_driver_register(&omap_dsihw_driver);
}
void dsi_uninit_platform_driver(void)
{
- return platform_driver_unregister(&omap_dsi1hw_driver);
+ return platform_driver_unregister(&omap_dsihw_driver);
}
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 0f9c3a6457a5..3e09726d32c7 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -639,6 +639,17 @@ void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi)
REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */
}
+enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void)
+{
+ enum omap_display_type displays;
+
+ displays = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT);
+ if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0)
+ return DSS_VENC_TV_CLK;
+
+ return REG_GET(DSS_CONTROL, 15, 15);
+}
+
static int dss_get_clocks(void)
{
struct clk *clk;
@@ -691,11 +702,6 @@ static void dss_put_clocks(void)
clk_put(dss.dss_clk);
}
-struct clk *dss_get_ick(void)
-{
- return clk_get(&dss.pdev->dev, "ick");
-}
-
int dss_runtime_get(void)
{
int r;
@@ -824,13 +830,11 @@ static int omap_dsshw_remove(struct platform_device *pdev)
static int dss_runtime_suspend(struct device *dev)
{
dss_save_context();
- clk_disable(dss.dss_clk);
return 0;
}
static int dss_runtime_resume(struct device *dev)
{
- clk_enable(dss.dss_clk);
dss_restore_context();
return 0;
}
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 9c94b1152c20..6308fc59fc9e 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -97,10 +97,10 @@ extern unsigned int dss_debug;
#define FLD_MOD(orig, val, start, end) \
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
-enum omap_parallel_interface_mode {
- OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */
- OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */
- OMAP_DSS_PARALLELMODE_DSI,
+enum dss_io_pad_mode {
+ DSS_IO_PAD_MODE_RESET,
+ DSS_IO_PAD_MODE_RFBI,
+ DSS_IO_PAD_MODE_BYPASS,
};
enum dss_hdmi_venc_clk_source_select {
@@ -108,6 +108,11 @@ enum dss_hdmi_venc_clk_source_select {
DSS_HDMI_M_PCLK = 1,
};
+enum dss_dsi_content_type {
+ DSS_DSI_CONTENT_DCS,
+ DSS_DSI_CONTENT_GENERIC,
+};
+
struct dss_clock_info {
/* rates that we get with dividers below */
unsigned long fck;
@@ -150,16 +155,6 @@ struct dsi_clock_info {
bool use_sys_clk;
};
-/* HDMI PLL structure */
-struct hdmi_pll_info {
- u16 regn;
- u16 regm;
- u32 regmf;
- u16 regm2;
- u16 regsd;
- u16 dcofreq;
-};
-
struct seq_file;
struct platform_device;
@@ -209,9 +204,8 @@ void dss_uninit_platform_driver(void);
int dss_runtime_get(void);
void dss_runtime_put(void);
-struct clk *dss_get_ick(void);
-
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
+enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void);
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
void dss_dump_clocks(struct seq_file *s);
@@ -279,6 +273,8 @@ void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir,
int dsi_init_display(struct omap_dss_device *display);
void dsi_irq_handler(void);
+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);
@@ -309,6 +305,11 @@ static inline int dsi_runtime_get(struct platform_device *dsidev)
static inline void dsi_runtime_put(struct platform_device *dsidev)
{
}
+static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
+{
+ WARN("%s: DSI not compiled in, returning pixel_size as 0\n", __func__);
+ return 0;
+}
static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
{
WARN("%s: DSI not compiled in, returning rate as 0\n", __func__);
@@ -385,90 +386,71 @@ void dispc_disable_sidle(void);
void dispc_lcd_enable_signal_polarity(bool act_high);
void dispc_lcd_enable_signal(bool enable);
void dispc_pck_free_enable(bool enable);
-void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable);
-
-void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height);
void dispc_set_digit_size(u16 width, u16 height);
-u32 dispc_get_plane_fifo_size(enum omap_plane plane);
-void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
void dispc_enable_fifomerge(bool enable);
-u32 dispc_get_burst_size(enum omap_plane plane);
-void dispc_enable_cpr(enum omap_channel channel, bool enable);
-void dispc_set_cpr_coef(enum omap_channel channel,
- struct omap_dss_cpr_coefs *coefs);
-
-void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
-void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
-void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y);
-void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height);
-void dispc_set_channel_out(enum omap_plane plane,
- enum omap_channel channel_out);
-
void dispc_enable_gamma_table(bool enable);
-int dispc_setup_plane(enum omap_plane plane,
- u32 paddr, u16 screen_width,
- u16 pos_x, u16 pos_y,
- u16 width, u16 height,
- u16 out_width, u16 out_height,
- enum omap_color_mode color_mode,
- bool ilace,
- enum omap_dss_rotation_type rotation_type,
- u8 rotation, bool mirror,
- u8 global_alpha, u8 pre_mult_alpha,
- enum omap_channel channel,
- u32 puv_addr);
-
-bool dispc_go_busy(enum omap_channel channel);
-void dispc_go(enum omap_channel channel);
-void dispc_enable_channel(enum omap_channel channel, bool enable);
-bool dispc_is_channel_enabled(enum omap_channel channel);
-int dispc_enable_plane(enum omap_plane plane, bool enable);
-void dispc_enable_replication(enum omap_plane plane, bool enable);
-
-void dispc_set_parallel_interface_mode(enum omap_channel channel,
- enum omap_parallel_interface_mode mode);
-void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_set_lcd_display_type(enum omap_channel channel,
- enum omap_lcd_display_type type);
void dispc_set_loadmode(enum omap_dss_load_mode mode);
-void dispc_set_default_color(enum omap_channel channel, u32 color);
-u32 dispc_get_default_color(enum omap_channel channel);
-void dispc_set_trans_key(enum omap_channel ch,
- enum omap_dss_trans_key_type type,
- u32 trans_key);
-void dispc_get_trans_key(enum omap_channel ch,
- enum omap_dss_trans_key_type *type,
- u32 *trans_key);
-void dispc_enable_trans_key(enum omap_channel ch, bool enable);
-void dispc_enable_alpha_blending(enum omap_channel ch, bool enable);
-bool dispc_trans_key_enabled(enum omap_channel ch);
-bool dispc_alpha_blending_enabled(enum omap_channel ch);
-
bool dispc_lcd_timings_ok(struct omap_video_timings *timings);
-void dispc_set_lcd_timings(enum omap_channel channel,
- struct omap_video_timings *timings);
unsigned long dispc_fclk_rate(void);
-unsigned long dispc_lclk_rate(enum omap_channel channel);
-unsigned long dispc_pclk_rate(enum omap_channel channel);
-void dispc_set_pol_freq(enum omap_channel channel,
- enum omap_panel_config config, u8 acbi, u8 acb);
void dispc_find_clk_divs(bool is_tft, 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);
-int dispc_set_clock_div(enum omap_channel channel,
+
+
+u32 dispc_ovl_get_fifo_size(enum omap_plane plane);
+u32 dispc_ovl_get_burst_size(enum omap_plane plane);
+int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
+ bool ilace, enum omap_channel channel, bool replication,
+ u32 fifo_low, u32 fifo_high);
+int dispc_ovl_enable(enum omap_plane plane, bool enable);
+
+
+void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable);
+void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height);
+void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable);
+void dispc_mgr_set_cpr_coef(enum omap_channel channel,
+ struct omap_dss_cpr_coefs *coefs);
+bool dispc_mgr_go_busy(enum omap_channel channel);
+void dispc_mgr_go(enum omap_channel channel);
+void dispc_mgr_enable(enum omap_channel channel, bool enable);
+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_default_color(enum omap_channel channel, u32 color);
+u32 dispc_mgr_get_default_color(enum omap_channel channel);
+void dispc_mgr_set_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type type,
+ u32 trans_key);
+void dispc_mgr_get_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type *type,
+ u32 *trans_key);
+void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable);
+void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable);
+bool dispc_mgr_trans_key_enabled(enum omap_channel ch);
+bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch);
+void dispc_mgr_set_lcd_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);
+int dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
-int dispc_get_clock_div(enum omap_channel channel,
+int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
-
/* VENC */
#ifdef CONFIG_OMAP2_DSS_VENC
int venc_init_platform_driver(void);
void venc_uninit_platform_driver(void);
void venc_dump_regs(struct seq_file *s);
int venc_init_display(struct omap_dss_device *display);
+unsigned long venc_get_pixel_clock(void);
#else
static inline int venc_init_platform_driver(void)
{
@@ -477,6 +459,11 @@ static inline int venc_init_platform_driver(void)
static inline void venc_uninit_platform_driver(void)
{
}
+static inline unsigned long venc_get_pixel_clock(void)
+{
+ WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__);
+ return 0;
+}
#endif
/* HDMI */
@@ -484,6 +471,8 @@ static inline void venc_uninit_platform_driver(void)
int hdmi_init_platform_driver(void);
void hdmi_uninit_platform_driver(void);
int hdmi_init_display(struct omap_dss_device *dssdev);
+unsigned long hdmi_get_pixel_clock(void);
+void hdmi_dump_regs(struct seq_file *s);
#else
static inline int hdmi_init_display(struct omap_dss_device *dssdev)
{
@@ -496,12 +485,19 @@ static inline int hdmi_init_platform_driver(void)
static inline void hdmi_uninit_platform_driver(void)
{
}
+static inline unsigned long hdmi_get_pixel_clock(void)
+{
+ WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__);
+ return 0;
+}
#endif
int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev);
void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev);
void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev);
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
+int omapdss_hdmi_read_edid(u8 *buf, int len);
+bool omapdss_hdmi_detect(void);
int hdmi_panel_init(void);
void hdmi_panel_exit(void);
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index b415c4ee621d..b402699168a5 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -47,6 +47,7 @@ struct omap_dss_features {
const int num_ovls;
const enum omap_display_type *supported_displays;
const enum omap_color_mode *supported_color_modes;
+ const enum omap_overlay_caps *overlay_caps;
const char * const *clksrc_names;
const struct dss_param_range *dss_params;
@@ -209,6 +210,68 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = {
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
+
+ /* OMAP_DSS_VIDEO3 */
+ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
+ OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
+ OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
+ OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
+ OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
+ OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
+ OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
+ OMAP_DSS_COLOR_RGBX32,
+};
+
+static const enum omap_overlay_caps omap2_dss_overlay_caps[] = {
+ /* OMAP_DSS_GFX */
+ 0,
+
+ /* OMAP_DSS_VIDEO1 */
+ OMAP_DSS_OVL_CAP_SCALE,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_OVL_CAP_SCALE,
+};
+
+static const enum omap_overlay_caps omap3430_dss_overlay_caps[] = {
+ /* OMAP_DSS_GFX */
+ OMAP_DSS_OVL_CAP_GLOBAL_ALPHA,
+
+ /* OMAP_DSS_VIDEO1 */
+ OMAP_DSS_OVL_CAP_SCALE,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA,
+};
+
+static const enum omap_overlay_caps omap3630_dss_overlay_caps[] = {
+ /* OMAP_DSS_GFX */
+ OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA,
+
+ /* OMAP_DSS_VIDEO1 */
+ OMAP_DSS_OVL_CAP_SCALE,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
+ OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA,
+};
+
+static const enum omap_overlay_caps omap4_dss_overlay_caps[] = {
+ /* OMAP_DSS_GFX */
+ OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
+ OMAP_DSS_OVL_CAP_ZORDER,
+
+ /* OMAP_DSS_VIDEO1 */
+ OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
+ OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
+ OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER,
+
+ /* OMAP_DSS_VIDEO3 */
+ OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
+ OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER,
};
static const char * const omap2_dss_clk_source_names[] = {
@@ -233,32 +296,38 @@ static const char * const omap4_dss_clk_source_names[] = {
static const struct dss_param_range omap2_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 173000000 },
+ [FEAT_PARAM_DSS_PCD] = { 2, 255 },
[FEAT_PARAM_DSIPLL_REGN] = { 0, 0 },
[FEAT_PARAM_DSIPLL_REGM] = { 0, 0 },
[FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, 0 },
[FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, 0 },
[FEAT_PARAM_DSIPLL_FINT] = { 0, 0 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, 0 },
+ [FEAT_PARAM_DOWNSCALE] = { 1, 2 },
};
static const struct dss_param_range omap3_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 173000000 },
+ [FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 7) - 1 },
[FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 11) - 1 },
[FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 4) - 1 },
[FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 4) - 1 },
[FEAT_PARAM_DSIPLL_FINT] = { 750000, 2100000 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1},
+ [FEAT_PARAM_DOWNSCALE] = { 1, 4 },
};
static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 186000000 },
+ [FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 8) - 1 },
[FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 12) - 1 },
[FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 5) - 1 },
[FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 5) - 1 },
[FEAT_PARAM_DSIPLL_FINT] = { 500000, 2500000 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
+ [FEAT_PARAM_DOWNSCALE] = { 1, 4 },
};
/* OMAP2 DSS Features */
@@ -275,6 +344,7 @@ static const struct omap_dss_features omap2_dss_features = {
.num_ovls = 3,
.supported_displays = omap2_dss_supported_displays,
.supported_color_modes = omap2_dss_supported_color_modes,
+ .overlay_caps = omap2_dss_overlay_caps,
.clksrc_names = omap2_dss_clk_source_names,
.dss_params = omap2_dss_param_range,
.buffer_size_unit = 1,
@@ -287,18 +357,19 @@ static const struct omap_dss_features omap3430_dss_features = {
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
.has_feature =
- FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL |
+ FEAT_LCDENABLEPOL |
FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
FEAT_FUNCGATED | FEAT_ROWREPEATENABLE |
FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF |
FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC |
FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD |
- FEAT_FIR_COEF_V,
+ FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER,
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap3430_dss_supported_displays,
.supported_color_modes = omap3_dss_supported_color_modes,
+ .overlay_caps = omap3430_dss_overlay_caps,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
.buffer_size_unit = 1,
@@ -310,18 +381,19 @@ static const struct omap_dss_features omap3630_dss_features = {
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
.has_feature =
- FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL |
+ FEAT_LCDENABLEPOL |
FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
- FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED |
+ FEAT_FUNCGATED |
FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT |
FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG |
FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD |
- FEAT_FIR_COEF_V,
+ FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER,
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap3630_dss_supported_displays,
.supported_color_modes = omap3_dss_supported_color_modes,
+ .overlay_caps = omap3630_dss_overlay_caps,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
.buffer_size_unit = 1,
@@ -335,17 +407,18 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = {
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
.has_feature =
- FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA |
- FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 |
+ FEAT_MGR_LCD2 |
FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 |
- FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V,
+ FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V |
+ FEAT_ALPHA_FREE_ZORDER,
.num_mgrs = 3,
- .num_ovls = 3,
+ .num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_color_modes = omap4_dss_supported_color_modes,
+ .overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
.buffer_size_unit = 16,
@@ -358,24 +431,50 @@ static const struct omap_dss_features omap4_dss_features = {
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
.has_feature =
- FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA |
- FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 |
+ FEAT_MGR_LCD2 |
FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE |
FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR |
- FEAT_PRELOAD | FEAT_FIR_COEF_V,
+ FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER,
.num_mgrs = 3,
- .num_ovls = 3,
+ .num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_color_modes = omap4_dss_supported_color_modes,
+ .overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
.buffer_size_unit = 16,
.burst_size_unit = 16,
};
+#if defined(CONFIG_OMAP4_DSS_HDMI)
+/* HDMI OMAP4 Functions*/
+static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
+
+ .video_configure = ti_hdmi_4xxx_basic_configure,
+ .phy_enable = ti_hdmi_4xxx_phy_enable,
+ .phy_disable = ti_hdmi_4xxx_phy_disable,
+ .read_edid = ti_hdmi_4xxx_read_edid,
+ .detect = ti_hdmi_4xxx_detect,
+ .pll_enable = ti_hdmi_4xxx_pll_enable,
+ .pll_disable = ti_hdmi_4xxx_pll_disable,
+ .video_enable = ti_hdmi_4xxx_wp_video_start,
+ .dump_wrapper = ti_hdmi_4xxx_wp_dump,
+ .dump_core = ti_hdmi_4xxx_core_dump,
+ .dump_pll = ti_hdmi_4xxx_pll_dump,
+ .dump_phy = ti_hdmi_4xxx_phy_dump,
+
+};
+
+void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data)
+{
+ if (cpu_is_omap44xx())
+ ip_data->ops = &omap4_hdmi_functions;
+}
+#endif
+
/* Functions returning values related to a DSS feature */
int dss_feat_get_num_mgrs(void)
{
@@ -407,6 +506,11 @@ enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
return omap_current_dss_features->supported_color_modes[plane];
}
+enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane)
+{
+ return omap_current_dss_features->overlay_caps[plane];
+}
+
bool dss_feat_color_mode_supported(enum omap_plane plane,
enum omap_color_mode color_mode)
{
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index b7398cbcda5f..6a6c05dd45ce 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -20,16 +20,17 @@
#ifndef __OMAP2_DSS_FEATURES_H
#define __OMAP2_DSS_FEATURES_H
+#if defined(CONFIG_OMAP4_DSS_HDMI)
+#include "ti_hdmi.h"
+#endif
+
#define MAX_DSS_MANAGERS 3
-#define MAX_DSS_OVERLAYS 3
+#define MAX_DSS_OVERLAYS 4
#define MAX_DSS_LCD_MANAGERS 2
#define MAX_NUM_DSI 2
/* DSS has feature id */
enum dss_feat_id {
- FEAT_GLOBAL_ALPHA = 1 << 0,
- FEAT_GLOBAL_ALPHA_VID1 = 1 << 1,
- FEAT_PRE_MULT_ALPHA = 1 << 2,
FEAT_LCDENABLEPOL = 1 << 3,
FEAT_LCDENABLESIGNAL = 1 << 4,
FEAT_PCKFREEENABLE = 1 << 5,
@@ -55,6 +56,8 @@ enum dss_feat_id {
FEAT_CPR = 1 << 23,
FEAT_PRELOAD = 1 << 24,
FEAT_FIR_COEF_V = 1 << 25,
+ FEAT_ALPHA_FIXED_ZORDER = 1 << 26,
+ FEAT_ALPHA_FREE_ZORDER = 1 << 27,
};
/* DSS register field id */
@@ -75,12 +78,14 @@ enum dss_feat_reg_field {
enum dss_range_param {
FEAT_PARAM_DSS_FCK,
+ FEAT_PARAM_DSS_PCD,
FEAT_PARAM_DSIPLL_REGN,
FEAT_PARAM_DSIPLL_REGM,
FEAT_PARAM_DSIPLL_REGM_DISPC,
FEAT_PARAM_DSIPLL_REGM_DSI,
FEAT_PARAM_DSIPLL_FINT,
FEAT_PARAM_DSIPLL_LPDIV,
+ FEAT_PARAM_DOWNSCALE,
};
/* DSS Feature Functions */
@@ -90,6 +95,7 @@ unsigned long dss_feat_get_param_min(enum dss_range_param param);
unsigned long dss_feat_get_param_max(enum dss_range_param param);
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane);
+enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane);
bool dss_feat_color_mode_supported(enum omap_plane plane,
enum omap_color_mode color_mode);
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id);
@@ -100,4 +106,7 @@ u32 dss_feat_get_burst_size_unit(void); /* in bytes */
bool dss_has_feature(enum dss_feat_id id);
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
void dss_features_init(void);
+#if defined(CONFIG_OMAP4_DSS_HDMI)
+void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data);
+#endif
#endif
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 256f27a9064a..3262f0f1fa35 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -37,26 +37,41 @@
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include "ti_hdmi_4xxx_ip.h"
#endif
+#include "ti_hdmi.h"
#include "dss.h"
-#include "hdmi.h"
#include "dss_features.h"
+#define HDMI_WP 0x0
+#define HDMI_CORE_SYS 0x400
+#define HDMI_CORE_AV 0x900
+#define HDMI_PLLCTRL 0x200
+#define HDMI_PHY 0x300
+
+/* HDMI EDID Length move this */
+#define HDMI_EDID_MAX_LENGTH 256
+#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
+#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
+#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
+#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
+#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
+
+#define OMAP_HDMI_TIMINGS_NB 34
+
+#define HDMI_DEFAULT_REGN 16
+#define HDMI_DEFAULT_REGM2 1
+
static struct {
struct mutex lock;
struct omap_display_platform_data *pdata;
struct platform_device *pdev;
- void __iomem *base_wp; /* HDMI wrapper */
+ struct hdmi_ip_data ip_data;
int code;
int mode;
- u8 edid[HDMI_EDID_MAX_LENGTH];
- u8 edid_set;
- bool custom_set;
- struct hdmi_config cfg;
struct clk *sys_clk;
- struct clk *hdmi_clk;
} hdmi;
/*
@@ -144,30 +159,6 @@ static const int code_vesa[85] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 27, 28, -1, 33};
-static const u8 edid_header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0};
-
-static inline void hdmi_write_reg(const struct hdmi_reg idx, u32 val)
-{
- __raw_writel(val, hdmi.base_wp + idx.idx);
-}
-
-static inline u32 hdmi_read_reg(const struct hdmi_reg idx)
-{
- return __raw_readl(hdmi.base_wp + idx.idx);
-}
-
-static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx,
- int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
-
static int hdmi_runtime_get(void)
{
int r;
@@ -193,304 +184,7 @@ int hdmi_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("init_display\n");
- return 0;
-}
-
-static int hdmi_pll_init(enum hdmi_clk_refsel refsel, int dcofreq,
- struct hdmi_pll_info *fmt, u16 sd)
-{
- u32 r;
-
- /* PLL start always use manual mode */
- REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
-
- r = hdmi_read_reg(PLLCTRL_CFG1);
- r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
- r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1_PLL_REGN */
-
- hdmi_write_reg(PLLCTRL_CFG1, r);
-
- r = hdmi_read_reg(PLLCTRL_CFG2);
-
- r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
- r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
- r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
-
- if (dcofreq) {
- /* divider programming for frequency beyond 1000Mhz */
- REG_FLD_MOD(PLLCTRL_CFG3, sd, 17, 10);
- r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
- } else {
- r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
- }
-
- hdmi_write_reg(PLLCTRL_CFG2, r);
-
- r = hdmi_read_reg(PLLCTRL_CFG4);
- r = FLD_MOD(r, fmt->regm2, 24, 18);
- r = FLD_MOD(r, fmt->regmf, 17, 0);
-
- hdmi_write_reg(PLLCTRL_CFG4, r);
-
- /* go now */
- REG_FLD_MOD(PLLCTRL_PLL_GO, 0x1, 0, 0);
-
- /* wait for bit change */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_GO, 0, 0, 1) != 1) {
- DSSERR("PLL GO bit not set\n");
- return -ETIMEDOUT;
- }
-
- /* Wait till the lock bit is set in PLL status */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
- DSSWARN("cannot lock PLL\n");
- DSSWARN("CFG1 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG1));
- DSSWARN("CFG2 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG2));
- DSSWARN("CFG4 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG4));
- return -ETIMEDOUT;
- }
-
- DSSDBG("PLL locked!\n");
-
- return 0;
-}
-
-/* PHY_PWR_CMD */
-static int hdmi_set_phy_pwr(enum hdmi_phy_pwr val)
-{
- /* Command for power control of HDMI PHY */
- REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 7, 6);
-
- /* Status of the power control of HDMI PHY */
- if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 5, 4, val) != val) {
- DSSERR("Failed to set PHY power mode to %d\n", val);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-/* PLL_PWR_CMD */
-static int hdmi_set_pll_pwr(enum hdmi_pll_pwr val)
-{
- /* Command for power control of HDMI PLL */
- REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 3, 2);
-
- /* wait till PHY_PWR_STATUS is set */
- if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 1, 0, val) != val) {
- DSSERR("Failed to set PHY_PWR_STATUS\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int hdmi_pll_reset(void)
-{
- /* SYSRESET controlled by power FSM */
- REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
-
- /* READ 0x0 reset is in progress */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) {
- DSSERR("Failed to sysreset PLL\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int hdmi_phy_init(void)
-{
- u16 r = 0;
-
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON);
- if (r)
- return r;
-
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
- if (r)
- return r;
-
- /*
- * Read address 0 in order to get the SCP reset done completed
- * Dummy access performed to make sure reset is done
- */
- hdmi_read_reg(HDMI_TXPHY_TX_CTRL);
-
- /*
- * Write to phy address 0 to configure the clock
- * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
- */
- REG_FLD_MOD(HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
-
- /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
- hdmi_write_reg(HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
-
- /* Setup max LDO voltage */
- REG_FLD_MOD(HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
-
- /* Write to phy address 3 to change the polarity control */
- REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
-
- return 0;
-}
-
-static int hdmi_pll_program(struct hdmi_pll_info *fmt)
-{
- u16 r = 0;
- enum hdmi_clk_refsel refsel;
-
- r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
- if (r)
- return r;
-
- r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
- if (r)
- return r;
-
- r = hdmi_pll_reset();
- if (r)
- return r;
-
- refsel = HDMI_REFSEL_SYSCLK;
-
- r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd);
- if (r)
- return r;
-
- return 0;
-}
-
-static void hdmi_phy_off(void)
-{
- hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
-}
-
-static int hdmi_core_ddc_edid(u8 *pedid, int ext)
-{
- u32 i, j;
- char checksum = 0;
- u32 offset = 0;
-
- /* Turn on CLK for DDC */
- REG_FLD_MOD(HDMI_CORE_AV_DPD, 0x7, 2, 0);
-
- /*
- * SW HACK : Without the Delay DDC(i2c bus) reads 0 values /
- * right shifted values( The behavior is not consistent and seen only
- * with some TV's)
- */
- usleep_range(800, 1000);
-
- if (!ext) {
- /* Clk SCL Devices */
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0xA, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_IN_PROG */
- if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS,
- 4, 4, 0) != 0) {
- DSSERR("Failed to program DDC\n");
- return -ETIMEDOUT;
- }
-
- /* Clear FIFO */
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x9, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_IN_PROG */
- if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS,
- 4, 4, 0) != 0) {
- DSSERR("Failed to program DDC\n");
- return -ETIMEDOUT;
- }
-
- } else {
- if (ext % 2 != 0)
- offset = 0x80;
- }
-
- /* Load Segment Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_SEGM, ext/2, 7, 0);
-
- /* Load Slave Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
-
- /* Load Offset Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_OFFSET, offset, 7, 0);
-
- /* Load Byte Count */
- REG_FLD_MOD(HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
- REG_FLD_MOD(HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
-
- /* Set DDC_CMD */
- if (ext)
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x4, 3, 0);
- else
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x2, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_BUS_LOW */
- if (REG_GET(HDMI_CORE_DDC_STATUS, 6, 6) == 1) {
- DSSWARN("I2C Bus Low?\n");
- return -EIO;
- }
- /* HDMI_CORE_DDC_STATUS_NO_ACK */
- if (REG_GET(HDMI_CORE_DDC_STATUS, 5, 5) == 1) {
- DSSWARN("I2C No Ack\n");
- return -EIO;
- }
-
- i = ext * 128;
- j = 0;
- while (((REG_GET(HDMI_CORE_DDC_STATUS, 4, 4) == 1) ||
- (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0)) &&
- j < 128) {
-
- if (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0) {
- /* FIFO not empty */
- pedid[i++] = REG_GET(HDMI_CORE_DDC_DATA, 7, 0);
- j++;
- }
- }
-
- for (j = 0; j < 128; j++)
- checksum += pedid[j];
-
- if (checksum != 0) {
- DSSERR("E-EDID checksum failed!!\n");
- return -EIO;
- }
-
- return 0;
-}
-
-static int read_edid(u8 *pedid, u16 max_length)
-{
- int r = 0, n = 0, i = 0;
- int max_ext_blocks = (max_length / 128) - 1;
-
- r = hdmi_core_ddc_edid(pedid, 0);
- if (r) {
- return r;
- } else {
- n = pedid[0x7e];
-
- /*
- * README: need to comply with max_length set by the caller.
- * Better implementation should be to allocate necessary
- * memory to store EDID according to nb_block field found
- * in first block
- */
- if (n > max_ext_blocks)
- n = max_ext_blocks;
-
- for (i = 1; i <= n; i++) {
- r = hdmi_core_ddc_edid(pedid, i);
- if (r)
- return r;
- }
- }
+ dss_init_hdmi_ip_ops(&hdmi.ip_data);
return 0;
}
@@ -518,7 +212,7 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
{
int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0;
int timing_vsync = 0, timing_hsync = 0;
- struct omap_video_timings temp;
+ struct hdmi_video_timings temp;
struct hdmi_cm cm = {-1};
DSSDBG("hdmi_get_code\n");
@@ -556,500 +250,6 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
return cm;
}
-static void get_horz_vert_timing_info(int current_descriptor_addrs, u8 *edid ,
- struct omap_video_timings *timings)
-{
- /* X and Y resolution */
- timings->x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) |
- edid[current_descriptor_addrs + 2]);
- timings->y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) |
- edid[current_descriptor_addrs + 5]);
-
- timings->pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) |
- edid[current_descriptor_addrs]);
-
- timings->pixel_clock = 10 * timings->pixel_clock;
-
- /* HORIZONTAL FRONT PORCH */
- timings->hfp = edid[current_descriptor_addrs + 8] |
- ((edid[current_descriptor_addrs + 11] & 0xc0) << 2);
- /* HORIZONTAL SYNC WIDTH */
- timings->hsw = edid[current_descriptor_addrs + 9] |
- ((edid[current_descriptor_addrs + 11] & 0x30) << 4);
- /* HORIZONTAL BACK PORCH */
- timings->hbp = (((edid[current_descriptor_addrs + 4] & 0x0F) << 8) |
- edid[current_descriptor_addrs + 3]) -
- (timings->hfp + timings->hsw);
- /* VERTICAL FRONT PORCH */
- timings->vfp = ((edid[current_descriptor_addrs + 10] & 0xF0) >> 4) |
- ((edid[current_descriptor_addrs + 11] & 0x0f) << 2);
- /* VERTICAL SYNC WIDTH */
- timings->vsw = (edid[current_descriptor_addrs + 10] & 0x0F) |
- ((edid[current_descriptor_addrs + 11] & 0x03) << 4);
- /* VERTICAL BACK PORCH */
- timings->vbp = (((edid[current_descriptor_addrs + 7] & 0x0F) << 8) |
- edid[current_descriptor_addrs + 6]) -
- (timings->vfp + timings->vsw);
-
-}
-
-/* Description : This function gets the resolution information from EDID */
-static void get_edid_timing_data(u8 *edid)
-{
- u8 count;
- u16 current_descriptor_addrs;
- struct hdmi_cm cm;
- struct omap_video_timings edid_timings;
-
- /* search block 0, there are 4 DTDs arranged in priority order */
- for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) {
- current_descriptor_addrs =
- EDID_DESCRIPTOR_BLOCK0_ADDRESS +
- count * EDID_TIMING_DESCRIPTOR_SIZE;
- get_horz_vert_timing_info(current_descriptor_addrs,
- edid, &edid_timings);
- cm = hdmi_get_code(&edid_timings);
- DSSDBG("Block0[%d] value matches code = %d , mode = %d\n",
- count, cm.code, cm.mode);
- if (cm.code == -1) {
- continue;
- } else {
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
- DSSDBG("code = %d , mode = %d\n",
- hdmi.code, hdmi.mode);
- return;
- }
- }
- if (edid[0x7e] != 0x00) {
- for (count = 0; count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR;
- count++) {
- current_descriptor_addrs =
- EDID_DESCRIPTOR_BLOCK1_ADDRESS +
- count * EDID_TIMING_DESCRIPTOR_SIZE;
- get_horz_vert_timing_info(current_descriptor_addrs,
- edid, &edid_timings);
- cm = hdmi_get_code(&edid_timings);
- DSSDBG("Block1[%d] value matches code = %d, mode = %d",
- count, cm.code, cm.mode);
- if (cm.code == -1) {
- continue;
- } else {
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
- DSSDBG("code = %d , mode = %d\n",
- hdmi.code, hdmi.mode);
- return;
- }
- }
- }
-
- DSSINFO("no valid timing found , falling back to VGA\n");
- hdmi.code = 4; /* setting default value of 640 480 VGA */
- hdmi.mode = HDMI_DVI;
-}
-
-static void hdmi_read_edid(struct omap_video_timings *dp)
-{
- int ret = 0, code;
-
- memset(hdmi.edid, 0, HDMI_EDID_MAX_LENGTH);
-
- if (!hdmi.edid_set)
- ret = read_edid(hdmi.edid, HDMI_EDID_MAX_LENGTH);
-
- if (!ret) {
- if (!memcmp(hdmi.edid, edid_header, sizeof(edid_header))) {
- /* search for timings of default resolution */
- get_edid_timing_data(hdmi.edid);
- hdmi.edid_set = true;
- }
- } else {
- DSSWARN("failed to read E-EDID\n");
- }
-
- if (!hdmi.edid_set) {
- DSSINFO("fallback to VGA\n");
- hdmi.code = 4; /* setting default value of 640 480 VGA */
- hdmi.mode = HDMI_DVI;
- }
-
- code = get_timings_index();
-
- *dp = cea_vesa_timings[code].timings;
-}
-
-static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
- struct hdmi_core_infoframe_avi *avi_cfg,
- struct hdmi_core_packet_enable_repeat *repeat_cfg)
-{
- DSSDBG("Enter hdmi_core_init\n");
-
- /* video core */
- video_cfg->ip_bus_width = HDMI_INPUT_8BIT;
- video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT;
- video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE;
- video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE;
- video_cfg->hdmi_dvi = HDMI_DVI;
- video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK;
-
- /* info frame */
- avi_cfg->db1_format = 0;
- avi_cfg->db1_active_info = 0;
- avi_cfg->db1_bar_info_dv = 0;
- avi_cfg->db1_scan_info = 0;
- avi_cfg->db2_colorimetry = 0;
- avi_cfg->db2_aspect_ratio = 0;
- avi_cfg->db2_active_fmt_ar = 0;
- avi_cfg->db3_itc = 0;
- avi_cfg->db3_ec = 0;
- avi_cfg->db3_q_range = 0;
- avi_cfg->db3_nup_scaling = 0;
- avi_cfg->db4_videocode = 0;
- avi_cfg->db5_pixel_repeat = 0;
- avi_cfg->db6_7_line_eoftop = 0 ;
- avi_cfg->db8_9_line_sofbottom = 0;
- avi_cfg->db10_11_pixel_eofleft = 0;
- avi_cfg->db12_13_pixel_sofright = 0;
-
- /* packet enable and repeat */
- repeat_cfg->audio_pkt = 0;
- repeat_cfg->audio_pkt_repeat = 0;
- repeat_cfg->avi_infoframe = 0;
- repeat_cfg->avi_infoframe_repeat = 0;
- repeat_cfg->gen_cntrl_pkt = 0;
- repeat_cfg->gen_cntrl_pkt_repeat = 0;
- repeat_cfg->generic_pkt = 0;
- repeat_cfg->generic_pkt_repeat = 0;
-}
-
-static void hdmi_core_powerdown_disable(void)
-{
- DSSDBG("Enter hdmi_core_powerdown_disable\n");
- REG_FLD_MOD(HDMI_CORE_CTRL1, 0x0, 0, 0);
-}
-
-static void hdmi_core_swreset_release(void)
-{
- DSSDBG("Enter hdmi_core_swreset_release\n");
- REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x0, 0, 0);
-}
-
-static void hdmi_core_swreset_assert(void)
-{
- DSSDBG("Enter hdmi_core_swreset_assert\n");
- REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x1, 0, 0);
-}
-
-/* DSS_HDMI_CORE_VIDEO_CONFIG */
-static void hdmi_core_video_config(struct hdmi_core_video_config *cfg)
-{
- u32 r = 0;
-
- /* sys_ctrl1 default configuration not tunable */
- r = hdmi_read_reg(HDMI_CORE_CTRL1);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1);
- hdmi_write_reg(HDMI_CORE_CTRL1, r);
-
- REG_FLD_MOD(HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6);
-
- /* Vid_Mode */
- r = hdmi_read_reg(HDMI_CORE_SYS_VID_MODE);
-
- /* dither truncation configuration */
- if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) {
- r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6);
- r = FLD_MOD(r, 1, 5, 5);
- } else {
- r = FLD_MOD(r, cfg->op_dither_truc, 7, 6);
- r = FLD_MOD(r, 0, 5, 5);
- }
- hdmi_write_reg(HDMI_CORE_SYS_VID_MODE, r);
-
- /* HDMI_Ctrl */
- r = hdmi_read_reg(HDMI_CORE_AV_HDMI_CTRL);
- r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6);
- r = FLD_MOD(r, cfg->pkt_mode, 5, 3);
- r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_HDMI_CTRL, r);
-
- /* TMDS_CTRL */
- REG_FLD_MOD(HDMI_CORE_SYS_TMDS_CTRL,
- cfg->tclk_sel_clkmult, 6, 5);
-}
-
-static void hdmi_core_aux_infoframe_avi_config(
- struct hdmi_core_infoframe_avi info_avi)
-{
- u32 val;
- char sum = 0, checksum = 0;
-
- sum += 0x82 + 0x002 + 0x00D;
- hdmi_write_reg(HDMI_CORE_AV_AVI_TYPE, 0x082);
- hdmi_write_reg(HDMI_CORE_AV_AVI_VERS, 0x002);
- hdmi_write_reg(HDMI_CORE_AV_AVI_LEN, 0x00D);
-
- val = (info_avi.db1_format << 5) |
- (info_avi.db1_active_info << 4) |
- (info_avi.db1_bar_info_dv << 2) |
- (info_avi.db1_scan_info);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(0), val);
- sum += val;
-
- val = (info_avi.db2_colorimetry << 6) |
- (info_avi.db2_aspect_ratio << 4) |
- (info_avi.db2_active_fmt_ar);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(1), val);
- sum += val;
-
- val = (info_avi.db3_itc << 7) |
- (info_avi.db3_ec << 4) |
- (info_avi.db3_q_range << 2) |
- (info_avi.db3_nup_scaling);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(2), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(3), info_avi.db4_videocode);
- sum += info_avi.db4_videocode;
-
- val = info_avi.db5_pixel_repeat;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(4), val);
- sum += val;
-
- val = info_avi.db6_7_line_eoftop & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(5), val);
- sum += val;
-
- val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(6), val);
- sum += val;
-
- val = info_avi.db8_9_line_sofbottom & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(7), val);
- sum += val;
-
- val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(8), val);
- sum += val;
-
- val = info_avi.db10_11_pixel_eofleft & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(9), val);
- sum += val;
-
- val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(10), val);
- sum += val;
-
- val = info_avi.db12_13_pixel_sofright & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(11), val);
- sum += val;
-
- val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(12), val);
- sum += val;
-
- checksum = 0x100 - sum;
- hdmi_write_reg(HDMI_CORE_AV_AVI_CHSUM, checksum);
-}
-
-static void hdmi_core_av_packet_config(
- struct hdmi_core_packet_enable_repeat repeat_cfg)
-{
- /* enable/repeat the infoframe */
- hdmi_write_reg(HDMI_CORE_AV_PB_CTRL1,
- (repeat_cfg.audio_pkt << 5) |
- (repeat_cfg.audio_pkt_repeat << 4) |
- (repeat_cfg.avi_infoframe << 1) |
- (repeat_cfg.avi_infoframe_repeat));
-
- /* enable/repeat the packet */
- hdmi_write_reg(HDMI_CORE_AV_PB_CTRL2,
- (repeat_cfg.gen_cntrl_pkt << 3) |
- (repeat_cfg.gen_cntrl_pkt_repeat << 2) |
- (repeat_cfg.generic_pkt << 1) |
- (repeat_cfg.generic_pkt_repeat));
-}
-
-static void hdmi_wp_init(struct omap_video_timings *timings,
- struct hdmi_video_format *video_fmt,
- struct hdmi_video_interface *video_int)
-{
- DSSDBG("Enter hdmi_wp_init\n");
-
- timings->hbp = 0;
- timings->hfp = 0;
- timings->hsw = 0;
- timings->vbp = 0;
- timings->vfp = 0;
- timings->vsw = 0;
-
- video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
- video_fmt->y_res = 0;
- video_fmt->x_res = 0;
-
- video_int->vsp = 0;
- video_int->hsp = 0;
-
- video_int->interlacing = 0;
- video_int->tm = 0; /* HDMI_TIMING_SLAVE */
-
-}
-
-static void hdmi_wp_video_start(bool start)
-{
- REG_FLD_MOD(HDMI_WP_VIDEO_CFG, start, 31, 31);
-}
-
-static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
- struct omap_video_timings *timings, struct hdmi_config *param)
-{
- DSSDBG("Enter hdmi_wp_video_init_format\n");
-
- video_fmt->y_res = param->timings.timings.y_res;
- video_fmt->x_res = param->timings.timings.x_res;
-
- timings->hbp = param->timings.timings.hbp;
- timings->hfp = param->timings.timings.hfp;
- timings->hsw = param->timings.timings.hsw;
- timings->vbp = param->timings.timings.vbp;
- timings->vfp = param->timings.timings.vfp;
- timings->vsw = param->timings.timings.vsw;
-}
-
-static void hdmi_wp_video_config_format(
- struct hdmi_video_format *video_fmt)
-{
- u32 l = 0;
-
- REG_FLD_MOD(HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 10, 8);
-
- l |= FLD_VAL(video_fmt->y_res, 31, 16);
- l |= FLD_VAL(video_fmt->x_res, 15, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_SIZE, l);
-}
-
-static void hdmi_wp_video_config_interface(
- struct hdmi_video_interface *video_int)
-{
- u32 r;
- DSSDBG("Enter hdmi_wp_video_config_interface\n");
-
- r = hdmi_read_reg(HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, video_int->vsp, 7, 7);
- r = FLD_MOD(r, video_int->hsp, 6, 6);
- r = FLD_MOD(r, video_int->interlacing, 3, 3);
- r = FLD_MOD(r, video_int->tm, 1, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_CFG, r);
-}
-
-static void hdmi_wp_video_config_timing(
- struct omap_video_timings *timings)
-{
- u32 timing_h = 0;
- u32 timing_v = 0;
-
- DSSDBG("Enter hdmi_wp_video_config_timing\n");
-
- timing_h |= FLD_VAL(timings->hbp, 31, 20);
- timing_h |= FLD_VAL(timings->hfp, 19, 8);
- timing_h |= FLD_VAL(timings->hsw, 7, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_TIMING_H, timing_h);
-
- timing_v |= FLD_VAL(timings->vbp, 31, 20);
- timing_v |= FLD_VAL(timings->vfp, 19, 8);
- timing_v |= FLD_VAL(timings->vsw, 7, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_TIMING_V, timing_v);
-}
-
-static void hdmi_basic_configure(struct hdmi_config *cfg)
-{
- /* HDMI */
- struct omap_video_timings video_timing;
- struct hdmi_video_format video_format;
- struct hdmi_video_interface video_interface;
- /* HDMI core */
- struct hdmi_core_infoframe_avi avi_cfg;
- struct hdmi_core_video_config v_core_cfg;
- struct hdmi_core_packet_enable_repeat repeat_cfg;
-
- hdmi_wp_init(&video_timing, &video_format,
- &video_interface);
-
- hdmi_core_init(&v_core_cfg,
- &avi_cfg,
- &repeat_cfg);
-
- hdmi_wp_video_init_format(&video_format,
- &video_timing, cfg);
-
- hdmi_wp_video_config_timing(&video_timing);
-
- /* video config */
- video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
-
- hdmi_wp_video_config_format(&video_format);
-
- video_interface.vsp = cfg->timings.vsync_pol;
- video_interface.hsp = cfg->timings.hsync_pol;
- video_interface.interlacing = cfg->interlace;
- video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */
-
- hdmi_wp_video_config_interface(&video_interface);
-
- /*
- * configure core video part
- * set software reset in the core
- */
- hdmi_core_swreset_assert();
-
- /* power down off */
- hdmi_core_powerdown_disable();
-
- v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;
- v_core_cfg.hdmi_dvi = cfg->cm.mode;
-
- hdmi_core_video_config(&v_core_cfg);
-
- /* release software reset in the core */
- hdmi_core_swreset_release();
-
- /*
- * configure packet
- * info frame video see doc CEA861-D page 65
- */
- avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
- avi_cfg.db1_active_info =
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
- avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
- avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
- avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
- avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
- avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
- avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
- avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
- avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
- avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
- avi_cfg.db4_videocode = cfg->cm.code;
- avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
- avi_cfg.db6_7_line_eoftop = 0;
- avi_cfg.db8_9_line_sofbottom = 0;
- avi_cfg.db10_11_pixel_eofleft = 0;
- avi_cfg.db12_13_pixel_sofright = 0;
-
- hdmi_core_aux_infoframe_avi_config(avi_cfg);
-
- /* enable/repeat the infoframe */
- repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
- repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON;
- /* wakeup */
- repeat_cfg.audio_pkt = HDMI_PACKETENABLE;
- repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON;
- hdmi_core_av_packet_config(repeat_cfg);
-}
-
static void update_hdmi_timings(struct hdmi_config *cfg,
struct omap_video_timings *timings, int code)
{
@@ -1066,6 +266,12 @@ static void update_hdmi_timings(struct hdmi_config *cfg,
cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol;
}
+unsigned long hdmi_get_pixel_clock(void)
+{
+ /* HDMI Pixel Clock in Mhz */
+ return hdmi.ip_data.cfg.timings.timings.pixel_clock * 10000;
+}
+
static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
struct hdmi_pll_info *pi)
{
@@ -1077,15 +283,23 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
* Input clock is predivided by N + 1
* out put of which is reference clk
*/
- pi->regn = dssdev->clocks.hdmi.regn;
- refclk = clkin / (pi->regn + 1);
+ if (dssdev->clocks.hdmi.regn == 0)
+ pi->regn = HDMI_DEFAULT_REGN;
+ else
+ pi->regn = dssdev->clocks.hdmi.regn;
+
+ refclk = clkin / pi->regn;
/*
* multiplier is pixel_clk/ref_clk
* Multiplying by 100 to avoid fractional part removal
*/
pi->regm = (phy * 100 / (refclk)) / 100;
- pi->regm2 = dssdev->clocks.hdmi.regm2;
+
+ if (dssdev->clocks.hdmi.regm2 == 0)
+ pi->regm2 = HDMI_DEFAULT_REGM2;
+ else
+ pi->regm2 = dssdev->clocks.hdmi.regm2;
/*
* fractional multiplier is remainder of the difference between
@@ -1100,7 +314,10 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
* is greater than 1000MHz
*/
pi->dcofreq = phy > 1000 * 100;
- pi->regsd = ((pi->regm * clkin / 10) / ((pi->regn + 1) * 250) + 5) / 10;
+ pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
+
+ /* Set the reference clock to sysclk reference */
+ pi->refsel = HDMI_REFSEL_SYSCLK;
DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
@@ -1109,7 +326,6 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
static int hdmi_power_on(struct omap_dss_device *dssdev)
{
int r, code = 0;
- struct hdmi_pll_info pll_data;
struct omap_video_timings *p;
unsigned long phy;
@@ -1117,7 +333,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
if (r)
return r;
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0);
+ dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0);
p = &dssdev->panel.timings;
@@ -1125,36 +341,31 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
- if (!hdmi.custom_set) {
- DSSDBG("Read EDID as no EDID is not set on poweron\n");
- hdmi_read_edid(p);
- }
code = get_timings_index();
- dssdev->panel.timings = cea_vesa_timings[code].timings;
- update_hdmi_timings(&hdmi.cfg, p, code);
+ update_hdmi_timings(&hdmi.ip_data.cfg, p, code);
phy = p->pixel_clock;
- hdmi_compute_pll(dssdev, phy, &pll_data);
+ hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
- hdmi_wp_video_start(0);
+ hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0);
- /* config the PLL and PHY first */
- r = hdmi_pll_program(&pll_data);
+ /* config the PLL and PHY hdmi_set_pll_pwrfirst */
+ r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data);
if (r) {
DSSDBG("Failed to lock PLL\n");
goto err;
}
- r = hdmi_phy_init();
+ r = hdmi.ip_data.ops->phy_enable(&hdmi.ip_data);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err;
}
- hdmi.cfg.cm.mode = hdmi.mode;
- hdmi.cfg.cm.code = hdmi.code;
- hdmi_basic_configure(&hdmi.cfg);
+ hdmi.ip_data.cfg.cm.mode = hdmi.mode;
+ hdmi.ip_data.cfg.cm.code = hdmi.code;
+ hdmi.ip_data.ops->video_configure(&hdmi.ip_data);
/* Make selection of HDMI in DSS */
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
@@ -1174,9 +385,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
dispc_set_digit_size(dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 1);
+ hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1);
- hdmi_wp_video_start(1);
+ dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 1);
return 0;
err:
@@ -1186,14 +397,12 @@ err:
static void hdmi_power_off(struct omap_dss_device *dssdev)
{
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0);
+ dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0);
- hdmi_wp_video_start(0);
- hdmi_phy_off();
- hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
+ hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0);
+ hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
+ hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
hdmi_runtime_put();
-
- hdmi.edid_set = 0;
}
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
@@ -1203,7 +412,6 @@ int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
cm = hdmi_get_code(timings);
if (cm.code == -1) {
- DSSERR("Invalid timing entered\n");
return -EINVAL;
}
@@ -1215,12 +423,69 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev)
{
struct hdmi_cm cm;
- hdmi.custom_set = 1;
cm = hdmi_get_code(&dssdev->panel.timings);
hdmi.code = cm.code;
hdmi.mode = cm.mode;
- omapdss_hdmi_display_enable(dssdev);
- hdmi.custom_set = 0;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ int r;
+
+ hdmi_power_off(dssdev);
+
+ r = hdmi_power_on(dssdev);
+ if (r)
+ DSSERR("failed to power on device\n");
+ }
+}
+
+void hdmi_dump_regs(struct seq_file *s)
+{
+ mutex_lock(&hdmi.lock);
+
+ if (hdmi_runtime_get())
+ return;
+
+ hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
+ hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
+ hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
+ hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
+
+ hdmi_runtime_put();
+ mutex_unlock(&hdmi.lock);
+}
+
+int omapdss_hdmi_read_edid(u8 *buf, int len)
+{
+ int r;
+
+ mutex_lock(&hdmi.lock);
+
+ r = hdmi_runtime_get();
+ BUG_ON(r);
+
+ r = hdmi.ip_data.ops->read_edid(&hdmi.ip_data, buf, len);
+
+ hdmi_runtime_put();
+ mutex_unlock(&hdmi.lock);
+
+ return r;
+}
+
+bool omapdss_hdmi_detect(void)
+{
+ int r;
+
+ mutex_lock(&hdmi.lock);
+
+ r = hdmi_runtime_get();
+ BUG_ON(r);
+
+ r = hdmi.ip_data.ops->detect(&hdmi.ip_data);
+
+ hdmi_runtime_put();
+ mutex_unlock(&hdmi.lock);
+
+ return r == 1;
}
int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
@@ -1231,6 +496,12 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
+ if (dssdev->manager == NULL) {
+ DSSERR("failed to enable display: no manager\n");
+ r = -ENODEV;
+ goto err0;
+ }
+
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
@@ -1282,219 +553,9 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
-static void hdmi_wp_audio_config_format(
- struct hdmi_audio_format *aud_fmt)
-{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_format\n");
-
- r = hdmi_read_reg(HDMI_WP_AUDIO_CFG);
- r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
- r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
- r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
- r = FLD_MOD(r, aud_fmt->type, 4, 4);
- r = FLD_MOD(r, aud_fmt->justification, 3, 3);
- r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
- r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
- r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CFG, r);
-}
-
-static void hdmi_wp_audio_config_dma(struct hdmi_audio_dma *aud_dma)
-{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_dma\n");
-
- r = hdmi_read_reg(HDMI_WP_AUDIO_CFG2);
- r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
- r = FLD_MOD(r, aud_dma->block_size, 7, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CFG2, r);
-
- r = hdmi_read_reg(HDMI_WP_AUDIO_CTRL);
- r = FLD_MOD(r, aud_dma->mode, 9, 9);
- r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CTRL, r);
-}
-
-static void hdmi_core_audio_config(struct hdmi_core_audio_config *cfg)
-{
- u32 r;
-
- /* audio clock recovery parameters */
- r = hdmi_read_reg(HDMI_CORE_AV_ACR_CTRL);
- r = FLD_MOD(r, cfg->use_mclk, 2, 2);
- r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
- r = FLD_MOD(r, cfg->cts_mode, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_ACR_CTRL, r);
-
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0);
-
- if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) {
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0);
- } else {
- /*
- * HDMI IP uses this configuration to divide the MCLK to
- * update CTS value.
- */
- REG_FLD_MOD(HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
-
- /* Configure clock for audio packets */
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_1,
- cfg->aud_par_busclk, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_2,
- (cfg->aud_par_busclk >> 8), 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_3,
- (cfg->aud_par_busclk >> 16), 7, 0);
- }
-
- /* Override of SPDIF sample frequency with value in I2S_CHST4 */
- REG_FLD_MOD(HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1);
-
- /* I2S parameters */
- REG_FLD_MOD(HDMI_CORE_AV_I2S_CHST4, cfg->freq_sample, 3, 0);
-
- r = hdmi_read_reg(HDMI_CORE_AV_I2S_IN_CTRL);
- r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7);
- r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6);
- r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5);
- r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4);
- r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3);
- r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2);
- r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1);
- r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_I2S_IN_CTRL, r);
-
- r = hdmi_read_reg(HDMI_CORE_AV_I2S_CHST5);
- r = FLD_MOD(r, cfg->freq_sample, 7, 4);
- r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1);
- r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_I2S_CHST5, r);
-
- REG_FLD_MOD(HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0);
-
- /* Audio channels and mode parameters */
- REG_FLD_MOD(HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1);
- r = hdmi_read_reg(HDMI_CORE_AV_AUD_MODE);
- r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4);
- r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3);
- r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2);
- r = FLD_MOD(r, cfg->en_spdif, 1, 1);
- hdmi_write_reg(HDMI_CORE_AV_AUD_MODE, r);
-}
-
-static void hdmi_core_audio_infoframe_config(
- struct hdmi_core_infoframe_audio *info_aud)
-{
- u8 val;
- u8 sum = 0, checksum = 0;
-
- /*
- * Set audio info frame type, version and length as
- * described in HDMI 1.4a Section 8.2.2 specification.
- * Checksum calculation is defined in Section 5.3.5.
- */
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_TYPE, 0x84);
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_VERS, 0x01);
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_LEN, 0x0a);
- sum += 0x84 + 0x001 + 0x00a;
-
- val = (info_aud->db1_coding_type << 4)
- | (info_aud->db1_channel_count - 1);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(0), val);
- sum += val;
-
- val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size;
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(1), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(2), 0x00);
-
- val = info_aud->db4_channel_alloc;
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(3), val);
- sum += val;
-
- val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(4), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(5), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(6), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(7), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(8), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(9), 0x00);
- checksum = 0x100 - sum;
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_CHSUM, checksum);
-
- /*
- * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing
- * is available.
- */
-}
-
-static int hdmi_config_audio_acr(u32 sample_freq, u32 *n, u32 *cts)
-{
- u32 r;
- u32 deep_color = 0;
- u32 pclk = hdmi.cfg.timings.timings.pixel_clock;
-
- if (n == NULL || cts == NULL)
- return -EINVAL;
- /*
- * Obtain current deep color configuration. This needed
- * to calculate the TMDS clock based on the pixel clock.
- */
- r = REG_GET(HDMI_WP_VIDEO_CFG, 1, 0);
- switch (r) {
- case 1: /* No deep color selected */
- deep_color = 100;
- break;
- case 2: /* 10-bit deep color selected */
- deep_color = 125;
- break;
- case 3: /* 12-bit deep color selected */
- deep_color = 150;
- break;
- default:
- return -EINVAL;
- }
-
- switch (sample_freq) {
- case 32000:
- if ((deep_color == 125) && ((pclk == 54054)
- || (pclk == 74250)))
- *n = 8192;
- else
- *n = 4096;
- break;
- case 44100:
- *n = 6272;
- break;
- case 48000:
- if ((deep_color == 125) && ((pclk == 54054)
- || (pclk == 74250)))
- *n = 8192;
- else
- *n = 6144;
- break;
- default:
- *n = 0;
- return -EINVAL;
- }
-
- /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
- *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
-
- return 0;
-}
-
-static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
+static int hdmi_audio_hw_params(struct hdmi_ip_data *ip_data,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
@@ -1548,7 +609,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- err = hdmi_config_audio_acr(params_rate(params), &n, &cts);
+ err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts);
if (err < 0)
return err;
@@ -1564,8 +625,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
audio_dma.fifo_threshold = 0x20; /* in number of samples */
- hdmi_wp_audio_config_dma(&audio_dma);
- hdmi_wp_audio_config_format(&audio_format);
+ hdmi_wp_audio_config_dma(ip_data, &audio_dma);
+ hdmi_wp_audio_config_format(ip_data, &audio_format);
/*
* I2S config
@@ -1609,7 +670,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
/* Use parallel audio interface */
core_cfg.en_parallel_aud_input = true;
- hdmi_core_audio_config(&core_cfg);
+ hdmi_core_audio_config(ip_data, &core_cfg);
/*
* Configure packet
@@ -1623,36 +684,10 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
aud_if_cfg.db5_downmix_inh = false;
aud_if_cfg.db5_lsv = 0;
- hdmi_core_audio_infoframe_config(&aud_if_cfg);
+ hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg);
return 0;
}
-static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- int err = 0;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 1, 0, 0);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 31, 31);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 30, 30);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 0, 0, 0);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 30, 30);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 31, 31);
- break;
- default:
- err = -EINVAL;
- }
- return err;
-}
-
static int hdmi_audio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1698,15 +733,6 @@ static int hdmi_get_clocks(struct platform_device *pdev)
hdmi.sys_clk = clk;
- clk = clk_get(&pdev->dev, "dss_48mhz_clk");
- if (IS_ERR(clk)) {
- DSSERR("can't get hdmi_clk\n");
- clk_put(hdmi.sys_clk);
- return PTR_ERR(clk);
- }
-
- hdmi.hdmi_clk = clk;
-
return 0;
}
@@ -1714,8 +740,6 @@ static void hdmi_put_clocks(void)
{
if (hdmi.sys_clk)
clk_put(hdmi.sys_clk);
- if (hdmi.hdmi_clk)
- clk_put(hdmi.hdmi_clk);
}
/* HDMI HW IP initialisation */
@@ -1736,20 +760,26 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
}
/* Base address taken from platform */
- hdmi.base_wp = ioremap(hdmi_mem->start, resource_size(hdmi_mem));
- if (!hdmi.base_wp) {
+ hdmi.ip_data.base_wp = ioremap(hdmi_mem->start,
+ resource_size(hdmi_mem));
+ if (!hdmi.ip_data.base_wp) {
DSSERR("can't ioremap WP\n");
return -ENOMEM;
}
r = hdmi_get_clocks(pdev);
if (r) {
- iounmap(hdmi.base_wp);
+ iounmap(hdmi.ip_data.base_wp);
return r;
}
pm_runtime_enable(&pdev->dev);
+ hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
+ hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
+ hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
+ hdmi.ip_data.phy_offset = HDMI_PHY;
+
hdmi_panel_init();
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
@@ -1779,14 +809,13 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev)
hdmi_put_clocks();
- iounmap(hdmi.base_wp);
+ iounmap(hdmi.ip_data.base_wp);
return 0;
}
static int hdmi_runtime_suspend(struct device *dev)
{
- clk_disable(hdmi.hdmi_clk);
clk_disable(hdmi.sys_clk);
dispc_runtime_put();
@@ -1809,7 +838,6 @@ static int hdmi_runtime_resume(struct device *dev)
clk_enable(hdmi.sys_clk);
- clk_enable(hdmi.hdmi_clk);
return 0;
diff --git a/drivers/video/omap2/dss/hdmi_omap4_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 7d4f2bd7c506..533d5dc634d2 100644
--- a/drivers/video/omap2/dss/hdmi_omap4_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -1,5 +1,5 @@
/*
- * hdmi_omap4_panel.c
+ * hdmi_panel.c
*
* HDMI library support functions for TI OMAP4 processors.
*
@@ -25,6 +25,7 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <video/omapdss.h>
+#include <linux/slab.h>
#include "dss.h"
@@ -40,13 +41,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
dssdev->panel.config = OMAP_DSS_LCD_TFT |
OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS;
- /*
- * Initialize the timings to 640 * 480
- * This is only for framebuffer update not for TV timing setting
- * Setting TV timing will be done only on enable
- */
- dssdev->panel.timings.x_res = 640;
- dssdev->panel.timings.y_res = 480;
+ dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31};
DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
dssdev->panel.timings.x_res,
@@ -161,12 +156,7 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev,
mutex_lock(&hdmi.hdmi_lock);
dssdev->panel.timings = *timings;
-
- if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
- /* turn the hdmi off and on to get new timings to use */
- omapdss_hdmi_display_disable(dssdev);
- omapdss_hdmi_display_set_timing(dssdev);
- }
+ omapdss_hdmi_display_set_timing(dssdev);
mutex_unlock(&hdmi.hdmi_lock);
}
@@ -181,12 +171,54 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev,
mutex_lock(&hdmi.hdmi_lock);
r = omapdss_hdmi_display_check_timing(dssdev, timings);
- if (r) {
- DSSERR("Timing cannot be applied\n");
- goto err;
+
+ mutex_unlock(&hdmi.hdmi_lock);
+ return r;
+}
+
+static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
+{
+ int r;
+
+ mutex_lock(&hdmi.hdmi_lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ r = omapdss_hdmi_display_enable(dssdev);
+ if (r)
+ goto err;
+ }
+
+ r = omapdss_hdmi_read_edid(buf, len);
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ omapdss_hdmi_display_disable(dssdev);
+err:
+ mutex_unlock(&hdmi.hdmi_lock);
+
+ return r;
+}
+
+static bool hdmi_detect(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ mutex_lock(&hdmi.hdmi_lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ r = omapdss_hdmi_display_enable(dssdev);
+ if (r)
+ goto err;
}
+
+ r = omapdss_hdmi_detect();
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ omapdss_hdmi_display_disable(dssdev);
err:
mutex_unlock(&hdmi.hdmi_lock);
+
return r;
}
@@ -200,6 +232,8 @@ static struct omap_dss_driver hdmi_driver = {
.get_timings = hdmi_get_timings,
.set_timings = hdmi_set_timings,
.check_timings = hdmi_check_timings,
+ .read_edid = hdmi_read_edid,
+ .detect = hdmi_detect,
.driver = {
.name = "hdmi_panel",
.owner = THIS_MODULE,
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 13d72d5c714b..6e63845cc7d7 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -106,7 +106,7 @@ put_device:
static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color);
+ return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.default_color);
}
static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
@@ -116,8 +116,9 @@ static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
u32 color;
int r;
- if (sscanf(buf, "%d", &color) != 1)
- return -EINVAL;
+ r = kstrtouint(buf, 0, &color);
+ if (r)
+ return r;
mgr->get_manager_info(mgr, &info);
@@ -184,7 +185,7 @@ static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key);
+ return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.trans_key);
}
static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
@@ -194,8 +195,9 @@ static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
u32 key_value;
int r;
- if (sscanf(buf, "%d", &key_value) != 1)
- return -EINVAL;
+ r = kstrtouint(buf, 0, &key_value);
+ if (r)
+ return r;
mgr->get_manager_info(mgr, &info);
@@ -222,15 +224,16 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
- int enable;
+ bool enable;
int r;
- if (sscanf(buf, "%d", &enable) != 1)
- return -EINVAL;
+ r = strtobool(buf, &enable);
+ if (r)
+ return r;
mgr->get_manager_info(mgr, &info);
- info.trans_enabled = enable ? true : false;
+ info.trans_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
@@ -246,7 +249,10 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
static ssize_t manager_alpha_blending_enabled_show(
struct omap_overlay_manager *mgr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled);
+ WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ mgr->info.partial_alpha_enabled);
}
static ssize_t manager_alpha_blending_enabled_store(
@@ -254,15 +260,18 @@ static ssize_t manager_alpha_blending_enabled_store(
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
- int enable;
+ bool enable;
int r;
- if (sscanf(buf, "%d", &enable) != 1)
- return -EINVAL;
+ WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
+
+ r = strtobool(buf, &enable);
+ if (r)
+ return r;
mgr->get_manager_info(mgr, &info);
- info.alpha_enabled = enable ? true : false;
+ info.partial_alpha_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
@@ -285,19 +294,16 @@ static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
- int v;
int r;
bool enable;
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
- r = kstrtoint(buf, 0, &v);
+ r = strtobool(buf, &enable);
if (r)
return r;
- enable = !!v;
-
mgr->get_manager_info(mgr, &info);
if (info.cpr_enable == enable)
@@ -586,6 +592,13 @@ static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
return -EINVAL;
}
+ /*
+ * Don't allow currently enabled displays to have the overlay manager
+ * pulled out from underneath them
+ */
+ if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
+ return -EINVAL;
+
mgr->device->manager = NULL;
mgr->device = NULL;
mgr->device_changed = true;
@@ -801,7 +814,7 @@ static int configure_overlay(enum omap_plane plane)
{
struct overlay_cache_data *c;
struct manager_cache_data *mc;
- struct omap_overlay_info *oi;
+ struct omap_overlay_info *oi, new_oi;
struct omap_overlay_manager_info *mi;
u16 outw, outh;
u16 x, y, w, h;
@@ -815,7 +828,7 @@ static int configure_overlay(enum omap_plane plane)
oi = &c->info;
if (!c->enabled) {
- dispc_enable_plane(plane, 0);
+ dispc_ovl_enable(plane, 0);
return 0;
}
@@ -843,7 +856,7 @@ static int configure_overlay(enum omap_plane plane)
/* If the overlay is outside the update region, disable it */
if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
x, y, outw, outh)) {
- dispc_enable_plane(plane, 0);
+ dispc_ovl_enable(plane, 0);
return 0;
}
@@ -921,34 +934,27 @@ static int configure_overlay(enum omap_plane plane)
}
}
- r = dispc_setup_plane(plane,
- paddr,
- oi->screen_width,
- x, y,
- w, h,
- outw, outh,
- oi->color_mode,
- c->ilace,
- oi->rotation_type,
- oi->rotation,
- oi->mirror,
- oi->global_alpha,
- oi->pre_mult_alpha,
- c->channel,
- oi->p_uv_addr);
+ new_oi = *oi;
+
+ /* update new_oi members which could have been possibly updated */
+ new_oi.pos_x = x;
+ new_oi.pos_y = y;
+ new_oi.width = w;
+ new_oi.height = h;
+ new_oi.out_width = outw;
+ new_oi.out_height = outh;
+ new_oi.paddr = paddr;
+ r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel,
+ c->replication, c->fifo_low, c->fifo_high);
if (r) {
/* this shouldn't happen */
- DSSERR("dispc_setup_plane failed for ovl %d\n", plane);
- dispc_enable_plane(plane, 0);
+ DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
+ dispc_ovl_enable(plane, 0);
return r;
}
- dispc_enable_replication(plane, c->replication);
-
- dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);
-
- dispc_enable_plane(plane, 1);
+ dispc_ovl_enable(plane, 1);
return 0;
}
@@ -962,13 +968,13 @@ static void configure_manager(enum omap_channel channel)
/* picking info from the cache */
mi = &dss_cache.manager_cache[channel].info;
- dispc_set_default_color(channel, mi->default_color);
- dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
- dispc_enable_trans_key(channel, mi->trans_enabled);
- dispc_enable_alpha_blending(channel, mi->alpha_enabled);
+ dispc_mgr_set_default_color(channel, mi->default_color);
+ dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
+ dispc_mgr_enable_trans_key(channel, mi->trans_enabled);
+ dispc_mgr_enable_alpha_fixed_zorder(channel, mi->partial_alpha_enabled);
if (dss_has_feature(FEAT_CPR)) {
- dispc_enable_cpr(channel, mi->cpr_enable);
- dispc_set_cpr_coef(channel, &mi->cpr_coefs);
+ dispc_mgr_enable_cpr(channel, mi->cpr_enable);
+ dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs);
}
}
@@ -992,7 +998,7 @@ static int configure_dispc(void)
busy = false;
for (i = 0; i < num_mgrs; i++) {
- mgr_busy[i] = dispc_go_busy(i);
+ mgr_busy[i] = dispc_mgr_go_busy(i);
mgr_go[i] = false;
}
@@ -1053,7 +1059,7 @@ static int configure_dispc(void)
* always be turned off after frame, and new settings will be
* taken in to use at next update */
if (!mc->manual_update)
- dispc_go(i);
+ dispc_mgr_go(i);
}
if (busy)
@@ -1258,7 +1264,7 @@ static void dss_apply_irq_handler(void *data, u32 mask)
u32 irq_mask;
for (i = 0; i < num_mgrs; i++)
- mgr_busy[i] = dispc_go_busy(i);
+ mgr_busy[i] = dispc_mgr_go_busy(i);
spin_lock(&dss_cache.lock);
@@ -1280,7 +1286,7 @@ static void dss_apply_irq_handler(void *data, u32 mask)
/* re-read busy flags */
for (i = 0; i < num_mgrs; i++)
- mgr_busy[i] = dispc_go_busy(i);
+ mgr_busy[i] = dispc_mgr_go_busy(i);
/* keep running as long as there are busy managers, so that
* we can collect overlay-applied information */
@@ -1326,11 +1332,13 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
ovl = omap_dss_get_overlay(i);
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
oc = &dss_cache.overlay_cache[ovl->id];
+ if (ovl->manager_changed) {
+ ovl->manager_changed = false;
+ ovl->info_dirty = true;
+ }
+
if (!overlay_enabled(ovl)) {
if (oc->enabled) {
oc->enabled = false;
@@ -1375,9 +1383,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
list_for_each_entry(mgr, &manager_list, list) {
struct omap_dss_device *dssdev;
- if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
- continue;
-
mc = &dss_cache.manager_cache[mgr->id];
if (mgr->device_changed) {
@@ -1423,9 +1428,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
ovl = omap_dss_get_overlay(i);
- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
- continue;
-
oc = &dss_cache.overlay_cache[ovl->id];
if (!oc->enabled)
@@ -1433,11 +1435,11 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
dssdev = ovl->manager->device;
- size = dispc_get_plane_fifo_size(ovl->id);
+ size = dispc_ovl_get_fifo_size(ovl->id);
if (use_fifomerge)
size *= 3;
- burst_size = dispc_get_burst_size(ovl->id);
+ burst_size = dispc_ovl_get_burst_size(ovl->id);
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
@@ -1484,12 +1486,17 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
static int dss_check_manager(struct omap_overlay_manager *mgr)
{
- /* OMAP supports only graphics source transparency color key and alpha
- * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
-
- if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
- mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
- return -EINVAL;
+ if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
+ /*
+ * OMAP3 supports only graphics source transparency color key
+ * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
+ * Alpha Mode
+ */
+ if (mgr->info.partial_alpha_enabled && mgr->info.trans_enabled
+ && mgr->info.trans_key_type !=
+ OMAP_DSS_COLOR_KEY_GFX_DST)
+ return -EINVAL;
+ }
return 0;
}
@@ -1522,13 +1529,13 @@ static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
static int dss_mgr_enable(struct omap_overlay_manager *mgr)
{
- dispc_enable_channel(mgr->id, 1);
+ dispc_mgr_enable(mgr->id, 1);
return 0;
}
static int dss_mgr_disable(struct omap_overlay_manager *mgr)
{
- dispc_enable_channel(mgr->id, 0);
+ dispc_mgr_enable(mgr->id, 0);
return 0;
}
@@ -1580,7 +1587,7 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->enable = &dss_mgr_enable;
mgr->disable = &dss_mgr_disable;
- mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
+ mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
@@ -1597,42 +1604,6 @@ int dss_init_overlay_managers(struct platform_device *pdev)
}
}
-#ifdef L4_EXAMPLE
- {
- int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr)
- {
- DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name);
-
- return 0;
- }
-
- struct omap_overlay_manager *mgr;
- mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
-
- BUG_ON(mgr == NULL);
-
- mgr->name = "l4";
- mgr->supported_displays =
- OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI;
-
- mgr->set_device = &omap_dss_set_device;
- mgr->unset_device = &omap_dss_unset_device;
- mgr->apply = &omap_dss_mgr_apply_l4;
- mgr->set_manager_info = &omap_dss_mgr_set_info;
- mgr->get_manager_info = &omap_dss_mgr_get_info;
-
- dss_overlay_setup_l4_manager(mgr);
-
- omap_dss_add_overlay_manager(mgr);
-
- r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
- &pdev->dev.kobj, "managerl4");
-
- if (r)
- DSSERR("failed to create sysfs file\n");
- }
-#endif
-
return 0;
}
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index c84380c53c39..ab8e40e48759 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -211,16 +211,17 @@ static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
size_t size)
{
- int r, enable;
+ int r;
+ bool enable;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
- r = kstrtoint(buf, 0, &enable);
+ r = strtobool(buf, &enable);
if (r)
return r;
- info.enabled = !!enable;
+ info.enabled = enable;
r = ovl->set_overlay_info(ovl, &info);
if (r)
@@ -248,7 +249,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
u8 alpha;
struct omap_overlay_info info;
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &alpha);
@@ -257,14 +258,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
ovl->get_overlay_info(ovl, &info);
- /* Video1 plane does not support global alpha
- * to always make it 255 completely opaque
- */
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
- ovl->id == OMAP_DSS_VIDEO1)
- info.global_alpha = 255;
- else
- info.global_alpha = alpha;
+ info.global_alpha = alpha;
r = ovl->set_overlay_info(ovl, &info);
if (r)
@@ -293,20 +287,52 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
u8 alpha;
struct omap_overlay_info info;
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
+ return -ENODEV;
+
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
- /* only GFX and Video2 plane support pre alpha multiplied
- * set zero for Video1 plane
- */
- if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
- ovl->id == OMAP_DSS_VIDEO1)
- info.pre_mult_alpha = 0;
- else
- info.pre_mult_alpha = alpha;
+ info.pre_mult_alpha = alpha;
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.zorder);
+}
+
+static ssize_t overlay_zorder_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ u8 zorder;
+ struct omap_overlay_info info;
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+ return -ENODEV;
+
+ r = kstrtou8(buf, 0, &zorder);
+ if (r)
+ return r;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.zorder = zorder;
r = ovl->set_overlay_info(ovl, &info);
if (r)
@@ -347,6 +373,8 @@ static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
overlay_pre_mult_alpha_show,
overlay_pre_mult_alpha_store);
+static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
+ overlay_zorder_show, overlay_zorder_store);
static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_name.attr,
@@ -358,6 +386,7 @@ static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_enabled.attr,
&overlay_attr_global_alpha.attr,
&overlay_attr_pre_mult_alpha.attr,
+ &overlay_attr_zorder.attr,
NULL
};
@@ -407,6 +436,7 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
struct omap_overlay_info *info;
u16 outw, outh;
u16 dw, dh;
+ int i;
if (!dssdev)
return 0;
@@ -462,6 +492,31 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
return -EINVAL;
}
+ if (ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) {
+ if (info->zorder < 0 || info->zorder > 3) {
+ DSSERR("zorder out of range: %d\n",
+ info->zorder);
+ return -EINVAL;
+ }
+ /*
+ * Check that zorder doesn't match with zorder of any other
+ * overlay which is enabled and is also connected to the same
+ * manager
+ */
+ for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+ struct omap_overlay *tmp_ovl = omap_dss_get_overlay(i);
+
+ if (tmp_ovl->id != ovl->id &&
+ tmp_ovl->manager == ovl->manager &&
+ tmp_ovl->info.enabled == true &&
+ tmp_ovl->info.zorder == info->zorder) {
+ DSSERR("%s and %s have same zorder: %d\n",
+ ovl->name, tmp_ovl->name, info->zorder);
+ return -EINVAL;
+ }
+ }
+ }
+
return 0;
}
@@ -516,6 +571,7 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,
}
ovl->manager = mgr;
+ ovl->manager_changed = true;
/* XXX: When there is an overlay on a DSI manual update display, and
* the overlay is first disabled, then moved to tv, and enabled, we
@@ -529,15 +585,12 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,
* Userspace workaround for this is to update the LCD after disabling
* the overlay, but before moving the overlay to TV.
*/
- dispc_set_channel_out(ovl->id, mgr->id);
return 0;
}
static int omap_dss_unset_manager(struct omap_overlay *ovl)
{
- int r;
-
if (!ovl->manager) {
DSSERR("failed to detach overlay: manager not set\n");
return -EINVAL;
@@ -548,11 +601,8 @@ static int omap_dss_unset_manager(struct omap_overlay *ovl)
return -EINVAL;
}
- r = ovl->wait_for_go(ovl);
- if (r)
- return r;
-
ovl->manager = NULL;
+ ovl->manager_changed = true;
return 0;
}
@@ -618,22 +668,29 @@ void dss_init_overlays(struct platform_device *pdev)
case 0:
ovl->name = "gfx";
ovl->id = OMAP_DSS_GFX;
- ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder = 0;
break;
case 1:
ovl->name = "vid1";
ovl->id = OMAP_DSS_VIDEO1;
- ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
- OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder =
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
break;
case 2:
ovl->name = "vid2";
ovl->id = OMAP_DSS_VIDEO2;
- ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
- OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder =
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
+ break;
+ case 3:
+ ovl->name = "vid3";
+ ovl->id = OMAP_DSS_VIDEO3;
+ ovl->info.global_alpha = 255;
+ ovl->info.zorder =
+ dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
break;
}
@@ -643,6 +700,7 @@ void dss_init_overlays(struct platform_device *pdev)
ovl->get_overlay_info = &dss_ovl_get_overlay_info;
ovl->wait_for_go = &dss_ovl_wait_for_go;
+ ovl->caps = dss_feat_get_overlay_caps(ovl->id);
ovl->supported_modes =
dss_feat_get_supported_color_modes(ovl->id);
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index 39f4c597026a..1bd3703e42ff 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -309,9 +309,9 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
DSSDBG("rfbi_transfer_area %dx%d\n", width, height);
- dispc_set_lcd_size(dssdev->manager->id, width, height);
+ dispc_mgr_set_lcd_size(dssdev->manager->id, width, height);
- dispc_enable_channel(dssdev->manager->id, true);
+ dispc_mgr_enable(dssdev->manager->id, true);
rfbi.framedone_callback = callback;
rfbi.framedone_callback_data = data;
@@ -783,10 +783,8 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
if (*w == 0 || *h == 0)
return -EINVAL;
- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
- dss_setup_partial_planes(dssdev, x, y, w, h, true);
- dispc_set_lcd_size(dssdev->manager->id, *w, *h);
- }
+ dss_setup_partial_planes(dssdev, x, y, w, h, true);
+ dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h);
return 0;
}
@@ -796,22 +794,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(void *), void *data)
{
- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
- rfbi_transfer_area(dssdev, w, h, callback, data);
- } else {
- struct omap_overlay *ovl;
- void __iomem *addr;
- int scr_width;
-
- ovl = dssdev->manager->overlays[0];
- scr_width = ovl->info.screen_width;
- addr = ovl->info.vaddr;
-
- omap_rfbi_write_pixels(addr, scr_width, x, y, w, h);
-
- callback(data);
- }
-
+ rfbi_transfer_area(dssdev, w, h, callback, data);
return 0;
}
EXPORT_SYMBOL(omap_rfbi_update);
@@ -860,6 +843,11 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
{
int r;
+ if (dssdev->manager == NULL) {
+ DSSERR("failed to enable display: no manager\n");
+ return -ENODEV;
+ }
+
r = rfbi_runtime_get();
if (r)
return r;
@@ -877,13 +865,13 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
goto err1;
}
- dispc_set_lcd_display_type(dssdev->manager->id,
+ dispc_mgr_set_lcd_display_type(dssdev->manager->id,
OMAP_DSS_LCD_DISPLAY_TFT);
- dispc_set_parallel_interface_mode(dssdev->manager->id,
- OMAP_DSS_PARALLELMODE_RFBI);
+ dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI);
+ dispc_mgr_enable_stallmode(dssdev->manager->id, true);
- dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
+ dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
rfbi_configure(dssdev->phy.rfbi.channel,
dssdev->ctrl.pixel_size,
@@ -952,10 +940,7 @@ static int omap_rfbihw_probe(struct platform_device *pdev)
msleep(10);
- if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap3630())
- clk = dss_get_ick();
- else
- clk = clk_get(&pdev->dev, "ick");
+ clk = clk_get(&pdev->dev, "ick");
if (IS_ERR(clk)) {
DSSERR("can't get ick\n");
r = PTR_ERR(clk);
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 3a688c871a45..695dc04cabba 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -35,13 +35,13 @@ static struct {
static void sdi_basic_init(struct omap_dss_device *dssdev)
{
- dispc_set_parallel_interface_mode(dssdev->manager->id,
- OMAP_DSS_PARALLELMODE_BYPASS);
+ dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
+ dispc_mgr_enable_stallmode(dssdev->manager->id, false);
- dispc_set_lcd_display_type(dssdev->manager->id,
+ dispc_mgr_set_lcd_display_type(dssdev->manager->id,
OMAP_DSS_LCD_DISPLAY_TFT);
- dispc_set_tft_data_lines(dssdev->manager->id, 24);
+ dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24);
dispc_lcd_enable_signal_polarity(1);
}
@@ -55,6 +55,11 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
unsigned long pck;
int r;
+ if (dssdev->manager == NULL) {
+ DSSERR("failed to enable display: no manager\n");
+ return -ENODEV;
+ }
+
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
@@ -78,7 +83,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
/* 15.5.9.1.2 */
dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
- dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
+ dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
dssdev->panel.acbi, dssdev->panel.acb);
r = dss_calc_clock_div(1, t->pixel_clock * 1000,
@@ -101,13 +106,13 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
}
- dispc_set_lcd_timings(dssdev->manager->id, t);
+ dispc_mgr_set_lcd_timings(dssdev->manager->id, t);
r = dss_set_clock_div(&dss_cinfo);
if (r)
goto err_set_dss_clock_div;
- r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);
+ r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
if (r)
goto err_set_dispc_clock_div;
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
new file mode 100644
index 000000000000..2c3443dabb14
--- /dev/null
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -0,0 +1,138 @@
+/*
+ * ti_hdmi.h
+ *
+ * HDMI driver definition for TI OMAP4, DM81xx, DM38xx Processor.
+ *
+ * Copyright (C) 2010-2011 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 version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TI_HDMI_H
+#define _TI_HDMI_H
+
+struct hdmi_ip_data;
+
+enum hdmi_pll_pwr {
+ HDMI_PLLPWRCMD_ALLOFF = 0,
+ HDMI_PLLPWRCMD_PLLONLY = 1,
+ HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2,
+ HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
+};
+
+enum hdmi_core_hdmi_dvi {
+ HDMI_DVI = 0,
+ HDMI_HDMI = 1
+};
+
+enum hdmi_clk_refsel {
+ HDMI_REFSEL_PCLK = 0,
+ HDMI_REFSEL_REF1 = 1,
+ HDMI_REFSEL_REF2 = 2,
+ HDMI_REFSEL_SYSCLK = 3
+};
+
+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;
+};
+
+/* HDMI timing structure */
+struct hdmi_timings {
+ struct hdmi_video_timings timings;
+ int vsync_pol;
+ int hsync_pol;
+};
+
+struct hdmi_cm {
+ int code;
+ int mode;
+};
+
+struct hdmi_config {
+ struct hdmi_timings timings;
+ u16 interlace;
+ struct hdmi_cm cm;
+};
+
+/* HDMI PLL structure */
+struct hdmi_pll_info {
+ u16 regn;
+ u16 regm;
+ u32 regmf;
+ u16 regm2;
+ u16 regsd;
+ u16 dcofreq;
+ enum hdmi_clk_refsel refsel;
+};
+
+struct ti_hdmi_ip_ops {
+
+ void (*video_configure)(struct hdmi_ip_data *ip_data);
+
+ int (*phy_enable)(struct hdmi_ip_data *ip_data);
+
+ void (*phy_disable)(struct hdmi_ip_data *ip_data);
+
+ int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
+
+ bool (*detect)(struct hdmi_ip_data *ip_data);
+
+ int (*pll_enable)(struct hdmi_ip_data *ip_data);
+
+ void (*pll_disable)(struct hdmi_ip_data *ip_data);
+
+ void (*video_enable)(struct hdmi_ip_data *ip_data, bool start);
+
+ void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s);
+
+ void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
+
+ void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s);
+
+ void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
+
+};
+
+struct hdmi_ip_data {
+ void __iomem *base_wp; /* HDMI wrapper */
+ unsigned long core_sys_offset;
+ unsigned long core_av_offset;
+ unsigned long pll_offset;
+ unsigned long phy_offset;
+ const struct ti_hdmi_ip_ops *ops;
+ struct hdmi_config cfg;
+ struct hdmi_pll_info pll_data;
+};
+int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
+void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
+int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
+bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data);
+void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start);
+int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
+void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data);
+void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
+void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
+void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
+void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
+void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
+
+#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
new file mode 100644
index 000000000000..e1a6ce518af6
--- /dev/null
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -0,0 +1,1239 @@
+/*
+ * ti_hdmi_4xxx_ip.c
+ *
+ * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Yong Zhi
+ * Mythri pk <mythripk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+
+#include "ti_hdmi_4xxx_ip.h"
+#include "dss.h"
+
+static inline void hdmi_write_reg(void __iomem *base_addr,
+ const u16 idx, u32 val)
+{
+ __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr,
+ const u16 idx)
+{
+ return __raw_readl(base_addr + idx);
+}
+
+static inline void __iomem *hdmi_wp_base(struct hdmi_ip_data *ip_data)
+{
+ return ip_data->base_wp;
+}
+
+static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
+{
+ return ip_data->base_wp + ip_data->phy_offset;
+}
+
+static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data)
+{
+ return ip_data->base_wp + ip_data->pll_offset;
+}
+
+static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
+{
+ return ip_data->base_wp + ip_data->core_av_offset;
+}
+
+static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
+{
+ return ip_data->base_wp + ip_data->core_sys_offset;
+}
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx,
+ int b2, int b1, u32 val)
+{
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
+}
+
+static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
+{
+ u32 r;
+ void __iomem *pll_base = hdmi_pll_base(ip_data);
+ struct hdmi_pll_info *fmt = &ip_data->pll_data;
+
+ /* PLL start always use manual mode */
+ REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
+
+ r = hdmi_read_reg(pll_base, PLLCTRL_CFG1);
+ r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
+ r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
+
+ hdmi_write_reg(pll_base, PLLCTRL_CFG1, r);
+
+ r = hdmi_read_reg(pll_base, PLLCTRL_CFG2);
+
+ r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
+ r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
+ r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
+ r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */
+
+ if (fmt->dcofreq) {
+ /* divider programming for frequency beyond 1000Mhz */
+ REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
+ r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
+ } else {
+ r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
+ }
+
+ hdmi_write_reg(pll_base, PLLCTRL_CFG2, r);
+
+ r = hdmi_read_reg(pll_base, PLLCTRL_CFG4);
+ r = FLD_MOD(r, fmt->regm2, 24, 18);
+ r = FLD_MOD(r, fmt->regmf, 17, 0);
+
+ hdmi_write_reg(pll_base, PLLCTRL_CFG4, r);
+
+ /* go now */
+ REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0);
+
+ /* wait for bit change */
+ if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO,
+ 0, 0, 1) != 1) {
+ pr_err("PLL GO bit not set\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Wait till the lock bit is set in PLL status */
+ if (hdmi_wait_for_bit_change(pll_base,
+ PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
+ pr_err("cannot lock PLL\n");
+ pr_err("CFG1 0x%x\n",
+ hdmi_read_reg(pll_base, PLLCTRL_CFG1));
+ pr_err("CFG2 0x%x\n",
+ hdmi_read_reg(pll_base, PLLCTRL_CFG2));
+ pr_err("CFG4 0x%x\n",
+ hdmi_read_reg(pll_base, PLLCTRL_CFG4));
+ return -ETIMEDOUT;
+ }
+
+ pr_debug("PLL locked!\n");
+
+ return 0;
+}
+
+/* PHY_PWR_CMD */
+static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
+{
+ /* Command for power control of HDMI PHY */
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
+
+ /* Status of the power control of HDMI PHY */
+ if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data),
+ HDMI_WP_PWR_CTRL, 5, 4, val) != val) {
+ pr_err("Failed to set PHY power mode to %d\n", val);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/* PLL_PWR_CMD */
+static int hdmi_set_pll_pwr(struct hdmi_ip_data *ip_data, enum hdmi_pll_pwr val)
+{
+ /* Command for power control of HDMI PLL */
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 3, 2);
+
+ /* wait till PHY_PWR_STATUS is set */
+ if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL,
+ 1, 0, val) != val) {
+ pr_err("Failed to set PLL_PWR_STATUS\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int hdmi_pll_reset(struct hdmi_ip_data *ip_data)
+{
+ /* SYSRESET controlled by power FSM */
+ REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
+
+ /* READ 0x0 reset is in progress */
+ if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data),
+ PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) {
+ pr_err("Failed to sysreset PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data)
+{
+ u16 r = 0;
+
+ r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
+ if (r)
+ return r;
+
+ r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
+ if (r)
+ return r;
+
+ r = hdmi_pll_reset(ip_data);
+ if (r)
+ return r;
+
+ r = hdmi_pll_init(ip_data);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
+{
+ hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
+}
+
+int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
+{
+ u16 r = 0;
+ void __iomem *phy_base = hdmi_phy_base(ip_data);
+
+ r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ if (r)
+ return r;
+
+ r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
+ if (r)
+ return r;
+
+ /*
+ * Read address 0 in order to get the SCP reset done completed
+ * Dummy access performed to make sure reset is done
+ */
+ hdmi_read_reg(phy_base, HDMI_TXPHY_TX_CTRL);
+
+ /*
+ * Write to phy address 0 to configure the clock
+ * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
+ */
+ REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
+
+ /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
+ hdmi_write_reg(phy_base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
+
+ /* Setup max LDO voltage */
+ REG_FLD_MOD(phy_base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
+
+ /* Write to phy address 3 to change the polarity control */
+ REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
+
+ return 0;
+}
+
+void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
+{
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
+}
+
+static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
+{
+ void __iomem *base = hdmi_core_sys_base(ip_data);
+
+ /* Turn on CLK for DDC */
+ REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0);
+
+ /* IN_PROG */
+ if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 1) {
+ /* Abort transaction */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xf, 3, 0);
+ /* IN_PROG */
+ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
+ 4, 4, 0) != 0) {
+ DSSERR("Timeout aborting DDC transaction\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* Clk SCL Devices */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xA, 3, 0);
+
+ /* HDMI_CORE_DDC_STATUS_IN_PROG */
+ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
+ 4, 4, 0) != 0) {
+ DSSERR("Timeout starting SCL clock\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Clear FIFO */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x9, 3, 0);
+
+ /* HDMI_CORE_DDC_STATUS_IN_PROG */
+ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
+ 4, 4, 0) != 0) {
+ DSSERR("Timeout clearing DDC fifo\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data,
+ u8 *pedid, int ext)
+{
+ void __iomem *base = hdmi_core_sys_base(ip_data);
+ u32 i;
+ char checksum;
+ u32 offset = 0;
+
+ /* HDMI_CORE_DDC_STATUS_IN_PROG */
+ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
+ 4, 4, 0) != 0) {
+ DSSERR("Timeout waiting DDC to be ready\n");
+ return -ETIMEDOUT;
+ }
+
+ if (ext % 2 != 0)
+ offset = 0x80;
+
+ /* Load Segment Address Register */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0);
+
+ /* Load Slave Address Register */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
+
+ /* Load Offset Address Register */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0);
+
+ /* Load Byte Count */
+ REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
+ REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
+
+ /* Set DDC_CMD */
+ if (ext)
+ REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0);
+ else
+ REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0);
+
+ /* HDMI_CORE_DDC_STATUS_BUS_LOW */
+ if (REG_GET(base, HDMI_CORE_DDC_STATUS, 6, 6) == 1) {
+ pr_err("I2C Bus Low?\n");
+ return -EIO;
+ }
+ /* HDMI_CORE_DDC_STATUS_NO_ACK */
+ if (REG_GET(base, HDMI_CORE_DDC_STATUS, 5, 5) == 1) {
+ pr_err("I2C No Ack\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < 0x80; ++i) {
+ int t;
+
+ /* IN_PROG */
+ if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 0) {
+ DSSERR("operation stopped when reading edid\n");
+ return -EIO;
+ }
+
+ t = 0;
+ /* FIFO_EMPTY */
+ while (REG_GET(base, HDMI_CORE_DDC_STATUS, 2, 2) == 1) {
+ if (t++ > 10000) {
+ DSSERR("timeout reading edid\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0);
+ }
+
+ checksum = 0;
+ for (i = 0; i < 0x80; ++i)
+ checksum += pedid[i];
+
+ if (checksum != 0) {
+ pr_err("E-EDID checksum failed!!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data,
+ u8 *edid, int len)
+{
+ int r, l;
+
+ if (len < 128)
+ return -EINVAL;
+
+ r = hdmi_core_ddc_init(ip_data);
+ if (r)
+ return r;
+
+ r = hdmi_core_ddc_edid(ip_data, edid, 0);
+ if (r)
+ return r;
+
+ l = 128;
+
+ if (len >= 128 * 2 && edid[0x7e] > 0) {
+ r = hdmi_core_ddc_edid(ip_data, edid + 0x80, 1);
+ if (r)
+ return r;
+ l += 128;
+ }
+
+ return l;
+}
+
+bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data)
+{
+ int r;
+
+ void __iomem *base = hdmi_core_sys_base(ip_data);
+
+ /* HPD */
+ r = REG_GET(base, HDMI_CORE_SYS_SYS_STAT, 1, 1);
+
+ return r == 1;
+}
+
+static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
+ struct hdmi_core_infoframe_avi *avi_cfg,
+ struct hdmi_core_packet_enable_repeat *repeat_cfg)
+{
+ pr_debug("Enter hdmi_core_init\n");
+
+ /* video core */
+ video_cfg->ip_bus_width = HDMI_INPUT_8BIT;
+ video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT;
+ video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE;
+ video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE;
+ video_cfg->hdmi_dvi = HDMI_DVI;
+ video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK;
+
+ /* info frame */
+ avi_cfg->db1_format = 0;
+ avi_cfg->db1_active_info = 0;
+ avi_cfg->db1_bar_info_dv = 0;
+ avi_cfg->db1_scan_info = 0;
+ avi_cfg->db2_colorimetry = 0;
+ avi_cfg->db2_aspect_ratio = 0;
+ avi_cfg->db2_active_fmt_ar = 0;
+ avi_cfg->db3_itc = 0;
+ avi_cfg->db3_ec = 0;
+ avi_cfg->db3_q_range = 0;
+ avi_cfg->db3_nup_scaling = 0;
+ avi_cfg->db4_videocode = 0;
+ avi_cfg->db5_pixel_repeat = 0;
+ avi_cfg->db6_7_line_eoftop = 0 ;
+ avi_cfg->db8_9_line_sofbottom = 0;
+ avi_cfg->db10_11_pixel_eofleft = 0;
+ avi_cfg->db12_13_pixel_sofright = 0;
+
+ /* packet enable and repeat */
+ repeat_cfg->audio_pkt = 0;
+ repeat_cfg->audio_pkt_repeat = 0;
+ repeat_cfg->avi_infoframe = 0;
+ repeat_cfg->avi_infoframe_repeat = 0;
+ repeat_cfg->gen_cntrl_pkt = 0;
+ repeat_cfg->gen_cntrl_pkt_repeat = 0;
+ repeat_cfg->generic_pkt = 0;
+ repeat_cfg->generic_pkt_repeat = 0;
+}
+
+static void hdmi_core_powerdown_disable(struct hdmi_ip_data *ip_data)
+{
+ pr_debug("Enter hdmi_core_powerdown_disable\n");
+ REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_CTRL1, 0x0, 0, 0);
+}
+
+static void hdmi_core_swreset_release(struct hdmi_ip_data *ip_data)
+{
+ pr_debug("Enter hdmi_core_swreset_release\n");
+ REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x0, 0, 0);
+}
+
+static void hdmi_core_swreset_assert(struct hdmi_ip_data *ip_data)
+{
+ pr_debug("Enter hdmi_core_swreset_assert\n");
+ REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x1, 0, 0);
+}
+
+/* HDMI_CORE_VIDEO_CONFIG */
+static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_video_config *cfg)
+{
+ u32 r = 0;
+ void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+ /* sys_ctrl1 default configuration not tunable */
+ r = hdmi_read_reg(core_sys_base, HDMI_CORE_CTRL1);
+ r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
+ r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
+ r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2);
+ r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1);
+ hdmi_write_reg(core_sys_base, HDMI_CORE_CTRL1, r);
+
+ REG_FLD_MOD(core_sys_base,
+ HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6);
+
+ /* Vid_Mode */
+ r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE);
+
+ /* dither truncation configuration */
+ if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) {
+ r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6);
+ r = FLD_MOD(r, 1, 5, 5);
+ } else {
+ r = FLD_MOD(r, cfg->op_dither_truc, 7, 6);
+ r = FLD_MOD(r, 0, 5, 5);
+ }
+ hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r);
+
+ /* HDMI_Ctrl */
+ r = hdmi_read_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL);
+ r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6);
+ r = FLD_MOD(r, cfg->pkt_mode, 5, 3);
+ r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0);
+ hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL, r);
+
+ /* TMDS_CTRL */
+ REG_FLD_MOD(core_sys_base,
+ HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5);
+}
+
+static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_infoframe_avi info_avi)
+{
+ u32 val;
+ char sum = 0, checksum = 0;
+ void __iomem *av_base = hdmi_av_base(ip_data);
+
+ sum += 0x82 + 0x002 + 0x00D;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_VERS, 0x002);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_LEN, 0x00D);
+
+ val = (info_avi.db1_format << 5) |
+ (info_avi.db1_active_info << 4) |
+ (info_avi.db1_bar_info_dv << 2) |
+ (info_avi.db1_scan_info);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(0), val);
+ sum += val;
+
+ val = (info_avi.db2_colorimetry << 6) |
+ (info_avi.db2_aspect_ratio << 4) |
+ (info_avi.db2_active_fmt_ar);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(1), val);
+ sum += val;
+
+ val = (info_avi.db3_itc << 7) |
+ (info_avi.db3_ec << 4) |
+ (info_avi.db3_q_range << 2) |
+ (info_avi.db3_nup_scaling);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(2), val);
+ sum += val;
+
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(3),
+ info_avi.db4_videocode);
+ sum += info_avi.db4_videocode;
+
+ val = info_avi.db5_pixel_repeat;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(4), val);
+ sum += val;
+
+ val = info_avi.db6_7_line_eoftop & 0x00FF;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(5), val);
+ sum += val;
+
+ val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(6), val);
+ sum += val;
+
+ val = info_avi.db8_9_line_sofbottom & 0x00FF;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(7), val);
+ sum += val;
+
+ val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(8), val);
+ sum += val;
+
+ val = info_avi.db10_11_pixel_eofleft & 0x00FF;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(9), val);
+ sum += val;
+
+ val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(10), val);
+ sum += val;
+
+ val = info_avi.db12_13_pixel_sofright & 0x00FF;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(11), val);
+ sum += val;
+
+ val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(12), val);
+ sum += val;
+
+ checksum = 0x100 - sum;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_CHSUM, checksum);
+}
+
+static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_packet_enable_repeat repeat_cfg)
+{
+ /* enable/repeat the infoframe */
+ hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL1,
+ (repeat_cfg.audio_pkt << 5) |
+ (repeat_cfg.audio_pkt_repeat << 4) |
+ (repeat_cfg.avi_infoframe << 1) |
+ (repeat_cfg.avi_infoframe_repeat));
+
+ /* enable/repeat the packet */
+ hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL2,
+ (repeat_cfg.gen_cntrl_pkt << 3) |
+ (repeat_cfg.gen_cntrl_pkt_repeat << 2) |
+ (repeat_cfg.generic_pkt << 1) |
+ (repeat_cfg.generic_pkt_repeat));
+}
+
+static void hdmi_wp_init(struct omap_video_timings *timings,
+ struct hdmi_video_format *video_fmt,
+ struct hdmi_video_interface *video_int)
+{
+ pr_debug("Enter hdmi_wp_init\n");
+
+ timings->hbp = 0;
+ timings->hfp = 0;
+ timings->hsw = 0;
+ timings->vbp = 0;
+ timings->vfp = 0;
+ timings->vsw = 0;
+
+ video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
+ video_fmt->y_res = 0;
+ video_fmt->x_res = 0;
+
+ video_int->vsp = 0;
+ video_int->hsp = 0;
+
+ video_int->interlacing = 0;
+ video_int->tm = 0; /* HDMI_TIMING_SLAVE */
+
+}
+
+void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start)
+{
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31);
+}
+
+static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
+ struct omap_video_timings *timings, struct hdmi_config *param)
+{
+ pr_debug("Enter hdmi_wp_video_init_format\n");
+
+ video_fmt->y_res = param->timings.timings.y_res;
+ video_fmt->x_res = param->timings.timings.x_res;
+
+ timings->hbp = param->timings.timings.hbp;
+ timings->hfp = param->timings.timings.hfp;
+ timings->hsw = param->timings.timings.hsw;
+ timings->vbp = param->timings.timings.vbp;
+ timings->vfp = param->timings.timings.vfp;
+ timings->vsw = param->timings.timings.vsw;
+}
+
+static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
+ struct hdmi_video_format *video_fmt)
+{
+ u32 l = 0;
+
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG,
+ video_fmt->packing_mode, 10, 8);
+
+ l |= FLD_VAL(video_fmt->y_res, 31, 16);
+ l |= FLD_VAL(video_fmt->x_res, 15, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l);
+}
+
+static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data,
+ struct hdmi_video_interface *video_int)
+{
+ u32 r;
+ pr_debug("Enter hdmi_wp_video_config_interface\n");
+
+ r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
+ r = FLD_MOD(r, video_int->vsp, 7, 7);
+ r = FLD_MOD(r, video_int->hsp, 6, 6);
+ r = FLD_MOD(r, video_int->interlacing, 3, 3);
+ r = FLD_MOD(r, video_int->tm, 1, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
+}
+
+static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
+ struct omap_video_timings *timings)
+{
+ u32 timing_h = 0;
+ u32 timing_v = 0;
+
+ pr_debug("Enter hdmi_wp_video_config_timing\n");
+
+ timing_h |= FLD_VAL(timings->hbp, 31, 20);
+ timing_h |= FLD_VAL(timings->hfp, 19, 8);
+ timing_h |= FLD_VAL(timings->hsw, 7, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_H, timing_h);
+
+ timing_v |= FLD_VAL(timings->vbp, 31, 20);
+ timing_v |= FLD_VAL(timings->vfp, 19, 8);
+ timing_v |= FLD_VAL(timings->vsw, 7, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_V, timing_v);
+}
+
+void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
+{
+ /* HDMI */
+ struct omap_video_timings video_timing;
+ struct hdmi_video_format video_format;
+ struct hdmi_video_interface video_interface;
+ /* HDMI core */
+ struct hdmi_core_infoframe_avi avi_cfg;
+ struct hdmi_core_video_config v_core_cfg;
+ struct hdmi_core_packet_enable_repeat repeat_cfg;
+ struct hdmi_config *cfg = &ip_data->cfg;
+
+ hdmi_wp_init(&video_timing, &video_format,
+ &video_interface);
+
+ hdmi_core_init(&v_core_cfg,
+ &avi_cfg,
+ &repeat_cfg);
+
+ hdmi_wp_video_init_format(&video_format, &video_timing, cfg);
+
+ hdmi_wp_video_config_timing(ip_data, &video_timing);
+
+ /* video config */
+ video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
+
+ hdmi_wp_video_config_format(ip_data, &video_format);
+
+ video_interface.vsp = cfg->timings.vsync_pol;
+ video_interface.hsp = cfg->timings.hsync_pol;
+ video_interface.interlacing = cfg->interlace;
+ video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */
+
+ hdmi_wp_video_config_interface(ip_data, &video_interface);
+
+ /*
+ * configure core video part
+ * set software reset in the core
+ */
+ hdmi_core_swreset_assert(ip_data);
+
+ /* power down off */
+ hdmi_core_powerdown_disable(ip_data);
+
+ v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;
+ v_core_cfg.hdmi_dvi = cfg->cm.mode;
+
+ hdmi_core_video_config(ip_data, &v_core_cfg);
+
+ /* release software reset in the core */
+ hdmi_core_swreset_release(ip_data);
+
+ /*
+ * configure packet
+ * info frame video see doc CEA861-D page 65
+ */
+ avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
+ avi_cfg.db1_active_info =
+ HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
+ avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
+ avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
+ avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
+ avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
+ avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
+ avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
+ avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
+ avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
+ avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
+ avi_cfg.db4_videocode = cfg->cm.code;
+ avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
+ avi_cfg.db6_7_line_eoftop = 0;
+ avi_cfg.db8_9_line_sofbottom = 0;
+ avi_cfg.db10_11_pixel_eofleft = 0;
+ avi_cfg.db12_13_pixel_sofright = 0;
+
+ hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg);
+
+ /* enable/repeat the infoframe */
+ repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
+ repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON;
+ /* wakeup */
+ repeat_cfg.audio_pkt = HDMI_PACKETENABLE;
+ repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON;
+ hdmi_core_av_packet_config(ip_data, repeat_cfg);
+}
+
+void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(hdmi_wp_base(ip_data), r))
+
+ DUMPREG(HDMI_WP_REVISION);
+ DUMPREG(HDMI_WP_SYSCONFIG);
+ DUMPREG(HDMI_WP_IRQSTATUS_RAW);
+ DUMPREG(HDMI_WP_IRQSTATUS);
+ DUMPREG(HDMI_WP_PWR_CTRL);
+ DUMPREG(HDMI_WP_IRQENABLE_SET);
+ DUMPREG(HDMI_WP_VIDEO_CFG);
+ DUMPREG(HDMI_WP_VIDEO_SIZE);
+ DUMPREG(HDMI_WP_VIDEO_TIMING_H);
+ DUMPREG(HDMI_WP_VIDEO_TIMING_V);
+ DUMPREG(HDMI_WP_WP_CLK);
+ DUMPREG(HDMI_WP_AUDIO_CFG);
+ DUMPREG(HDMI_WP_AUDIO_CFG2);
+ DUMPREG(HDMI_WP_AUDIO_CTRL);
+ DUMPREG(HDMI_WP_AUDIO_DATA);
+}
+
+void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+{
+#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(hdmi_pll_base(ip_data), r))
+
+ DUMPPLL(PLLCTRL_PLL_CONTROL);
+ DUMPPLL(PLLCTRL_PLL_STATUS);
+ DUMPPLL(PLLCTRL_PLL_GO);
+ DUMPPLL(PLLCTRL_CFG1);
+ DUMPPLL(PLLCTRL_CFG2);
+ DUMPPLL(PLLCTRL_CFG3);
+ DUMPPLL(PLLCTRL_CFG4);
+}
+
+void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+{
+ int i;
+
+#define CORE_REG(i, name) name(i)
+#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(hdmi_pll_base(ip_data), r))
+#define DUMPCOREAV(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \
+ (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \
+ hdmi_read_reg(hdmi_pll_base(ip_data), CORE_REG(i, r)))
+
+ DUMPCORE(HDMI_CORE_SYS_VND_IDL);
+ DUMPCORE(HDMI_CORE_SYS_DEV_IDL);
+ DUMPCORE(HDMI_CORE_SYS_DEV_IDH);
+ DUMPCORE(HDMI_CORE_SYS_DEV_REV);
+ DUMPCORE(HDMI_CORE_SYS_SRST);
+ DUMPCORE(HDMI_CORE_CTRL1);
+ DUMPCORE(HDMI_CORE_SYS_SYS_STAT);
+ DUMPCORE(HDMI_CORE_SYS_VID_ACEN);
+ DUMPCORE(HDMI_CORE_SYS_VID_MODE);
+ DUMPCORE(HDMI_CORE_SYS_INTR_STATE);
+ DUMPCORE(HDMI_CORE_SYS_INTR1);
+ DUMPCORE(HDMI_CORE_SYS_INTR2);
+ DUMPCORE(HDMI_CORE_SYS_INTR3);
+ DUMPCORE(HDMI_CORE_SYS_INTR4);
+ DUMPCORE(HDMI_CORE_SYS_UMASK1);
+ DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL);
+ DUMPCORE(HDMI_CORE_SYS_DE_DLY);
+ DUMPCORE(HDMI_CORE_SYS_DE_CTRL);
+ DUMPCORE(HDMI_CORE_SYS_DE_TOP);
+ DUMPCORE(HDMI_CORE_SYS_DE_CNTL);
+ DUMPCORE(HDMI_CORE_SYS_DE_CNTH);
+ DUMPCORE(HDMI_CORE_SYS_DE_LINL);
+ DUMPCORE(HDMI_CORE_SYS_DE_LINH_1);
+
+ DUMPCORE(HDMI_CORE_DDC_CMD);
+ DUMPCORE(HDMI_CORE_DDC_STATUS);
+ DUMPCORE(HDMI_CORE_DDC_ADDR);
+ DUMPCORE(HDMI_CORE_DDC_OFFSET);
+ DUMPCORE(HDMI_CORE_DDC_COUNT1);
+ DUMPCORE(HDMI_CORE_DDC_COUNT2);
+ DUMPCORE(HDMI_CORE_DDC_DATA);
+ DUMPCORE(HDMI_CORE_DDC_SEGM);
+
+ DUMPCORE(HDMI_CORE_AV_HDMI_CTRL);
+ DUMPCORE(HDMI_CORE_AV_DPD);
+ DUMPCORE(HDMI_CORE_AV_PB_CTRL1);
+ DUMPCORE(HDMI_CORE_AV_PB_CTRL2);
+ DUMPCORE(HDMI_CORE_AV_AVI_TYPE);
+ DUMPCORE(HDMI_CORE_AV_AVI_VERS);
+ DUMPCORE(HDMI_CORE_AV_AVI_LEN);
+ DUMPCORE(HDMI_CORE_AV_AVI_CHSUM);
+
+ for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_AVI_DBYTE);
+
+ for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_SPD_DBYTE);
+
+ for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_AUD_DBYTE);
+
+ for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_MPEG_DBYTE);
+
+ for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_GEN_DBYTE);
+
+ for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++)
+ DUMPCOREAV(i, HDMI_CORE_AV_GEN2_DBYTE);
+
+ DUMPCORE(HDMI_CORE_AV_ACR_CTRL);
+ DUMPCORE(HDMI_CORE_AV_FREQ_SVAL);
+ DUMPCORE(HDMI_CORE_AV_N_SVAL1);
+ DUMPCORE(HDMI_CORE_AV_N_SVAL2);
+ DUMPCORE(HDMI_CORE_AV_N_SVAL3);
+ DUMPCORE(HDMI_CORE_AV_CTS_SVAL1);
+ DUMPCORE(HDMI_CORE_AV_CTS_SVAL2);
+ DUMPCORE(HDMI_CORE_AV_CTS_SVAL3);
+ DUMPCORE(HDMI_CORE_AV_CTS_HVAL1);
+ DUMPCORE(HDMI_CORE_AV_CTS_HVAL2);
+ DUMPCORE(HDMI_CORE_AV_CTS_HVAL3);
+ DUMPCORE(HDMI_CORE_AV_AUD_MODE);
+ DUMPCORE(HDMI_CORE_AV_SPDIF_CTRL);
+ DUMPCORE(HDMI_CORE_AV_HW_SPDIF_FS);
+ DUMPCORE(HDMI_CORE_AV_SWAP_I2S);
+ DUMPCORE(HDMI_CORE_AV_SPDIF_ERTH);
+ DUMPCORE(HDMI_CORE_AV_I2S_IN_MAP);
+ DUMPCORE(HDMI_CORE_AV_I2S_IN_CTRL);
+ DUMPCORE(HDMI_CORE_AV_I2S_CHST0);
+ DUMPCORE(HDMI_CORE_AV_I2S_CHST1);
+ DUMPCORE(HDMI_CORE_AV_I2S_CHST2);
+ DUMPCORE(HDMI_CORE_AV_I2S_CHST4);
+ DUMPCORE(HDMI_CORE_AV_I2S_CHST5);
+ DUMPCORE(HDMI_CORE_AV_ASRC);
+ DUMPCORE(HDMI_CORE_AV_I2S_IN_LEN);
+ DUMPCORE(HDMI_CORE_AV_HDMI_CTRL);
+ DUMPCORE(HDMI_CORE_AV_AUDO_TXSTAT);
+ DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_1);
+ DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_2);
+ DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_3);
+ DUMPCORE(HDMI_CORE_AV_TEST_TXCTRL);
+ DUMPCORE(HDMI_CORE_AV_DPD);
+ DUMPCORE(HDMI_CORE_AV_PB_CTRL1);
+ DUMPCORE(HDMI_CORE_AV_PB_CTRL2);
+ DUMPCORE(HDMI_CORE_AV_AVI_TYPE);
+ DUMPCORE(HDMI_CORE_AV_AVI_VERS);
+ DUMPCORE(HDMI_CORE_AV_AVI_LEN);
+ DUMPCORE(HDMI_CORE_AV_AVI_CHSUM);
+ DUMPCORE(HDMI_CORE_AV_SPD_TYPE);
+ DUMPCORE(HDMI_CORE_AV_SPD_VERS);
+ DUMPCORE(HDMI_CORE_AV_SPD_LEN);
+ DUMPCORE(HDMI_CORE_AV_SPD_CHSUM);
+ DUMPCORE(HDMI_CORE_AV_AUDIO_TYPE);
+ DUMPCORE(HDMI_CORE_AV_AUDIO_VERS);
+ DUMPCORE(HDMI_CORE_AV_AUDIO_LEN);
+ DUMPCORE(HDMI_CORE_AV_AUDIO_CHSUM);
+ DUMPCORE(HDMI_CORE_AV_MPEG_TYPE);
+ DUMPCORE(HDMI_CORE_AV_MPEG_VERS);
+ DUMPCORE(HDMI_CORE_AV_MPEG_LEN);
+ DUMPCORE(HDMI_CORE_AV_MPEG_CHSUM);
+ DUMPCORE(HDMI_CORE_AV_CP_BYTE1);
+ DUMPCORE(HDMI_CORE_AV_CEC_ADDR_ID);
+}
+
+void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+{
+#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(hdmi_phy_base(ip_data), r))
+
+ DUMPPHY(HDMI_TXPHY_TX_CTRL);
+ DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
+ DUMPPHY(HDMI_TXPHY_POWER_CTRL);
+ DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
+}
+
+#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
+ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
+void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data,
+ struct hdmi_audio_format *aud_fmt)
+{
+ u32 r;
+
+ DSSDBG("Enter hdmi_wp_audio_config_format\n");
+
+ r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG);
+ r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
+ r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
+ r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
+ r = FLD_MOD(r, aud_fmt->type, 4, 4);
+ r = FLD_MOD(r, aud_fmt->justification, 3, 3);
+ r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
+ r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
+ r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r);
+}
+
+void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
+ struct hdmi_audio_dma *aud_dma)
+{
+ u32 r;
+
+ DSSDBG("Enter hdmi_wp_audio_config_dma\n");
+
+ r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2);
+ r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
+ r = FLD_MOD(r, aud_dma->block_size, 7, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2, r);
+
+ r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL);
+ r = FLD_MOD(r, aud_dma->mode, 9, 9);
+ r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r);
+}
+
+void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_audio_config *cfg)
+{
+ u32 r;
+ void __iomem *av_base = hdmi_av_base(ip_data);
+
+ /* audio clock recovery parameters */
+ r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL);
+ r = FLD_MOD(r, cfg->use_mclk, 2, 2);
+ r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
+ r = FLD_MOD(r, cfg->cts_mode, 0, 0);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r);
+
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0);
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0);
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0);
+
+ if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) {
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0);
+ REG_FLD_MOD(av_base,
+ HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0);
+ REG_FLD_MOD(av_base,
+ HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0);
+ } else {
+ /*
+ * HDMI IP uses this configuration to divide the MCLK to
+ * update CTS value.
+ */
+ REG_FLD_MOD(av_base,
+ HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
+
+ /* Configure clock for audio packets */
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1,
+ cfg->aud_par_busclk, 7, 0);
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2,
+ (cfg->aud_par_busclk >> 8), 7, 0);
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_3,
+ (cfg->aud_par_busclk >> 16), 7, 0);
+ }
+
+ /* Override of SPDIF sample frequency with value in I2S_CHST4 */
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL,
+ cfg->fs_override, 1, 1);
+
+ /* I2S parameters */
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4,
+ cfg->freq_sample, 3, 0);
+
+ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL);
+ r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7);
+ r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6);
+ r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5);
+ r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4);
+ r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3);
+ r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2);
+ r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1);
+ r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r);
+
+ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5);
+ r = FLD_MOD(r, cfg->freq_sample, 7, 4);
+ r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1);
+ r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r);
+
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN,
+ cfg->i2s_cfg.in_length_bits, 3, 0);
+
+ /* Audio channels and mode parameters */
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1);
+ r = hdmi_read_reg(av_base, HDMI_CORE_AV_AUD_MODE);
+ r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4);
+ r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3);
+ r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2);
+ r = FLD_MOD(r, cfg->en_spdif, 1, 1);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r);
+}
+
+void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_infoframe_audio *info_aud)
+{
+ u8 val;
+ u8 sum = 0, checksum = 0;
+ void __iomem *av_base = hdmi_av_base(ip_data);
+
+ /*
+ * Set audio info frame type, version and length as
+ * described in HDMI 1.4a Section 8.2.2 specification.
+ * Checksum calculation is defined in Section 5.3.5.
+ */
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_TYPE, 0x84);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_VERS, 0x01);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a);
+ sum += 0x84 + 0x001 + 0x00a;
+
+ val = (info_aud->db1_coding_type << 4)
+ | (info_aud->db1_channel_count - 1);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val);
+ sum += val;
+
+ val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val);
+ sum += val;
+
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00);
+
+ val = info_aud->db4_channel_alloc;
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val);
+ sum += val;
+
+ val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val);
+ sum += val;
+
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(7), 0x00);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(8), 0x00);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(9), 0x00);
+
+ checksum = 0x100 - sum;
+ hdmi_write_reg(av_base,
+ HDMI_CORE_AV_AUDIO_CHSUM, checksum);
+
+ /*
+ * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing
+ * is available.
+ */
+}
+
+int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
+ u32 sample_freq, u32 *n, u32 *cts)
+{
+ u32 r;
+ u32 deep_color = 0;
+ u32 pclk = ip_data->cfg.timings.timings.pixel_clock;
+
+ if (n == NULL || cts == NULL)
+ return -EINVAL;
+ /*
+ * Obtain current deep color configuration. This needed
+ * to calculate the TMDS clock based on the pixel clock.
+ */
+ r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0);
+ switch (r) {
+ case 1: /* No deep color selected */
+ deep_color = 100;
+ break;
+ case 2: /* 10-bit deep color selected */
+ deep_color = 125;
+ break;
+ case 3: /* 12-bit deep color selected */
+ deep_color = 150;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (sample_freq) {
+ case 32000:
+ if ((deep_color == 125) && ((pclk == 54054)
+ || (pclk == 74250)))
+ *n = 8192;
+ else
+ *n = 4096;
+ break;
+ case 44100:
+ *n = 6272;
+ break;
+ case 48000:
+ if ((deep_color == 125) && ((pclk == 54054)
+ || (pclk == 74250)))
+ *n = 8192;
+ else
+ *n = 6144;
+ break;
+ default:
+ *n = 0;
+ return -EINVAL;
+ }
+
+ /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
+ *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
+
+ return 0;
+}
+
+int hdmi_audio_trigger(struct hdmi_ip_data *ip_data,
+ struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ REG_FLD_MOD(hdmi_av_base(ip_data),
+ HDMI_CORE_AV_AUD_MODE, 1, 0, 0);
+ REG_FLD_MOD(hdmi_wp_base(ip_data),
+ HDMI_WP_AUDIO_CTRL, 1, 31, 31);
+ REG_FLD_MOD(hdmi_wp_base(ip_data),
+ HDMI_WP_AUDIO_CTRL, 1, 30, 30);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ REG_FLD_MOD(hdmi_av_base(ip_data),
+ HDMI_CORE_AV_AUD_MODE, 0, 0, 0);
+ REG_FLD_MOD(hdmi_wp_base(ip_data),
+ HDMI_WP_AUDIO_CTRL, 0, 30, 30);
+ REG_FLD_MOD(hdmi_wp_base(ip_data),
+ HDMI_WP_AUDIO_CTRL, 0, 31, 31);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
+}
+#endif
diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index c885f9cb0659..204095632d27 100644
--- a/drivers/video/omap2/dss/hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -1,7 +1,7 @@
/*
- * hdmi.h
+ * ti_hdmi_4xxx_ip.h
*
- * HDMI driver definition for TI OMAP4 processors.
+ * HDMI header definition for DM81xx, DM38xx, TI OMAP4 etc processors.
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
*
@@ -18,202 +18,177 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _OMAP4_DSS_HDMI_H_
-#define _OMAP4_DSS_HDMI_H_
+#ifndef _HDMI_TI_4xxx_H_
+#define _HDMI_TI_4xxx_H_
#include <linux/string.h>
#include <video/omapdss.h>
-
-#define HDMI_WP 0x0
-#define HDMI_CORE_SYS 0x400
-#define HDMI_CORE_AV 0x900
-#define HDMI_PLLCTRL 0x200
-#define HDMI_PHY 0x300
-
-struct hdmi_reg { u16 idx; };
-
-#define HDMI_REG(idx) ((const struct hdmi_reg) { idx })
+#include "ti_hdmi.h"
+#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
+ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#endif
/* HDMI Wrapper */
-#define HDMI_WP_REG(idx) HDMI_REG(HDMI_WP + idx)
-
-#define HDMI_WP_REVISION HDMI_WP_REG(0x0)
-#define HDMI_WP_SYSCONFIG HDMI_WP_REG(0x10)
-#define HDMI_WP_IRQSTATUS_RAW HDMI_WP_REG(0x24)
-#define HDMI_WP_IRQSTATUS HDMI_WP_REG(0x28)
-#define HDMI_WP_PWR_CTRL HDMI_WP_REG(0x40)
-#define HDMI_WP_IRQENABLE_SET HDMI_WP_REG(0x2C)
-#define HDMI_WP_VIDEO_CFG HDMI_WP_REG(0x50)
-#define HDMI_WP_VIDEO_SIZE HDMI_WP_REG(0x60)
-#define HDMI_WP_VIDEO_TIMING_H HDMI_WP_REG(0x68)
-#define HDMI_WP_VIDEO_TIMING_V HDMI_WP_REG(0x6C)
-#define HDMI_WP_WP_CLK HDMI_WP_REG(0x70)
-#define HDMI_WP_AUDIO_CFG HDMI_WP_REG(0x80)
-#define HDMI_WP_AUDIO_CFG2 HDMI_WP_REG(0x84)
-#define HDMI_WP_AUDIO_CTRL HDMI_WP_REG(0x88)
-#define HDMI_WP_AUDIO_DATA HDMI_WP_REG(0x8C)
+
+#define HDMI_WP_REVISION 0x0
+#define HDMI_WP_SYSCONFIG 0x10
+#define HDMI_WP_IRQSTATUS_RAW 0x24
+#define HDMI_WP_IRQSTATUS 0x28
+#define HDMI_WP_PWR_CTRL 0x40
+#define HDMI_WP_IRQENABLE_SET 0x2C
+#define HDMI_WP_VIDEO_CFG 0x50
+#define HDMI_WP_VIDEO_SIZE 0x60
+#define HDMI_WP_VIDEO_TIMING_H 0x68
+#define HDMI_WP_VIDEO_TIMING_V 0x6C
+#define HDMI_WP_WP_CLK 0x70
+#define HDMI_WP_AUDIO_CFG 0x80
+#define HDMI_WP_AUDIO_CFG2 0x84
+#define HDMI_WP_AUDIO_CTRL 0x88
+#define HDMI_WP_AUDIO_DATA 0x8C
/* HDMI IP Core System */
-#define HDMI_CORE_SYS_REG(idx) HDMI_REG(HDMI_CORE_SYS + idx)
-
-#define HDMI_CORE_SYS_VND_IDL HDMI_CORE_SYS_REG(0x0)
-#define HDMI_CORE_SYS_DEV_IDL HDMI_CORE_SYS_REG(0x8)
-#define HDMI_CORE_SYS_DEV_IDH HDMI_CORE_SYS_REG(0xC)
-#define HDMI_CORE_SYS_DEV_REV HDMI_CORE_SYS_REG(0x10)
-#define HDMI_CORE_SYS_SRST HDMI_CORE_SYS_REG(0x14)
-#define HDMI_CORE_CTRL1 HDMI_CORE_SYS_REG(0x20)
-#define HDMI_CORE_SYS_SYS_STAT HDMI_CORE_SYS_REG(0x24)
-#define HDMI_CORE_SYS_VID_ACEN HDMI_CORE_SYS_REG(0x124)
-#define HDMI_CORE_SYS_VID_MODE HDMI_CORE_SYS_REG(0x128)
-#define HDMI_CORE_SYS_INTR_STATE HDMI_CORE_SYS_REG(0x1C0)
-#define HDMI_CORE_SYS_INTR1 HDMI_CORE_SYS_REG(0x1C4)
-#define HDMI_CORE_SYS_INTR2 HDMI_CORE_SYS_REG(0x1C8)
-#define HDMI_CORE_SYS_INTR3 HDMI_CORE_SYS_REG(0x1CC)
-#define HDMI_CORE_SYS_INTR4 HDMI_CORE_SYS_REG(0x1D0)
-#define HDMI_CORE_SYS_UMASK1 HDMI_CORE_SYS_REG(0x1D4)
-#define HDMI_CORE_SYS_TMDS_CTRL HDMI_CORE_SYS_REG(0x208)
-#define HDMI_CORE_SYS_DE_DLY HDMI_CORE_SYS_REG(0xC8)
-#define HDMI_CORE_SYS_DE_CTRL HDMI_CORE_SYS_REG(0xCC)
-#define HDMI_CORE_SYS_DE_TOP HDMI_CORE_SYS_REG(0xD0)
-#define HDMI_CORE_SYS_DE_CNTL HDMI_CORE_SYS_REG(0xD8)
-#define HDMI_CORE_SYS_DE_CNTH HDMI_CORE_SYS_REG(0xDC)
-#define HDMI_CORE_SYS_DE_LINL HDMI_CORE_SYS_REG(0xE0)
-#define HDMI_CORE_SYS_DE_LINH_1 HDMI_CORE_SYS_REG(0xE4)
+
+#define HDMI_CORE_SYS_VND_IDL 0x0
+#define HDMI_CORE_SYS_DEV_IDL 0x8
+#define HDMI_CORE_SYS_DEV_IDH 0xC
+#define HDMI_CORE_SYS_DEV_REV 0x10
+#define HDMI_CORE_SYS_SRST 0x14
+#define HDMI_CORE_CTRL1 0x20
+#define HDMI_CORE_SYS_SYS_STAT 0x24
+#define HDMI_CORE_SYS_VID_ACEN 0x124
+#define HDMI_CORE_SYS_VID_MODE 0x128
+#define HDMI_CORE_SYS_INTR_STATE 0x1C0
+#define HDMI_CORE_SYS_INTR1 0x1C4
+#define HDMI_CORE_SYS_INTR2 0x1C8
+#define HDMI_CORE_SYS_INTR3 0x1CC
+#define HDMI_CORE_SYS_INTR4 0x1D0
+#define HDMI_CORE_SYS_UMASK1 0x1D4
+#define HDMI_CORE_SYS_TMDS_CTRL 0x208
+#define HDMI_CORE_SYS_DE_DLY 0xC8
+#define HDMI_CORE_SYS_DE_CTRL 0xCC
+#define HDMI_CORE_SYS_DE_TOP 0xD0
+#define HDMI_CORE_SYS_DE_CNTL 0xD8
+#define HDMI_CORE_SYS_DE_CNTH 0xDC
+#define HDMI_CORE_SYS_DE_LINL 0xE0
+#define HDMI_CORE_SYS_DE_LINH_1 0xE4
#define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1
#define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1
#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1
#define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1
/* HDMI DDC E-DID */
-#define HDMI_CORE_DDC_CMD HDMI_CORE_SYS_REG(0x3CC)
-#define HDMI_CORE_DDC_STATUS HDMI_CORE_SYS_REG(0x3C8)
-#define HDMI_CORE_DDC_ADDR HDMI_CORE_SYS_REG(0x3B4)
-#define HDMI_CORE_DDC_OFFSET HDMI_CORE_SYS_REG(0x3BC)
-#define HDMI_CORE_DDC_COUNT1 HDMI_CORE_SYS_REG(0x3C0)
-#define HDMI_CORE_DDC_COUNT2 HDMI_CORE_SYS_REG(0x3C4)
-#define HDMI_CORE_DDC_DATA HDMI_CORE_SYS_REG(0x3D0)
-#define HDMI_CORE_DDC_SEGM HDMI_CORE_SYS_REG(0x3B8)
+#define HDMI_CORE_DDC_CMD 0x3CC
+#define HDMI_CORE_DDC_STATUS 0x3C8
+#define HDMI_CORE_DDC_ADDR 0x3B4
+#define HDMI_CORE_DDC_OFFSET 0x3BC
+#define HDMI_CORE_DDC_COUNT1 0x3C0
+#define HDMI_CORE_DDC_COUNT2 0x3C4
+#define HDMI_CORE_DDC_DATA 0x3D0
+#define HDMI_CORE_DDC_SEGM 0x3B8
/* HDMI IP Core Audio Video */
-#define HDMI_CORE_AV_REG(idx) HDMI_REG(HDMI_CORE_AV + idx)
-
-#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC)
-#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4)
-#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8)
-#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC)
-#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100)
-#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104)
-#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108)
-#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C)
-#define HDMI_CORE_AV_AVI_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x110)
-#define HDMI_CORE_AV_AVI_DBYTE_NELEMS HDMI_CORE_AV_REG(15)
-#define HDMI_CORE_AV_SPD_DBYTE HDMI_CORE_AV_REG(0x190)
-#define HDMI_CORE_AV_SPD_DBYTE_NELEMS HDMI_CORE_AV_REG(27)
-#define HDMI_CORE_AV_AUD_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x210)
-#define HDMI_CORE_AV_AUD_DBYTE_NELEMS HDMI_CORE_AV_REG(10)
-#define HDMI_CORE_AV_MPEG_DBYTE HDMI_CORE_AV_REG(0x290)
-#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS HDMI_CORE_AV_REG(27)
-#define HDMI_CORE_AV_GEN_DBYTE HDMI_CORE_AV_REG(0x300)
-#define HDMI_CORE_AV_GEN_DBYTE_NELEMS HDMI_CORE_AV_REG(31)
-#define HDMI_CORE_AV_GEN2_DBYTE HDMI_CORE_AV_REG(0x380)
-#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS HDMI_CORE_AV_REG(31)
-#define HDMI_CORE_AV_ACR_CTRL HDMI_CORE_AV_REG(0x4)
-#define HDMI_CORE_AV_FREQ_SVAL HDMI_CORE_AV_REG(0x8)
-#define HDMI_CORE_AV_N_SVAL1 HDMI_CORE_AV_REG(0xC)
-#define HDMI_CORE_AV_N_SVAL2 HDMI_CORE_AV_REG(0x10)
-#define HDMI_CORE_AV_N_SVAL3 HDMI_CORE_AV_REG(0x14)
-#define HDMI_CORE_AV_CTS_SVAL1 HDMI_CORE_AV_REG(0x18)
-#define HDMI_CORE_AV_CTS_SVAL2 HDMI_CORE_AV_REG(0x1C)
-#define HDMI_CORE_AV_CTS_SVAL3 HDMI_CORE_AV_REG(0x20)
-#define HDMI_CORE_AV_CTS_HVAL1 HDMI_CORE_AV_REG(0x24)
-#define HDMI_CORE_AV_CTS_HVAL2 HDMI_CORE_AV_REG(0x28)
-#define HDMI_CORE_AV_CTS_HVAL3 HDMI_CORE_AV_REG(0x2C)
-#define HDMI_CORE_AV_AUD_MODE HDMI_CORE_AV_REG(0x50)
-#define HDMI_CORE_AV_SPDIF_CTRL HDMI_CORE_AV_REG(0x54)
-#define HDMI_CORE_AV_HW_SPDIF_FS HDMI_CORE_AV_REG(0x60)
-#define HDMI_CORE_AV_SWAP_I2S HDMI_CORE_AV_REG(0x64)
-#define HDMI_CORE_AV_SPDIF_ERTH HDMI_CORE_AV_REG(0x6C)
-#define HDMI_CORE_AV_I2S_IN_MAP HDMI_CORE_AV_REG(0x70)
-#define HDMI_CORE_AV_I2S_IN_CTRL HDMI_CORE_AV_REG(0x74)
-#define HDMI_CORE_AV_I2S_CHST0 HDMI_CORE_AV_REG(0x78)
-#define HDMI_CORE_AV_I2S_CHST1 HDMI_CORE_AV_REG(0x7C)
-#define HDMI_CORE_AV_I2S_CHST2 HDMI_CORE_AV_REG(0x80)
-#define HDMI_CORE_AV_I2S_CHST4 HDMI_CORE_AV_REG(0x84)
-#define HDMI_CORE_AV_I2S_CHST5 HDMI_CORE_AV_REG(0x88)
-#define HDMI_CORE_AV_ASRC HDMI_CORE_AV_REG(0x8C)
-#define HDMI_CORE_AV_I2S_IN_LEN HDMI_CORE_AV_REG(0x90)
-#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC)
-#define HDMI_CORE_AV_AUDO_TXSTAT HDMI_CORE_AV_REG(0xC0)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 HDMI_CORE_AV_REG(0xCC)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 HDMI_CORE_AV_REG(0xD0)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 HDMI_CORE_AV_REG(0xD4)
-#define HDMI_CORE_AV_TEST_TXCTRL HDMI_CORE_AV_REG(0xF0)
-#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4)
-#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8)
-#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC)
-#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100)
-#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104)
-#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108)
-#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C)
-#define HDMI_CORE_AV_SPD_TYPE HDMI_CORE_AV_REG(0x180)
-#define HDMI_CORE_AV_SPD_VERS HDMI_CORE_AV_REG(0x184)
-#define HDMI_CORE_AV_SPD_LEN HDMI_CORE_AV_REG(0x188)
-#define HDMI_CORE_AV_SPD_CHSUM HDMI_CORE_AV_REG(0x18C)
-#define HDMI_CORE_AV_AUDIO_TYPE HDMI_CORE_AV_REG(0x200)
-#define HDMI_CORE_AV_AUDIO_VERS HDMI_CORE_AV_REG(0x204)
-#define HDMI_CORE_AV_AUDIO_LEN HDMI_CORE_AV_REG(0x208)
-#define HDMI_CORE_AV_AUDIO_CHSUM HDMI_CORE_AV_REG(0x20C)
-#define HDMI_CORE_AV_MPEG_TYPE HDMI_CORE_AV_REG(0x280)
-#define HDMI_CORE_AV_MPEG_VERS HDMI_CORE_AV_REG(0x284)
-#define HDMI_CORE_AV_MPEG_LEN HDMI_CORE_AV_REG(0x288)
-#define HDMI_CORE_AV_MPEG_CHSUM HDMI_CORE_AV_REG(0x28C)
-#define HDMI_CORE_AV_CP_BYTE1 HDMI_CORE_AV_REG(0x37C)
-#define HDMI_CORE_AV_CEC_ADDR_ID HDMI_CORE_AV_REG(0x3FC)
+
+#define HDMI_CORE_AV_HDMI_CTRL 0xBC
+#define HDMI_CORE_AV_DPD 0xF4
+#define HDMI_CORE_AV_PB_CTRL1 0xF8
+#define HDMI_CORE_AV_PB_CTRL2 0xFC
+#define HDMI_CORE_AV_AVI_TYPE 0x100
+#define HDMI_CORE_AV_AVI_VERS 0x104
+#define HDMI_CORE_AV_AVI_LEN 0x108
+#define HDMI_CORE_AV_AVI_CHSUM 0x10C
+#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110)
+#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15
+#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190)
+#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27
+#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210)
+#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10
+#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290)
+#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27
+#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300)
+#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31
+#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380)
+#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31
+#define HDMI_CORE_AV_ACR_CTRL 0x4
+#define HDMI_CORE_AV_FREQ_SVAL 0x8
+#define HDMI_CORE_AV_N_SVAL1 0xC
+#define HDMI_CORE_AV_N_SVAL2 0x10
+#define HDMI_CORE_AV_N_SVAL3 0x14
+#define HDMI_CORE_AV_CTS_SVAL1 0x18
+#define HDMI_CORE_AV_CTS_SVAL2 0x1C
+#define HDMI_CORE_AV_CTS_SVAL3 0x20
+#define HDMI_CORE_AV_CTS_HVAL1 0x24
+#define HDMI_CORE_AV_CTS_HVAL2 0x28
+#define HDMI_CORE_AV_CTS_HVAL3 0x2C
+#define HDMI_CORE_AV_AUD_MODE 0x50
+#define HDMI_CORE_AV_SPDIF_CTRL 0x54
+#define HDMI_CORE_AV_HW_SPDIF_FS 0x60
+#define HDMI_CORE_AV_SWAP_I2S 0x64
+#define HDMI_CORE_AV_SPDIF_ERTH 0x6C
+#define HDMI_CORE_AV_I2S_IN_MAP 0x70
+#define HDMI_CORE_AV_I2S_IN_CTRL 0x74
+#define HDMI_CORE_AV_I2S_CHST0 0x78
+#define HDMI_CORE_AV_I2S_CHST1 0x7C
+#define HDMI_CORE_AV_I2S_CHST2 0x80
+#define HDMI_CORE_AV_I2S_CHST4 0x84
+#define HDMI_CORE_AV_I2S_CHST5 0x88
+#define HDMI_CORE_AV_ASRC 0x8C
+#define HDMI_CORE_AV_I2S_IN_LEN 0x90
+#define HDMI_CORE_AV_HDMI_CTRL 0xBC
+#define HDMI_CORE_AV_AUDO_TXSTAT 0xC0
+#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 0xCC
+#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 0xD0
+#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 0xD4
+#define HDMI_CORE_AV_TEST_TXCTRL 0xF0
+#define HDMI_CORE_AV_DPD 0xF4
+#define HDMI_CORE_AV_PB_CTRL1 0xF8
+#define HDMI_CORE_AV_PB_CTRL2 0xFC
+#define HDMI_CORE_AV_AVI_TYPE 0x100
+#define HDMI_CORE_AV_AVI_VERS 0x104
+#define HDMI_CORE_AV_AVI_LEN 0x108
+#define HDMI_CORE_AV_AVI_CHSUM 0x10C
+#define HDMI_CORE_AV_SPD_TYPE 0x180
+#define HDMI_CORE_AV_SPD_VERS 0x184
+#define HDMI_CORE_AV_SPD_LEN 0x188
+#define HDMI_CORE_AV_SPD_CHSUM 0x18C
+#define HDMI_CORE_AV_AUDIO_TYPE 0x200
+#define HDMI_CORE_AV_AUDIO_VERS 0x204
+#define HDMI_CORE_AV_AUDIO_LEN 0x208
+#define HDMI_CORE_AV_AUDIO_CHSUM 0x20C
+#define HDMI_CORE_AV_MPEG_TYPE 0x280
+#define HDMI_CORE_AV_MPEG_VERS 0x284
+#define HDMI_CORE_AV_MPEG_LEN 0x288
+#define HDMI_CORE_AV_MPEG_CHSUM 0x28C
+#define HDMI_CORE_AV_CP_BYTE1 0x37C
+#define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC
#define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4
/* PLL */
-#define HDMI_PLL_REG(idx) HDMI_REG(HDMI_PLLCTRL + idx)
-#define PLLCTRL_PLL_CONTROL HDMI_PLL_REG(0x0)
-#define PLLCTRL_PLL_STATUS HDMI_PLL_REG(0x4)
-#define PLLCTRL_PLL_GO HDMI_PLL_REG(0x8)
-#define PLLCTRL_CFG1 HDMI_PLL_REG(0xC)
-#define PLLCTRL_CFG2 HDMI_PLL_REG(0x10)
-#define PLLCTRL_CFG3 HDMI_PLL_REG(0x14)
-#define PLLCTRL_CFG4 HDMI_PLL_REG(0x20)
+#define PLLCTRL_PLL_CONTROL 0x0
+#define PLLCTRL_PLL_STATUS 0x4
+#define PLLCTRL_PLL_GO 0x8
+#define PLLCTRL_CFG1 0xC
+#define PLLCTRL_CFG2 0x10
+#define PLLCTRL_CFG3 0x14
+#define PLLCTRL_CFG4 0x20
/* HDMI PHY */
-#define HDMI_PHY_REG(idx) HDMI_REG(HDMI_PHY + idx)
-
-#define HDMI_TXPHY_TX_CTRL HDMI_PHY_REG(0x0)
-#define HDMI_TXPHY_DIGITAL_CTRL HDMI_PHY_REG(0x4)
-#define HDMI_TXPHY_POWER_CTRL HDMI_PHY_REG(0x8)
-#define HDMI_TXPHY_PAD_CFG_CTRL HDMI_PHY_REG(0xC)
-
-/* HDMI EDID Length */
-#define HDMI_EDID_MAX_LENGTH 256
-#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
-#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
-#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
-#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
-#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
-#define OMAP_HDMI_TIMINGS_NB 34
+#define HDMI_TXPHY_TX_CTRL 0x0
+#define HDMI_TXPHY_DIGITAL_CTRL 0x4
+#define HDMI_TXPHY_POWER_CTRL 0x8
+#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
-#define REG_FLD_MOD(idx, val, start, end) \
- hdmi_write_reg(idx, FLD_MOD(hdmi_read_reg(idx), val, start, end))
-#define REG_GET(idx, start, end) \
- FLD_GET(hdmi_read_reg(idx), start, end)
-
-/* HDMI timing structure */
-struct hdmi_timings {
- struct omap_video_timings timings;
- int vsync_pol;
- int hsync_pol;
-};
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
enum hdmi_phy_pwr {
HDMI_PHYPWRCMD_OFF = 0,
@@ -221,20 +196,6 @@ enum hdmi_phy_pwr {
HDMI_PHYPWRCMD_TXON = 2
};
-enum hdmi_pll_pwr {
- HDMI_PLLPWRCMD_ALLOFF = 0,
- HDMI_PLLPWRCMD_PLLONLY = 1,
- HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2,
- HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
-};
-
-enum hdmi_clk_refsel {
- HDMI_REFSEL_PCLK = 0,
- HDMI_REFSEL_REF1 = 1,
- HDMI_REFSEL_REF2 = 2,
- HDMI_REFSEL_SYSCLK = 3
-};
-
enum hdmi_core_inputbus_width {
HDMI_INPUT_8BIT = 0,
HDMI_INPUT_10BIT = 1,
@@ -263,11 +224,6 @@ enum hdmi_core_packet_mode {
HDMI_PACKETMODE48BITPERPIXEL = 7
};
-enum hdmi_core_hdmi_dvi {
- HDMI_DVI = 0,
- HDMI_HDMI = 1
-};
-
enum hdmi_core_tclkselclkmult {
HDMI_FPLL05IDCK = 0,
HDMI_FPLL10IDCK = 1,
@@ -495,40 +451,40 @@ struct hdmi_core_video_config {
* details about infoframe databytes
*/
struct hdmi_core_infoframe_avi {
+ /* Y0, Y1 rgb,yCbCr */
u8 db1_format;
- /* Y0, Y1 rgb,yCbCr */
+ /* A0 Active information Present */
u8 db1_active_info;
- /* A0 Active information Present */
+ /* B0, B1 Bar info data valid */
u8 db1_bar_info_dv;
- /* B0, B1 Bar info data valid */
+ /* S0, S1 scan information */
u8 db1_scan_info;
- /* S0, S1 scan information */
+ /* C0, C1 colorimetry */
u8 db2_colorimetry;
- /* C0, C1 colorimetry */
+ /* M0, M1 Aspect ratio (4:3, 16:9) */
u8 db2_aspect_ratio;
- /* M0, M1 Aspect ratio (4:3, 16:9) */
+ /* R0...R3 Active format aspect ratio */
u8 db2_active_fmt_ar;
- /* R0...R3 Active format aspect ratio */
+ /* ITC IT content. */
u8 db3_itc;
- /* ITC IT content. */
+ /* EC0, EC1, EC2 Extended colorimetry */
u8 db3_ec;
- /* EC0, EC1, EC2 Extended colorimetry */
+ /* Q1, Q0 Quantization range */
u8 db3_q_range;
- /* Q1, Q0 Quantization range */
+ /* SC1, SC0 Non-uniform picture scaling */
u8 db3_nup_scaling;
- /* SC1, SC0 Non-uniform picture scaling */
+ /* VIC0..6 Video format identification */
u8 db4_videocode;
- /* VIC0..6 Video format identification */
+ /* PR0..PR3 Pixel repetition factor */
u8 db5_pixel_repeat;
- /* PR0..PR3 Pixel repetition factor */
+ /* Line number end of top bar */
u16 db6_7_line_eoftop;
- /* Line number end of top bar */
+ /* Line number start of bottom bar */
u16 db8_9_line_sofbottom;
- /* Line number start of bottom bar */
+ /* Pixel number end of left bar */
u16 db10_11_pixel_eofleft;
- /* Pixel number end of left bar */
+ /* Pixel number start of right bar */
u16 db12_13_pixel_sofright;
- /* Pixel number start of right bar */
};
/*
* Refer to section 8.2 in HDMI 1.3 specification for
@@ -568,17 +524,6 @@ struct hdmi_video_interface {
int tm; /* Timing mode */
};
-struct hdmi_cm {
- int code;
- int mode;
-};
-
-struct hdmi_config {
- struct hdmi_timings timings;
- u16 interlace;
- struct hdmi_cm cm;
-};
-
struct hdmi_audio_format {
enum hdmi_stereo_channels stereo_channels;
u8 active_chnnls_msk;
@@ -628,4 +573,21 @@ struct hdmi_core_audio_config {
bool en_parallel_aud_input;
bool en_spdif;
};
+
+#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
+ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
+int hdmi_audio_trigger(struct hdmi_ip_data *ip_data,
+ struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai);
+int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
+ u32 sample_freq, u32 *n, u32 *cts);
+void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_infoframe_audio *info_aud);
+void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
+ struct hdmi_core_audio_config *cfg);
+void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
+ struct hdmi_audio_dma *aud_dma);
+void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data,
+ struct hdmi_audio_format *aud_fmt);
+#endif
#endif
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 173c66430dad..7533458ba4d2 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -295,7 +295,6 @@ static struct {
u32 wss_data;
struct regulator *vdda_dac_reg;
- struct clk *tv_clk;
struct clk *tv_dac_clk;
} venc;
@@ -464,9 +463,11 @@ static void venc_power_off(struct omap_dss_device *dssdev)
regulator_disable(venc.vdda_dac_reg);
}
-
-
-
+unsigned long venc_get_pixel_clock(void)
+{
+ /* VENC Pixel Clock in Mhz */
+ return 13500000;
+}
/* driver */
static int venc_panel_probe(struct omap_dss_device *dssdev)
@@ -732,22 +733,10 @@ static int venc_get_clocks(struct platform_device *pdev)
{
struct clk *clk;
- clk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(clk)) {
- DSSERR("can't get fck\n");
- return PTR_ERR(clk);
- }
-
- venc.tv_clk = clk;
-
if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) {
- if (cpu_is_omap34xx() || cpu_is_omap3630())
- clk = clk_get(&pdev->dev, "dss_96m_fck");
- else
- clk = clk_get(&pdev->dev, "tv_dac_clk");
+ clk = clk_get(&pdev->dev, "tv_dac_clk");
if (IS_ERR(clk)) {
DSSERR("can't get tv_dac_clk\n");
- clk_put(venc.tv_clk);
return PTR_ERR(clk);
}
} else {
@@ -761,8 +750,6 @@ static int venc_get_clocks(struct platform_device *pdev)
static void venc_put_clocks(void)
{
- if (venc.tv_clk)
- clk_put(venc.tv_clk);
if (venc.tv_dac_clk)
clk_put(venc.tv_dac_clk);
}
@@ -838,7 +825,6 @@ static int venc_runtime_suspend(struct device *dev)
{
if (venc.tv_dac_clk)
clk_disable(venc.tv_dac_clk);
- clk_disable(venc.tv_clk);
dispc_runtime_put();
dss_runtime_put();
@@ -858,7 +844,6 @@ static int venc_runtime_resume(struct device *dev)
if (r < 0)
goto err_get_dispc;
- clk_enable(venc.tv_clk);
if (venc.tv_dac_clk)
clk_enable(venc.tv_dac_clk);
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
index aa33386c81ff..83d3fe7ec9ae 100644
--- a/drivers/video/omap2/omapfb/Kconfig
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -1,5 +1,5 @@
menuconfig FB_OMAP2
- tristate "OMAP2+ frame buffer support (EXPERIMENTAL)"
+ tristate "OMAP2+ frame buffer support"
depends on FB && OMAP2_DSS
select OMAP2_VRAM
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 602b71a92d3c..70aa47de7146 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -808,19 +808,15 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var,
static void omapfb_calc_addr(const struct omapfb_info *ofbi,
const struct fb_var_screeninfo *var,
const struct fb_fix_screeninfo *fix,
- int rotation, u32 *paddr, void __iomem **vaddr)
+ int rotation, u32 *paddr)
{
u32 data_start_p;
- void __iomem *data_start_v;
int offset;
- if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
- data_start_v = NULL;
- } else {
+ else
data_start_p = omapfb_get_region_paddr(ofbi);
- data_start_v = omapfb_get_region_vaddr(ofbi);
- }
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
offset = calc_rotation_offset_vrfb(var, fix, rotation);
@@ -828,16 +824,14 @@ static void omapfb_calc_addr(const struct omapfb_info *ofbi,
offset = calc_rotation_offset_dma(var, fix, rotation);
data_start_p += offset;
- data_start_v += offset;
if (offset)
DBG("offset %d, %d = %d\n",
var->xoffset, var->yoffset, offset);
- DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
+ DBG("paddr %x\n", data_start_p);
*paddr = data_start_p;
- *vaddr = data_start_v;
}
/* setup overlay according to the fb */
@@ -850,7 +844,6 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
struct fb_fix_screeninfo *fix = &fbi->fix;
enum omap_color_mode mode = 0;
u32 data_start_p = 0;
- void __iomem *data_start_v = NULL;
struct omap_overlay_info info;
int xres, yres;
int screen_width;
@@ -880,8 +873,7 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
}
if (ofbi->region->size)
- omapfb_calc_addr(ofbi, var, fix, rotation,
- &data_start_p, &data_start_v);
+ omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p);
r = fb_mode_to_dss_mode(var, &mode);
if (r) {
@@ -910,7 +902,6 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
mirror = ofbi->mirror;
info.paddr = data_start_p;
- info.vaddr = data_start_v;
info.screen_width = screen_width;
info.width = xres;
info.height = yres;
@@ -2276,6 +2267,87 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
return r;
}
+static void fb_videomode_to_omap_timings(struct fb_videomode *m,
+ struct omap_video_timings *t)
+{
+ t->x_res = m->xres;
+ t->y_res = m->yres;
+ t->pixel_clock = PICOS2KHZ(m->pixclock);
+ t->hsw = m->hsync_len;
+ t->hfp = m->right_margin;
+ t->hbp = m->left_margin;
+ t->vsw = m->vsync_len;
+ t->vfp = m->lower_margin;
+ t->vbp = m->upper_margin;
+}
+
+static int omapfb_find_best_mode(struct omap_dss_device *display,
+ struct omap_video_timings *timings)
+{
+ struct fb_monspecs *specs;
+ u8 *edid;
+ int r, i, best_xres, best_idx, len;
+
+ if (!display->driver->read_edid)
+ return -ENODEV;
+
+ len = 0x80 * 2;
+ edid = kmalloc(len, GFP_KERNEL);
+
+ r = display->driver->read_edid(display, edid, len);
+ if (r < 0)
+ goto err1;
+
+ specs = kzalloc(sizeof(*specs), GFP_KERNEL);
+
+ fb_edid_to_monspecs(edid, specs);
+
+ if (edid[126] > 0)
+ fb_edid_add_monspecs(edid + 0x80, specs);
+
+ best_xres = 0;
+ best_idx = -1;
+
+ for (i = 0; i < specs->modedb_len; ++i) {
+ struct fb_videomode *m;
+ struct omap_video_timings t;
+
+ m = &specs->modedb[i];
+
+ if (m->pixclock == 0)
+ continue;
+
+ /* skip repeated pixel modes */
+ if (m->xres == 2880 || m->xres == 1440)
+ continue;
+
+ fb_videomode_to_omap_timings(m, &t);
+
+ r = display->driver->check_timings(display, &t);
+ if (r == 0 && best_xres < m->xres) {
+ best_xres = m->xres;
+ best_idx = i;
+ }
+ }
+
+ if (best_xres == 0) {
+ r = -ENOENT;
+ goto err2;
+ }
+
+ fb_videomode_to_omap_timings(&specs->modedb[best_idx], timings);
+
+ r = 0;
+
+err2:
+ fb_destroy_modedb(specs->modedb);
+ kfree(specs);
+err1:
+ kfree(edid);
+
+ return r;
+}
+
static int omapfb_init_display(struct omapfb2_device *fbdev,
struct omap_dss_device *dssdev)
{
@@ -2373,8 +2445,10 @@ static int omapfb_probe(struct platform_device *pdev)
omap_dss_get_device(dssdev);
if (!dssdev->driver) {
- dev_err(&pdev->dev, "no driver for display\n");
- r = -ENODEV;
+ dev_warn(&pdev->dev, "no driver for display: %s\n",
+ dssdev->name);
+ omap_dss_put_device(dssdev);
+ continue;
}
d = &fbdev->displays[fbdev->num_displays++];
@@ -2402,9 +2476,27 @@ static int omapfb_probe(struct platform_device *pdev)
for (i = 0; i < fbdev->num_managers; i++)
fbdev->managers[i] = omap_dss_get_overlay_manager(i);
+ /* gfx overlay should be the default one. find a display
+ * connected to that, and use it as default display */
+ ovl = omap_dss_get_overlay(0);
+ if (ovl->manager && ovl->manager->device) {
+ def_display = ovl->manager->device;
+ } else {
+ dev_warn(&pdev->dev, "cannot find default display\n");
+ def_display = NULL;
+ }
+
if (def_mode && strlen(def_mode) > 0) {
if (omapfb_parse_def_modes(fbdev))
dev_warn(&pdev->dev, "cannot parse default modes\n");
+ } else if (def_display && def_display->driver->set_timings &&
+ def_display->driver->check_timings) {
+ struct omap_video_timings t;
+
+ r = omapfb_find_best_mode(def_display, &t);
+
+ if (r == 0)
+ def_display->driver->set_timings(def_display, &t);
}
r = omapfb_create_framebuffers(fbdev);
@@ -2421,16 +2513,6 @@ static int omapfb_probe(struct platform_device *pdev)
DBG("mgr->apply'ed\n");
- /* gfx overlay should be the default one. find a display
- * connected to that, and use it as default display */
- ovl = omap_dss_get_overlay(0);
- if (ovl->manager && ovl->manager->device) {
- def_display = ovl->manager->device;
- } else {
- dev_warn(&pdev->dev, "cannot find default display\n");
- def_display = NULL;
- }
-
if (def_display) {
r = omapfb_init_display(fbdev, def_display);
if (r) {
diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c
index 153bf1aceebc..1694d5148f32 100644
--- a/drivers/video/omap2/omapfb/omapfb-sysfs.c
+++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c
@@ -104,16 +104,14 @@ static ssize_t store_mirror(struct device *dev,
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
- int mirror;
+ bool mirror;
int r;
struct fb_var_screeninfo new_var;
- r = kstrtoint(buf, 0, &mirror);
+ r = strtobool(buf, &mirror);
if (r)
return r;
- mirror = !!mirror;
-
if (!lock_fb_info(fbi))
return -ENODEV;
diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c
index f27ae16ead2e..ae3caa6755c2 100644
--- a/drivers/video/platinumfb.c
+++ b/drivers/video/platinumfb.c
@@ -490,7 +490,7 @@ static int platinum_var_to_par(struct fb_var_screeninfo *var,
/*
- * Parse user speficied options (`video=platinumfb:')
+ * Parse user specified options (`video=platinumfb:')
*/
static int __init platinumfb_setup(char *options)
{
@@ -683,7 +683,7 @@ static struct platform_driver platinum_driver =
.of_match_table = platinumfb_match,
},
.probe = platinumfb_probe,
- .remove = platinumfb_remove,
+ .remove = __devexit_p(platinumfb_remove),
};
static int __init platinumfb_init(void)
diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c
index 27f93aab6ddc..dc7bfa91e57a 100644
--- a/drivers/video/pm2fb.c
+++ b/drivers/video/pm2fb.c
@@ -973,8 +973,8 @@ static int pm2fb_pan_display(struct fb_var_screeninfo *var,
{
struct pm2fb_par *p = info->par;
u32 base;
- u32 depth = (var->bits_per_pixel + 7) & ~7;
- u32 xres = (var->xres + 31) & ~31;
+ u32 depth = (info->var.bits_per_pixel + 7) & ~7;
+ u32 xres = (info->var.xres + 31) & ~31;
depth = (depth > 32) ? 32 : depth;
base = to3264(var->yoffset * xres + var->xoffset, depth, 1);
@@ -1773,7 +1773,7 @@ MODULE_DEVICE_TABLE(pci, pm2fb_id_table);
#ifndef MODULE
/**
- * Parse user speficied options.
+ * Parse user specified options.
*
* This is, comma-separated options following `video=pm2fb:'.
*/
diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c
index 6666f45a2f8c..6632ee5ecb7e 100644
--- a/drivers/video/pm3fb.c
+++ b/drivers/video/pm3fb.c
@@ -1147,9 +1147,9 @@ static int pm3fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct pm3_par *par = info->par;
- const u32 xres = (var->xres + 31) & ~31;
+ const u32 xres = (info->var.xres + 31) & ~31;
- par->base = pm3fb_shift_bpp(var->bits_per_pixel,
+ par->base = pm3fb_shift_bpp(info->var.bits_per_pixel,
(var->yoffset * xres)
+ var->xoffset);
PM3_WAIT(par, 1);
@@ -1525,7 +1525,7 @@ static int __init pm3fb_setup(char *options)
{
char *this_opt;
- /* Parse user speficied options (`video=pm3fb:') */
+ /* Parse user specified options (`video=pm3fb:') */
if (!options || !*options)
return 0;
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index 65560a1a0439..213fbbcf613b 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -1082,7 +1082,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
}
retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt,
- IRQF_DISABLED, DEVICE_NAME, &dev->core);
+ 0, DEVICE_NAME, &dev->core);
if (retval) {
dev_err(&dev->core, "%s: request_irq failed %d\n", __func__,
retval);
diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c
index 0283c7021090..1ed8b366618d 100644
--- a/drivers/video/pxa3xx-gcu.c
+++ b/drivers/video/pxa3xx-gcu.c
@@ -31,8 +31,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
-
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/miscdevice.h>
@@ -678,7 +676,7 @@ pxa3xx_gcu_probe(struct platform_device *dev)
}
ret = request_irq(irq, pxa3xx_gcu_handle_irq,
- IRQF_DISABLED, DRV_NAME, priv);
+ 0, DRV_NAME, priv);
if (ret) {
dev_err(&dev->dev, "request_irq failed\n");
ret = -EBUSY;
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 0f4e8c942f9e..1d1e4f175e78 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -1309,16 +1309,6 @@ static int pxafb_smart_init(struct pxafb_info *fbi)
return 0;
}
#else
-int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
-{
- return 0;
-}
-
-int pxafb_smart_flush(struct fb_info *info)
-{
- return 0;
-}
-
static inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; }
#endif /* CONFIG_FB_PXA_SMARTPANEL */
@@ -2191,7 +2181,7 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_mem;
}
- ret = request_irq(irq, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi);
+ ret = request_irq(irq, pxafb_handle_irq, 0, "LCD", fbi);
if (ret) {
dev_err(&dev->dev, "request_irq failed: %d\n", ret);
ret = -EBUSY;
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 4aecf213c9be..0753b1cfcb8b 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -81,6 +81,7 @@ struct s3c_fb;
* @palette: Address of palette memory, or 0 if none.
* @has_prtcon: Set if has PRTCON register.
* @has_shadowcon: Set if has SHADOWCON register.
+ * @has_clksel: Set if VIDCON0 register has CLKSEL bit.
*/
struct s3c_fb_variant {
unsigned int is_2443:1;
@@ -98,6 +99,7 @@ struct s3c_fb_variant {
unsigned int has_prtcon:1;
unsigned int has_shadowcon:1;
+ unsigned int has_clksel:1;
};
/**
@@ -186,6 +188,7 @@ struct s3c_fb_vsync {
* @dev: The device that we bound to, for printing, etc.
* @regs_res: The resource we claimed for the IO registers.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
+ * @lcd_clk: The clk (sclk) feeding pixclk.
* @regs: The mapped hardware registers.
* @variant: Variant information for this hardware.
* @enabled: A bitmask of enabled hardware windows.
@@ -200,6 +203,7 @@ struct s3c_fb {
struct device *dev;
struct resource *regs_res;
struct clk *bus_clk;
+ struct clk *lcd_clk;
void __iomem *regs;
struct s3c_fb_variant variant;
@@ -336,10 +340,15 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
*/
static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
{
- unsigned long clk = clk_get_rate(sfb->bus_clk);
+ unsigned long clk;
unsigned long long tmp;
unsigned int result;
+ if (sfb->variant.has_clksel)
+ clk = clk_get_rate(sfb->bus_clk);
+ else
+ clk = clk_get_rate(sfb->lcd_clk);
+
tmp = (unsigned long long)clk;
tmp *= pixclk;
@@ -883,7 +892,7 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
}
}
/* Offset in bytes to the end of the displayed area */
- end_boff = start_boff + var->yres * info->fix.line_length;
+ end_boff = start_boff + info->var.yres * info->fix.line_length;
/* Temporarily turn off per-vsync update from shadow registers until
* both start and end addresses are updated to prevent corruption */
@@ -1354,13 +1363,24 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
clk_enable(sfb->bus_clk);
+ if (!sfb->variant.has_clksel) {
+ sfb->lcd_clk = clk_get(dev, "sclk_fimd");
+ if (IS_ERR(sfb->lcd_clk)) {
+ dev_err(dev, "failed to get lcd clock\n");
+ ret = PTR_ERR(sfb->lcd_clk);
+ goto err_bus_clk;
+ }
+
+ clk_enable(sfb->lcd_clk);
+ }
+
pm_runtime_enable(sfb->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "failed to find registers\n");
ret = -ENOENT;
- goto err_clk;
+ goto err_lcd_clk;
}
sfb->regs_res = request_mem_region(res->start, resource_size(res),
@@ -1368,7 +1388,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
if (!sfb->regs_res) {
dev_err(dev, "failed to claim register region\n");
ret = -ENOENT;
- goto err_clk;
+ goto err_lcd_clk;
}
sfb->regs = ioremap(res->start, resource_size(res));
@@ -1450,7 +1470,13 @@ err_ioremap:
err_req_region:
release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
-err_clk:
+err_lcd_clk:
+ if (!sfb->variant.has_clksel) {
+ clk_disable(sfb->lcd_clk);
+ clk_put(sfb->lcd_clk);
+ }
+
+err_bus_clk:
clk_disable(sfb->bus_clk);
clk_put(sfb->bus_clk);
@@ -1481,6 +1507,11 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
iounmap(sfb->regs);
+ if (!sfb->variant.has_clksel) {
+ clk_disable(sfb->lcd_clk);
+ clk_put(sfb->lcd_clk);
+ }
+
clk_disable(sfb->bus_clk);
clk_put(sfb->bus_clk);
@@ -1510,6 +1541,9 @@ static int s3c_fb_suspend(struct device *dev)
s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
}
+ if (!sfb->variant.has_clksel)
+ clk_disable(sfb->lcd_clk);
+
clk_disable(sfb->bus_clk);
return 0;
}
@@ -1524,6 +1558,9 @@ static int s3c_fb_resume(struct device *dev)
clk_enable(sfb->bus_clk);
+ if (!sfb->variant.has_clksel)
+ clk_enable(sfb->lcd_clk);
+
/* setup gpio and output polarity controls */
pd->setup_gpio();
writel(pd->vidcon1, sfb->regs + VIDCON1);
@@ -1569,6 +1606,9 @@ static int s3c_fb_runtime_suspend(struct device *dev)
s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
}
+ if (!sfb->variant.has_clksel)
+ clk_disable(sfb->lcd_clk);
+
clk_disable(sfb->bus_clk);
return 0;
}
@@ -1583,6 +1623,9 @@ static int s3c_fb_runtime_resume(struct device *dev)
clk_enable(sfb->bus_clk);
+ if (!sfb->variant.has_clksel)
+ clk_enable(sfb->lcd_clk);
+
/* setup gpio and output polarity controls */
pd->setup_gpio();
writel(pd->vidcon1, sfb->regs + VIDCON1);
@@ -1755,6 +1798,7 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx = {
},
.has_prtcon = 1,
+ .has_clksel = 1,
},
.win[0] = &s3c_fb_data_64xx_wins[0],
.win[1] = &s3c_fb_data_64xx_wins[1],
@@ -1785,6 +1829,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
},
.has_prtcon = 1,
+ .has_clksel = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
.win[1] = &s3c_fb_data_s5p_wins[1],
@@ -1815,6 +1860,37 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
},
.has_shadowcon = 1,
+ .has_clksel = 1,
+ },
+ .win[0] = &s3c_fb_data_s5p_wins[0],
+ .win[1] = &s3c_fb_data_s5p_wins[1],
+ .win[2] = &s3c_fb_data_s5p_wins[2],
+ .win[3] = &s3c_fb_data_s5p_wins[3],
+ .win[4] = &s3c_fb_data_s5p_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_shadowcon = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
.win[1] = &s3c_fb_data_s5p_wins[1],
@@ -1843,6 +1919,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
[0] = 0x400,
[1] = 0x800,
},
+ .has_clksel = 1,
},
.win[0] = &(struct s3c_fb_win_variant) {
.palette_sz = 256,
@@ -1859,6 +1936,30 @@ static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
},
};
+static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = {
+ .variant = {
+ .nr_windows = 3,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ },
+ },
+ .win[0] = &s3c_fb_data_s5p_wins[0],
+ .win[1] = &s3c_fb_data_s5p_wins[1],
+ .win[2] = &s3c_fb_data_s5p_wins[2],
+};
+
static struct platform_device_id s3c_fb_driver_ids[] = {
{
.name = "s3c-fb",
@@ -1870,8 +1971,14 @@ static struct platform_device_id s3c_fb_driver_ids[] = {
.name = "s5pv210-fb",
.driver_data = (unsigned long)&s3c_fb_data_s5pv210,
}, {
+ .name = "exynos4-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_exynos4,
+ }, {
.name = "s3c2443-fb",
.driver_data = (unsigned long)&s3c_fb_data_s3c2443,
+ }, {
+ .name = "s5p64x0-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5p64x0,
},
{},
};
diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c
index 0aa13761de6e..ee4c0df217f7 100644
--- a/drivers/video/s3c2410fb.c
+++ b/drivers/video/s3c2410fb.c
@@ -767,7 +767,6 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
{
- struct cpufreq_freqs *freqs = data;
struct s3c2410fb_info *info;
struct fb_info *fbinfo;
long delta_f;
@@ -911,7 +910,7 @@ static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
+ ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c
index 4ca5d0c8fe84..946a949f4c7d 100644
--- a/drivers/video/s3fb.c
+++ b/drivers/video/s3fb.c
@@ -1019,12 +1019,13 @@ static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
unsigned int offset;
/* Calculate the offset */
- if (var->bits_per_pixel == 0) {
- offset = (var->yoffset / 16) * (var->xres_virtual / 2) + (var->xoffset / 2);
+ if (info->var.bits_per_pixel == 0) {
+ offset = (var->yoffset / 16) * (info->var.xres_virtual / 2)
+ + (var->xoffset / 2);
offset = offset >> 2;
} else {
offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset * var->bits_per_pixel / 8);
+ (var->xoffset * info->var.bits_per_pixel / 8);
offset = offset >> 2;
}
@@ -1504,7 +1505,7 @@ static struct pci_driver s3fb_pci_driver = {
.resume = s3_pci_resume,
};
-/* Parse user speficied options */
+/* Parse user specified options */
#ifndef MODULE
static int __init s3fb_setup(char *options)
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index e8b76d65a070..98d55d0e2da5 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -1457,8 +1457,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev)
if (ret)
goto failed;
- ret = request_irq(irq, sa1100fb_handle_irq, IRQF_DISABLED,
- "LCD", fbi);
+ ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi);
if (ret) {
printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret);
goto failed;
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index 4de541ca9c52..beb495044b24 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -1477,15 +1477,9 @@ static void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *r
vgaHWProtect(par, 0);
}
-static void savagefb_update_start(struct savagefb_par *par,
- struct fb_var_screeninfo *var)
+static void savagefb_update_start(struct savagefb_par *par, int base)
{
- int base;
-
- base = ((var->yoffset * var->xres_virtual + (var->xoffset & ~1))
- * ((var->bits_per_pixel+7) / 8)) >> 2;
-
- /* now program the start address registers */
+ /* program the start address registers */
vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
vga_out8(0x3d4, 0x69, par);
@@ -1550,8 +1544,12 @@ static int savagefb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct savagefb_par *par = info->par;
+ int base;
+
+ base = (var->yoffset * info->fix.line_length
+ + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2;
- savagefb_update_start(par, var);
+ savagefb_update_start(par, base);
return 0;
}
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 7d54e2c612f7..647ba984f00f 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -1111,6 +1111,7 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
static void sh_hdmi_edid_work_fn(struct work_struct *work)
{
struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
+ struct fb_info *info;
struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
struct sh_mobile_lcdc_chan *ch;
int ret;
@@ -1123,8 +1124,9 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
mutex_lock(&hdmi->mutex);
+ info = hdmi->info;
+
if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) {
- struct fb_info *info = hdmi->info;
unsigned long parent_rate = 0, hdmi_rate;
ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate);
@@ -1148,42 +1150,45 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
ch = info->par;
- console_lock();
+ if (lock_fb_info(info)) {
+ console_lock();
- /* HDMI plug in */
- if (!sh_hdmi_must_reconfigure(hdmi) &&
- info->state == FBINFO_STATE_RUNNING) {
- /*
- * First activation with the default monitor - just turn
- * on, if we run a resume here, the logo disappears
- */
- if (lock_fb_info(info)) {
+ /* HDMI plug in */
+ if (!sh_hdmi_must_reconfigure(hdmi) &&
+ info->state == FBINFO_STATE_RUNNING) {
+ /*
+ * First activation with the default monitor - just turn
+ * on, if we run a resume here, the logo disappears
+ */
info->var.width = hdmi->var.width;
info->var.height = hdmi->var.height;
sh_hdmi_display_on(hdmi, info);
- unlock_fb_info(info);
+ } else {
+ /* New monitor or have to wake up */
+ fb_set_suspend(info, 0);
}
- } else {
- /* New monitor or have to wake up */
- fb_set_suspend(info, 0);
- }
- console_unlock();
+ console_unlock();
+ unlock_fb_info(info);
+ }
} else {
ret = 0;
- if (!hdmi->info)
+ if (!info)
goto out;
hdmi->monspec.modedb_len = 0;
fb_destroy_modedb(hdmi->monspec.modedb);
hdmi->monspec.modedb = NULL;
- console_lock();
+ if (lock_fb_info(info)) {
+ console_lock();
- /* HDMI disconnect */
- fb_set_suspend(hdmi->info, 1);
+ /* HDMI disconnect */
+ fb_set_suspend(info, 1);
- console_unlock();
+ console_unlock();
+ unlock_fb_info(info);
+ }
}
out:
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index b048417247e8..3a41c013d031 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -24,39 +24,14 @@
#include <linux/backlight.h>
#include <linux/gpio.h>
#include <video/sh_mobile_lcdc.h>
+#include <video/sh_mobile_meram.h>
#include <linux/atomic.h>
#include "sh_mobile_lcdcfb.h"
-#include "sh_mobile_meram.h"
#define SIDE_B_OFFSET 0x1000
#define MIRROR_OFFSET 0x2000
-/* shared registers */
-#define _LDDCKR 0x410
-#define _LDDCKSTPR 0x414
-#define _LDINTR 0x468
-#define _LDSR 0x46c
-#define _LDCNT1R 0x470
-#define _LDCNT2R 0x474
-#define _LDRCNTR 0x478
-#define _LDDDSR 0x47c
-#define _LDDWD0R 0x800
-#define _LDDRDR 0x840
-#define _LDDWAR 0x900
-#define _LDDRAR 0x904
-
-/* shared registers and their order for context save/restore */
-static int lcdc_shared_regs[] = {
- _LDDCKR,
- _LDDCKSTPR,
- _LDINTR,
- _LDDDSR,
- _LDCNT1R,
- _LDCNT2R,
-};
-#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
-
#define MAX_XRES 1920
#define MAX_YRES 1080
@@ -98,22 +73,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
[LDPMR] = 0x63c,
};
-#define START_LCDC 0x00000001
-#define LCDC_RESET 0x00000100
-#define DISPLAY_BEU 0x00000008
-#define LCDC_ENABLE 0x00000001
-#define LDINTR_FE 0x00000400
-#define LDINTR_VSE 0x00000200
-#define LDINTR_VEE 0x00000100
-#define LDINTR_FS 0x00000004
-#define LDINTR_VSS 0x00000002
-#define LDINTR_VES 0x00000001
-#define LDRCNTR_SRS 0x00020000
-#define LDRCNTR_SRC 0x00010000
-#define LDRCNTR_MRS 0x00000002
-#define LDRCNTR_MRC 0x00000001
-#define LDSR_MRS 0x00000100
-
static const struct fb_videomode default_720p = {
.name = "HDMI 720p",
.xres = 1280,
@@ -141,7 +100,6 @@ struct sh_mobile_lcdc_priv {
unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2];
struct notifier_block notifier;
- unsigned long saved_shared_regs[NR_SHARED_REGS];
int started;
int forced_bpp; /* 2 channel LCDC must share bpp setting */
struct sh_mobile_meram_info *meram_dev;
@@ -218,33 +176,36 @@ static void lcdc_sys_write_index(void *handle, unsigned long data)
{
struct sh_mobile_lcdc_chan *ch = handle;
- lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
- lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT);
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
+ (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
}
static void lcdc_sys_write_data(void *handle, unsigned long data)
{
struct sh_mobile_lcdc_chan *ch = handle;
- lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
- lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW);
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
+ (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
}
static unsigned long lcdc_sys_read_data(void *handle)
{
struct sh_mobile_lcdc_chan *ch = handle;
- lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
- lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR);
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
+ lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA |
+ (lcdc_chan_is_sublcd(ch) ? 2 : 0));
udelay(1);
- lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
- return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff;
+ return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK;
}
struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
@@ -256,18 +217,22 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{
if (atomic_inc_and_test(&priv->hw_usecnt)) {
- pm_runtime_get_sync(priv->dev);
if (priv->dot_clk)
clk_enable(priv->dot_clk);
+ pm_runtime_get_sync(priv->dev);
+ if (priv->meram_dev && priv->meram_dev->pdev)
+ pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
}
}
static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
+ if (priv->meram_dev && priv->meram_dev->pdev)
+ pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
+ pm_runtime_put(priv->dev);
if (priv->dot_clk)
clk_disable(priv->dot_clk);
- pm_runtime_put(priv->dev);
}
}
@@ -319,13 +284,13 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
if (bcfg->start_transfer)
bcfg->start_transfer(bcfg->board_data, ch,
&sh_mobile_lcdc_sys_bus_ops);
- lcdc_write_chan(ch, LDSM2R, 1);
+ lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
} else {
if (bcfg->start_transfer)
bcfg->start_transfer(bcfg->board_data, ch,
&sh_mobile_lcdc_sys_bus_ops);
- lcdc_write_chan(ch, LDSM2R, 1);
+ lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
}
}
@@ -341,22 +306,16 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{
struct sh_mobile_lcdc_priv *priv = data;
struct sh_mobile_lcdc_chan *ch;
- unsigned long tmp;
unsigned long ldintr;
int is_sub;
int k;
- /* acknowledge interrupt */
- ldintr = tmp = lcdc_read(priv, _LDINTR);
- /*
- * disable further VSYNC End IRQs, preserve all other enabled IRQs,
- * write 0 to bits 0-6 to ack all triggered IRQs.
- */
- tmp &= 0xffffff00 & ~LDINTR_VEE;
- lcdc_write(priv, _LDINTR, tmp);
+ /* Acknowledge interrupts and disable further VSYNC End IRQs. */
+ ldintr = lcdc_read(priv, _LDINTR);
+ lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE);
/* figure out if this interrupt is for main or sub lcd */
- is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0;
+ is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0;
/* wake up channel and disable clocks */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -365,7 +324,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
if (!ch->enabled)
continue;
- /* Frame Start */
+ /* Frame End */
if (ldintr & LDINTR_FS) {
if (is_sub == lcdc_chan_is_sublcd(ch)) {
ch->frame_end = 1;
@@ -391,16 +350,17 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
/* start or stop the lcdc */
if (start)
- lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
+ lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO);
else
- lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);
+ lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO);
/* wait until power is applied/stopped on all channels */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
while (1) {
- tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
- if (start && tmp == 3)
+ tmp = lcdc_read_chan(&priv->ch[k], LDPMR)
+ & LDPMR_LPS;
+ if (start && tmp == LDPMR_LPS)
break;
if (!start && tmp == 0)
break;
@@ -418,13 +378,13 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
u32 tmp;
tmp = ch->ldmt1r_value;
- tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
- tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
+ tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
+ tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
lcdc_write_chan(ch, LDMT1R, tmp);
/* setup SYS bus */
@@ -463,242 +423,239 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
lcdc_write_chan(ch, LDHAJR, tmp);
}
-static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
+/*
+ * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * @priv: LCDC device
+ *
+ * Configure all enabled channels and start the LCDC device. All external
+ * devices (clocks, MERAM, panels, ...) are not touched by this function.
+ */
+static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
- struct sh_mobile_lcdc_board_cfg *board_cfg;
unsigned long tmp;
int bpp = 0;
- unsigned long ldddsr;
- int k, m, ret;
+ int k, m;
- /* enable clocks before accessing the hardware */
- for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- if (priv->ch[k].enabled) {
- sh_mobile_lcdc_clk_on(priv);
- if (!bpp)
- bpp = priv->ch[k].info->var.bits_per_pixel;
- }
- }
-
- /* reset */
- lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
- lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
-
- /* enable LCDC channels */
- tmp = lcdc_read(priv, _LDCNT2R);
- tmp |= priv->ch[0].enabled;
- tmp |= priv->ch[1].enabled;
- lcdc_write(priv, _LDCNT2R, tmp);
-
- /* read data from external memory, avoid using the BEU for now */
- lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);
+ /* Enable LCDC channels. Read data from external memory, avoid using the
+ * BEU for now.
+ */
+ lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled);
- /* stop the lcdc first */
+ /* Stop the LCDC first and disable all interrupts. */
sh_mobile_lcdc_start_stop(priv, 0);
+ lcdc_write(priv, _LDINTR, 0);
- /* configure clocks */
+ /* Configure power supply, dot clocks and start them. */
tmp = priv->lddckr;
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
-
- if (!priv->ch[k].enabled)
+ if (!ch->enabled)
continue;
+ if (!bpp)
+ bpp = ch->info->var.bits_per_pixel;
+
+ /* Power supply */
+ lcdc_write_chan(ch, LDPMR, 0);
+
m = ch->cfg.clock_divider;
if (!m)
continue;
- if (m == 1)
- m = 1 << 6;
- tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
-
- /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
+ /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
+ * denominator.
+ */
lcdc_write_chan(ch, LDDCKPAT1R, 0);
lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
+
+ if (m == 1)
+ m = LDDCKR_MOSEL;
+ tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
}
lcdc_write(priv, _LDDCKR, tmp);
-
- /* start dotclock again */
lcdc_write(priv, _LDDCKSTPR, 0);
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
- /* interrupts are disabled to begin with */
- lcdc_write(priv, _LDINTR, 0);
-
+ /* Setup geometry, format, frame buffer memory and operation mode. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
-
if (!ch->enabled)
continue;
sh_mobile_lcdc_geometry(ch);
- /* power supply */
- lcdc_write_chan(ch, LDPMR, 0);
-
- board_cfg = &ch->cfg.board_cfg;
- if (board_cfg->setup_sys) {
- ret = board_cfg->setup_sys(board_cfg->board_data,
- ch, &sh_mobile_lcdc_sys_bus_ops);
- if (ret)
- return ret;
- }
- }
-
- /* word and long word swap */
- ldddsr = lcdc_read(priv, _LDDDSR);
- if (priv->ch[0].info->var.nonstd)
- lcdc_write(priv, _LDDDSR, ldddsr | 7);
- else {
- switch (bpp) {
- case 16:
- lcdc_write(priv, _LDDDSR, ldddsr | 6);
- break;
- case 24:
- lcdc_write(priv, _LDDDSR, ldddsr | 7);
- break;
- case 32:
- lcdc_write(priv, _LDDDSR, ldddsr | 4);
- break;
- }
- }
-
- for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- unsigned long base_addr_y;
- unsigned long base_addr_c = 0;
- int pitch;
- ch = &priv->ch[k];
-
- if (!priv->ch[k].enabled)
- continue;
-
- /* set bpp format in PKF[4:0] */
- tmp = lcdc_read_chan(ch, LDDFR);
- tmp &= ~0x0003031f;
if (ch->info->var.nonstd) {
- tmp |= (ch->info->var.nonstd << 16);
+ tmp = (ch->info->var.nonstd << 16);
switch (ch->info->var.bits_per_pixel) {
case 12:
+ tmp |= LDDFR_YF_420;
break;
case 16:
- tmp |= (0x1 << 8);
+ tmp |= LDDFR_YF_422;
break;
case 24:
- tmp |= (0x2 << 8);
+ default:
+ tmp |= LDDFR_YF_444;
break;
}
} else {
switch (ch->info->var.bits_per_pixel) {
case 16:
- tmp |= 0x03;
+ tmp = LDDFR_PKF_RGB16;
break;
case 24:
- tmp |= 0x0b;
+ tmp = LDDFR_PKF_RGB24;
break;
case 32:
+ default:
+ tmp = LDDFR_PKF_ARGB32;
break;
}
}
+
lcdc_write_chan(ch, LDDFR, tmp);
+ lcdc_write_chan(ch, LDMLSR, ch->pitch);
+ lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
+ if (ch->info->var.nonstd)
+ lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
- base_addr_y = ch->info->fix.smem_start;
- base_addr_c = base_addr_y +
- ch->info->var.xres *
- ch->info->var.yres_virtual;
- pitch = ch->info->fix.line_length;
+ /* When using deferred I/O mode, configure the LCDC for one-shot
+ * operation and enable the frame end interrupt. Otherwise use
+ * continuous read mode.
+ */
+ if (ch->ldmt1r_value & LDMT1R_IFM &&
+ ch->cfg.sys_bus_cfg.deferred_io_msec) {
+ lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
+ lcdc_write(priv, _LDINTR, LDINTR_FE);
+ } else {
+ lcdc_write_chan(ch, LDSM1R, 0);
+ }
+ }
- /* test if we can enable meram */
- if (ch->cfg.meram_cfg && priv->meram_dev &&
- priv->meram_dev->ops) {
- struct sh_mobile_meram_cfg *cfg;
- struct sh_mobile_meram_info *mdev;
- unsigned long icb_addr_y, icb_addr_c;
- int icb_pitch;
- int pf;
+ /* Word and long word swap. */
+ if (priv->ch[0].info->var.nonstd)
+ tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
+ else {
+ switch (bpp) {
+ case 16:
+ tmp = LDDDSR_LS | LDDDSR_WS;
+ break;
+ case 24:
+ tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
+ break;
+ case 32:
+ default:
+ tmp = LDDDSR_LS;
+ break;
+ }
+ }
+ lcdc_write(priv, _LDDDSR, tmp);
- cfg = ch->cfg.meram_cfg;
- mdev = priv->meram_dev;
- /* we need to de-init configured ICBs before we
- * we can re-initialize them.
- */
- if (ch->meram_enabled)
- mdev->ops->meram_unregister(mdev, cfg);
+ /* Enable the display output. */
+ lcdc_write(priv, _LDCNT1R, LDCNT1R_DE);
+ sh_mobile_lcdc_start_stop(priv, 1);
+ priv->started = 1;
+}
- ch->meram_enabled = 0;
+static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
+{
+ struct sh_mobile_meram_info *mdev = priv->meram_dev;
+ struct sh_mobile_lcdc_board_cfg *board_cfg;
+ struct sh_mobile_lcdc_chan *ch;
+ unsigned long tmp;
+ int ret;
+ int k;
- if (ch->info->var.nonstd) {
- if (ch->info->var.bits_per_pixel == 24)
- pf = SH_MOBILE_MERAM_PF_NV24;
- else
- pf = SH_MOBILE_MERAM_PF_NV;
- } else {
- pf = SH_MOBILE_MERAM_PF_RGB;
- }
+ /* enable clocks before accessing the hardware */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ if (priv->ch[k].enabled)
+ sh_mobile_lcdc_clk_on(priv);
+ }
- ret = mdev->ops->meram_register(mdev, cfg, pitch,
- ch->info->var.yres,
- pf,
- base_addr_y,
- base_addr_c,
- &icb_addr_y,
- &icb_addr_c,
- &icb_pitch);
- if (!ret) {
- /* set LDSA1R value */
- base_addr_y = icb_addr_y;
- pitch = icb_pitch;
-
- /* set LDSA2R value if required */
- if (base_addr_c)
- base_addr_c = icb_addr_c;
-
- ch->meram_enabled = 1;
- }
- }
+ /* reset */
+ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR);
+ lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
- /* point out our frame buffer */
- lcdc_write_chan(ch, LDSA1R, base_addr_y);
- if (ch->info->var.nonstd)
- lcdc_write_chan(ch, LDSA2R, base_addr_c);
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
- /* set line size */
- lcdc_write_chan(ch, LDMLSR, pitch);
+ if (!ch->enabled)
+ continue;
- /* setup deferred io if SYS bus */
- tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
- if (ch->ldmt1r_value & (1 << 12) && tmp) {
- ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
- ch->defio.delay = msecs_to_jiffies(tmp);
- ch->info->fbdefio = &ch->defio;
- fb_deferred_io_init(ch->info);
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->setup_sys) {
+ ret = board_cfg->setup_sys(board_cfg->board_data, ch,
+ &sh_mobile_lcdc_sys_bus_ops);
+ if (ret)
+ return ret;
+ }
+ }
- /* one-shot mode */
- lcdc_write_chan(ch, LDSM1R, 1);
+ /* Compute frame buffer base address and pitch for each channel. */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ struct sh_mobile_meram_cfg *cfg;
+ int pixelformat;
- /* enable "Frame End Interrupt Enable" bit */
- lcdc_write(priv, _LDINTR, LDINTR_FE);
+ ch = &priv->ch[k];
+ if (!ch->enabled)
+ continue;
- } else {
- /* continuous read mode */
- lcdc_write_chan(ch, LDSM1R, 0);
+ ch->base_addr_y = ch->info->fix.smem_start;
+ ch->base_addr_c = ch->base_addr_y
+ + ch->info->var.xres
+ * ch->info->var.yres_virtual;
+ ch->pitch = ch->info->fix.line_length;
+
+ /* Enable MERAM if possible. */
+ cfg = ch->cfg.meram_cfg;
+ if (mdev == NULL || mdev->ops == NULL || cfg == NULL)
+ continue;
+
+ /* we need to de-init configured ICBs before we can
+ * re-initialize them.
+ */
+ if (ch->meram_enabled) {
+ mdev->ops->meram_unregister(mdev, cfg);
+ ch->meram_enabled = 0;
}
+
+ if (!ch->info->var.nonstd)
+ pixelformat = SH_MOBILE_MERAM_PF_RGB;
+ else if (ch->info->var.bits_per_pixel == 24)
+ pixelformat = SH_MOBILE_MERAM_PF_NV24;
+ else
+ pixelformat = SH_MOBILE_MERAM_PF_NV;
+
+ ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
+ ch->info->var.yres, pixelformat,
+ ch->base_addr_y, ch->base_addr_c,
+ &ch->base_addr_y, &ch->base_addr_c,
+ &ch->pitch);
+ if (!ret)
+ ch->meram_enabled = 1;
}
- /* display output */
- lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
+ /* Start the LCDC. */
+ __sh_mobile_lcdc_start(priv);
- /* start the lcdc */
- sh_mobile_lcdc_start_stop(priv, 1);
- priv->started = 1;
-
- /* tell the board code to enable the panel */
+ /* Setup deferred I/O, tell the board code to enable the panels, and
+ * turn backlight on.
+ */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
if (!ch->enabled)
continue;
+ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
+ ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
+ ch->defio.delay = msecs_to_jiffies(tmp);
+ ch->info->fbdefio = &ch->defio;
+ fb_deferred_io_init(ch->info);
+ }
+
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
board_cfg->display_on(board_cfg->board_data, ch->info);
@@ -776,42 +733,42 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
{
- int ifm, miftyp;
-
- switch (ch->cfg.interface_type) {
- case RGB8: ifm = 0; miftyp = 0; break;
- case RGB9: ifm = 0; miftyp = 4; break;
- case RGB12A: ifm = 0; miftyp = 5; break;
- case RGB12B: ifm = 0; miftyp = 6; break;
- case RGB16: ifm = 0; miftyp = 7; break;
- case RGB18: ifm = 0; miftyp = 10; break;
- case RGB24: ifm = 0; miftyp = 11; break;
- case SYS8A: ifm = 1; miftyp = 0; break;
- case SYS8B: ifm = 1; miftyp = 1; break;
- case SYS8C: ifm = 1; miftyp = 2; break;
- case SYS8D: ifm = 1; miftyp = 3; break;
- case SYS9: ifm = 1; miftyp = 4; break;
- case SYS12: ifm = 1; miftyp = 5; break;
- case SYS16A: ifm = 1; miftyp = 7; break;
- case SYS16B: ifm = 1; miftyp = 8; break;
- case SYS16C: ifm = 1; miftyp = 9; break;
- case SYS18: ifm = 1; miftyp = 10; break;
- case SYS24: ifm = 1; miftyp = 11; break;
- default: goto bad;
+ int interface_type = ch->cfg.interface_type;
+
+ switch (interface_type) {
+ case RGB8:
+ case RGB9:
+ case RGB12A:
+ case RGB12B:
+ case RGB16:
+ case RGB18:
+ case RGB24:
+ case SYS8A:
+ case SYS8B:
+ case SYS8C:
+ case SYS8D:
+ case SYS9:
+ case SYS12:
+ case SYS16A:
+ case SYS16B:
+ case SYS16C:
+ case SYS18:
+ case SYS24:
+ break;
+ default:
+ return -EINVAL;
}
/* SUBLCD only supports SYS interface */
if (lcdc_chan_is_sublcd(ch)) {
- if (ifm == 0)
- goto bad;
- else
- ifm = 0;
+ if (!(interface_type & LDMT1R_IFM))
+ return -EINVAL;
+
+ interface_type &= ~LDMT1R_IFM;
}
- ch->ldmt1r_value = (ifm << 12) | miftyp;
+ ch->ldmt1r_value = interface_type;
return 0;
- bad:
- return -EINVAL;
}
static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
@@ -819,18 +776,24 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
struct sh_mobile_lcdc_priv *priv)
{
char *str;
- int icksel;
switch (clock_source) {
- case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
- case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
- case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
+ case LCDC_CLK_BUS:
+ str = "bus_clk";
+ priv->lddckr = LDDCKR_ICKSEL_BUS;
+ break;
+ case LCDC_CLK_PERIPHERAL:
+ str = "peripheral_clk";
+ priv->lddckr = LDDCKR_ICKSEL_MIPI;
+ break;
+ case LCDC_CLK_EXTERNAL:
+ str = NULL;
+ priv->lddckr = LDDCKR_ICKSEL_HDMI;
+ break;
default:
return -EINVAL;
}
- priv->lddckr = icksel << 16;
-
if (str) {
priv->dot_clk = clk_get(&pdev->dev, str);
if (IS_ERR(priv->dot_clk)) {
@@ -914,12 +877,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
unsigned long base_addr_y, base_addr_c;
unsigned long c_offset;
- if (!var->nonstd)
- new_pan_offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset * (info->var.bits_per_pixel / 8));
+ if (!info->var.nonstd)
+ new_pan_offset = var->yoffset * info->fix.line_length
+ + var->xoffset * (info->var.bits_per_pixel / 8);
else
- new_pan_offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset);
+ new_pan_offset = var->yoffset * info->fix.line_length
+ + var->xoffset;
if (new_pan_offset == ch->pan_offset)
return 0; /* No change, do nothing */
@@ -928,44 +891,40 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
/* Set the source address for the next refresh */
base_addr_y = ch->dma_handle + new_pan_offset;
- if (var->nonstd) {
+ if (info->var.nonstd) {
/* Set y offset */
- c_offset = (var->yoffset *
- info->fix.line_length *
- (info->var.bits_per_pixel - 8)) / 8;
- base_addr_c = ch->dma_handle + var->xres * var->yres_virtual +
- c_offset;
+ c_offset = var->yoffset * info->fix.line_length
+ * (info->var.bits_per_pixel - 8) / 8;
+ base_addr_c = ch->dma_handle
+ + info->var.xres * info->var.yres_virtual
+ + c_offset;
/* Set x offset */
if (info->var.bits_per_pixel == 24)
base_addr_c += 2 * var->xoffset;
else
base_addr_c += var->xoffset;
- } else
- base_addr_c = 0;
+ }
- if (!ch->meram_enabled) {
- lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
- if (base_addr_c)
- lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
- } else {
+ if (ch->meram_enabled) {
struct sh_mobile_meram_cfg *cfg;
struct sh_mobile_meram_info *mdev;
- unsigned long icb_addr_y, icb_addr_c;
int ret;
cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev;
ret = mdev->ops->meram_update(mdev, cfg,
base_addr_y, base_addr_c,
- &icb_addr_y, &icb_addr_c);
+ &base_addr_y, &base_addr_c);
if (ret)
return ret;
+ }
- lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y);
- if (icb_addr_c)
- lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c);
+ ch->base_addr_y = base_addr_y;
+ ch->base_addr_c = base_addr_c;
- }
+ lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
+ if (info->var.nonstd)
+ lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
if (lcdc_chan_is_sublcd(ch))
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
@@ -985,9 +944,11 @@ static int sh_mobile_wait_for_vsync(struct fb_info *info)
unsigned long ldintr;
int ret;
- /* Enable VSync End interrupt */
+ /* Enable VSync End interrupt and be careful not to acknowledge any
+ * pending interrupt.
+ */
ldintr = lcdc_read(ch->lcdc, _LDINTR);
- ldintr |= LDINTR_VEE;
+ ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
lcdc_write(ch->lcdc, _LDINTR, ldintr);
ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
@@ -1037,11 +998,6 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
/* Couldn't reconfigure, hopefully, can continue as before */
return;
- if (info->var.nonstd)
- info->fix.line_length = mode1.xres;
- else
- info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
-
/*
* fb_set_var() calls the notifier change internally, only if
* FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
@@ -1094,30 +1050,126 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *p = ch->lcdc;
+ unsigned int best_dist = (unsigned int)-1;
+ unsigned int best_xres = 0;
+ unsigned int best_yres = 0;
+ unsigned int i;
- if (var->xres > MAX_XRES || var->yres > MAX_YRES ||
- var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
- dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n",
- var->left_margin, var->xres, var->right_margin, var->hsync_len,
- var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
- PICOS2KHZ(var->pixclock));
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
return -EINVAL;
+
+ /* 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
+ * distance between two modes is defined as the size of the
+ * non-overlapping parts of the two rectangles.
+ */
+ for (i = 0; i < ch->cfg.num_cfg; ++i) {
+ const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i];
+ unsigned int dist;
+
+ /* We can only round up. */
+ if (var->xres > mode->xres || var->yres > mode->yres)
+ continue;
+
+ dist = var->xres * var->yres + mode->xres * mode->yres
+ - 2 * min(var->xres, mode->xres)
+ * min(var->yres, mode->yres);
+
+ if (dist < best_dist) {
+ best_xres = mode->xres;
+ best_yres = mode->yres;
+ best_dist = dist;
+ }
+ }
+
+ /* If no available mode can be used, return an error. */
+ if (ch->cfg.num_cfg != 0) {
+ if (best_dist == (unsigned int)-1)
+ return -EINVAL;
+
+ var->xres = best_xres;
+ 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 (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;
+
/* only accept the forced_bpp for dual channel configurations */
if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel)
return -EINVAL;
- switch (var->bits_per_pixel) {
- case 16: /* PKF[4:0] = 00011 - RGB 565 */
- case 24: /* PKF[4:0] = 01011 - RGB 888 */
- case 32: /* PKF[4:0] = 00000 - RGBA 888 */
- break;
- default:
- return -EINVAL;
+ return 0;
+}
+
+static int sh_mobile_set_par(struct fb_info *info)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ u32 line_length = info->fix.line_length;
+ int ret;
+
+ sh_mobile_lcdc_stop(ch->lcdc);
+
+ if (info->var.nonstd)
+ info->fix.line_length = info->var.xres;
+ else
+ info->fix.line_length = info->var.xres
+ * info->var.bits_per_pixel / 8;
+
+ ret = sh_mobile_lcdc_start(ch->lcdc);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: unable to restart LCDC\n", __func__);
+ info->fix.line_length = line_length;
}
- return 0;
+ return ret;
}
/*
@@ -1177,6 +1229,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_open = sh_mobile_open,
.fb_release = sh_mobile_release,
.fb_check_var = sh_mobile_check_var,
+ .fb_set_par = sh_mobile_set_par,
};
static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
@@ -1238,66 +1291,6 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
backlight_device_unregister(bdev);
}
-static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp,
- int nonstd)
-{
- if (nonstd) {
- switch (bpp) {
- case 12:
- case 16:
- case 24:
- var->bits_per_pixel = bpp;
- var->nonstd = nonstd;
- return 0;
- default:
- return -EINVAL;
- }
- }
-
- switch (bpp) {
- case 16: /* PKF[4:0] = 00011 - RGB 565 */
- 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;
- break;
-
- case 24: /* PKF[4:0] = 01011 - RGB 888 */
- 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;
- break;
-
- case 32: /* PKF[4:0] = 00000 - RGBA 888 */
- 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;
- break;
- default:
- return -EINVAL;
- }
- var->bits_per_pixel = bpp;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- var->transp.msb_right = 0;
- return 0;
-}
-
static int sh_mobile_lcdc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1316,47 +1309,20 @@ static int sh_mobile_lcdc_resume(struct device *dev)
static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
- struct sh_mobile_lcdc_chan *ch;
- int k, n;
-
- /* save per-channel registers */
- for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
- ch = &p->ch[k];
- if (!ch->enabled)
- continue;
- for (n = 0; n < NR_CH_REGS; n++)
- ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
- }
-
- /* save shared registers */
- for (n = 0; n < NR_SHARED_REGS; n++)
- p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);
+ struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
/* turn off LCDC hardware */
- lcdc_write(p, _LDCNT1R, 0);
+ lcdc_write(priv, _LDCNT1R, 0);
+
return 0;
}
static int sh_mobile_lcdc_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
- struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
- struct sh_mobile_lcdc_chan *ch;
- int k, n;
-
- /* restore per-channel registers */
- for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
- ch = &p->ch[k];
- if (!ch->enabled)
- continue;
- for (n = 0; n < NR_CH_REGS; n++)
- lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
- }
+ struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- /* restore shared registers */
- for (n = 0; n < NR_SHARED_REGS; n++)
- lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
+ __sh_mobile_lcdc_start(priv);
return 0;
}
@@ -1408,17 +1374,187 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
return NOTIFY_OK;
}
-static int sh_mobile_lcdc_remove(struct platform_device *pdev);
+static int sh_mobile_lcdc_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
+ struct fb_info *info;
+ int i;
-static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
+ fb_unregister_client(&priv->notifier);
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
+ if (priv->ch[i].info && priv->ch[i].info->dev)
+ unregister_framebuffer(priv->ch[i].info);
+
+ sh_mobile_lcdc_stop(priv);
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
+ info = priv->ch[i].info;
+
+ if (!info || !info->device)
+ continue;
+
+ if (priv->ch[i].sglist)
+ vfree(priv->ch[i].sglist);
+
+ if (info->screen_base)
+ dma_free_coherent(&pdev->dev, info->fix.smem_len,
+ info->screen_base,
+ priv->ch[i].dma_handle);
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
+ if (priv->ch[i].bl)
+ sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
+ }
+
+ if (priv->dot_clk)
+ clk_put(priv->dot_clk);
+
+ if (priv->dev)
+ pm_runtime_disable(priv->dev);
+
+ if (priv->base)
+ iounmap(priv->base);
+
+ if (priv->irq)
+ free_irq(priv->irq, priv);
+ kfree(priv);
+ return 0;
+}
+
+static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
+ struct device *dev)
{
+ struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
+ const struct fb_videomode *max_mode;
+ const struct fb_videomode *mode;
+ struct fb_var_screeninfo *var;
struct fb_info *info;
- struct sh_mobile_lcdc_priv *priv;
+ unsigned int max_size;
+ int num_cfg;
+ void *buf;
+ int ret;
+ int i;
+
+ mutex_init(&ch->open_lock);
+
+ /* Allocate the frame buffer device. */
+ ch->info = framebuffer_alloc(0, dev);
+ if (!ch->info) {
+ dev_err(dev, "unable to allocate fb_info\n");
+ return -ENOMEM;
+ }
+
+ info = ch->info;
+ info->fbops = &sh_mobile_lcdc_ops;
+ info->par = ch;
+ info->pseudo_palette = &ch->pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ /* Iterate through the modes to validate them and find the highest
+ * resolution.
+ */
+ max_mode = NULL;
+ max_size = 0;
+
+ for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) {
+ unsigned int size = mode->yres * mode->xres;
+
+ /* NV12 buffers must have even number of lines */
+ if ((cfg->nonstd) && cfg->bpp == 12 &&
+ (mode->yres & 0x1)) {
+ dev_err(dev, "yres must be multiple of 2 for YCbCr420 "
+ "mode.\n");
+ return -EINVAL;
+ }
+
+ if (size > max_size) {
+ max_mode = mode;
+ max_size = size;
+ }
+ }
+
+ if (!max_size)
+ max_size = MAX_XRES * MAX_YRES;
+ else
+ dev_dbg(dev, "Found largest videomode %ux%u\n",
+ max_mode->xres, max_mode->yres);
+
+ /* Initialize fixed screen information. Restrict pan to 2 lines steps
+ * for NV12.
+ */
+ info->fix = sh_mobile_lcdc_fix;
+ info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
+ if (cfg->nonstd && cfg->bpp == 12)
+ info->fix.ypanstep = 2;
+
+ /* Create the mode list. */
+ if (cfg->lcd_cfg == NULL) {
+ mode = &default_720p;
+ num_cfg = 1;
+ } else {
+ mode = cfg->lcd_cfg;
+ num_cfg = cfg->num_cfg;
+ }
+
+ fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
+
+ /* 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.
+ */
+ var = &info->var;
+ fb_videomode_to_var(var, mode);
+ var->bits_per_pixel = cfg->bpp;
+ var->width = cfg->lcd_size_cfg.width;
+ var->height = cfg->lcd_size_cfg.height;
+ var->yres_virtual = var->yres * 2;
+ var->activate = FB_ACTIVATE_NOW;
+
+ ret = sh_mobile_check_var(var, info);
+ if (ret)
+ return ret;
+
+ /* Allocate frame buffer memory and color map. */
+ buf = dma_alloc_coherent(dev, info->fix.smem_len, &ch->dma_handle,
+ GFP_KERNEL);
+ if (!buf) {
+ dev_err(dev, "unable to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
+ if (ret < 0) {
+ dev_err(dev, "unable to allocate cmap\n");
+ dma_free_coherent(dev, info->fix.smem_len,
+ buf, ch->dma_handle);
+ return ret;
+ }
+
+ info->fix.smem_start = ch->dma_handle;
+ if (var->nonstd)
+ info->fix.line_length = var->xres;
+ else
+ info->fix.line_length = var->xres * (cfg->bpp / 8);
+
+ info->screen_base = buf;
+ info->device = dev;
+ ch->display_var = *var;
+
+ return 0;
+}
+
+static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
+{
struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data;
+ struct sh_mobile_lcdc_priv *priv;
struct resource *res;
+ int num_channels;
int error;
- void *buf;
- int i, j;
+ int i;
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
@@ -1440,7 +1576,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
+ error = request_irq(i, sh_mobile_lcdc_irq, 0,
dev_name(&pdev->dev), priv);
if (error) {
dev_err(&pdev->dev, "unable to request irq\n");
@@ -1450,9 +1586,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
priv->irq = i;
atomic_set(&priv->hw_usecnt, -1);
- j = 0;
- for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
- struct sh_mobile_lcdc_chan *ch = priv->ch + j;
+ for (i = 0, num_channels = 0; i < ARRAY_SIZE(pdata->ch); i++) {
+ struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels;
ch->lcdc = priv;
memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
@@ -1472,26 +1607,26 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
switch (pdata->ch[i].chan) {
case LCDC_CHAN_MAINLCD:
- ch->enabled = 1 << 1;
+ ch->enabled = LDCNT2R_ME;
ch->reg_offs = lcdc_offs_mainlcd;
- j++;
+ num_channels++;
break;
case LCDC_CHAN_SUBLCD:
- ch->enabled = 1 << 2;
+ ch->enabled = LDCNT2R_SE;
ch->reg_offs = lcdc_offs_sublcd;
- j++;
+ num_channels++;
break;
}
}
- if (!j) {
+ if (!num_channels) {
dev_err(&pdev->dev, "no channels defined\n");
error = -EINVAL;
goto err1;
}
/* for dual channel LCDC (MAIN + SUB) force shared bpp setting */
- if (j == 2)
+ if (num_channels == 2)
priv->forced_bpp = pdata->ch[0].bpp;
priv->base = ioremap_nocache(res->start, resource_size(res));
@@ -1506,125 +1641,23 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
priv->meram_dev = pdata->meram_dev;
- for (i = 0; i < j; i++) {
- struct fb_var_screeninfo *var;
- const struct fb_videomode *lcd_cfg, *max_cfg = NULL;
+ for (i = 0; i < num_channels; i++) {
struct sh_mobile_lcdc_chan *ch = priv->ch + i;
- struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
- const struct fb_videomode *mode = cfg->lcd_cfg;
- unsigned long max_size = 0;
- int k;
- int num_cfg;
-
- ch->info = framebuffer_alloc(0, &pdev->dev);
- if (!ch->info) {
- dev_err(&pdev->dev, "unable to allocate fb_info\n");
- error = -ENOMEM;
- break;
- }
-
- info = ch->info;
- var = &info->var;
- info->fbops = &sh_mobile_lcdc_ops;
- info->par = ch;
-
- mutex_init(&ch->open_lock);
-
- for (k = 0, lcd_cfg = mode;
- k < cfg->num_cfg && lcd_cfg;
- k++, lcd_cfg++) {
- unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
- /* NV12 buffers must have even number of lines */
- if ((cfg->nonstd) && cfg->bpp == 12 &&
- (lcd_cfg->yres & 0x1)) {
- dev_err(&pdev->dev, "yres must be multiple of 2"
- " for YCbCr420 mode.\n");
- error = -EINVAL;
- goto err1;
- }
-
- if (size > max_size) {
- max_cfg = lcd_cfg;
- max_size = size;
- }
- }
-
- if (!mode)
- max_size = MAX_XRES * MAX_YRES;
- else if (max_cfg)
- dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
- max_cfg->xres, max_cfg->yres);
- info->fix = sh_mobile_lcdc_fix;
- info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
-
- /* Only pan in 2 line steps for NV12 */
- if (cfg->nonstd && cfg->bpp == 12)
- info->fix.ypanstep = 2;
-
- if (!mode) {
- mode = &default_720p;
- num_cfg = 1;
- } else {
- num_cfg = cfg->num_cfg;
- }
-
- fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
-
- fb_videomode_to_var(var, mode);
- var->width = cfg->lcd_size_cfg.width;
- var->height = cfg->lcd_size_cfg.height;
- /* Default Y virtual resolution is 2x panel size */
- var->yres_virtual = var->yres * 2;
- var->activate = FB_ACTIVATE_NOW;
-
- error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd);
+ error = sh_mobile_lcdc_channel_init(ch, &pdev->dev);
if (error)
- break;
-
- buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
- &ch->dma_handle, GFP_KERNEL);
- if (!buf) {
- dev_err(&pdev->dev, "unable to allocate buffer\n");
- error = -ENOMEM;
- break;
- }
-
- info->pseudo_palette = &ch->pseudo_palette;
- info->flags = FBINFO_FLAG_DEFAULT;
-
- error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
- if (error < 0) {
- dev_err(&pdev->dev, "unable to allocate cmap\n");
- dma_free_coherent(&pdev->dev, info->fix.smem_len,
- buf, ch->dma_handle);
- break;
- }
-
- info->fix.smem_start = ch->dma_handle;
- if (var->nonstd)
- info->fix.line_length = var->xres;
- else
- info->fix.line_length = var->xres * (cfg->bpp / 8);
-
- info->screen_base = buf;
- info->device = &pdev->dev;
- ch->display_var = *var;
+ goto err1;
}
- if (error)
- goto err1;
-
error = sh_mobile_lcdc_start(priv);
if (error) {
dev_err(&pdev->dev, "unable to start hardware\n");
goto err1;
}
- for (i = 0; i < j; i++) {
+ for (i = 0; i < num_channels; i++) {
struct sh_mobile_lcdc_chan *ch = priv->ch + i;
-
- info = ch->info;
+ struct fb_info *info = ch->info;
if (info->fbdefio) {
ch->sglist = vmalloc(sizeof(struct scatterlist) *
@@ -1665,57 +1698,6 @@ err1:
return error;
}
-static int sh_mobile_lcdc_remove(struct platform_device *pdev)
-{
- struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- struct fb_info *info;
- int i;
-
- fb_unregister_client(&priv->notifier);
-
- for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
- if (priv->ch[i].info && priv->ch[i].info->dev)
- unregister_framebuffer(priv->ch[i].info);
-
- sh_mobile_lcdc_stop(priv);
-
- for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
- info = priv->ch[i].info;
-
- if (!info || !info->device)
- continue;
-
- if (priv->ch[i].sglist)
- vfree(priv->ch[i].sglist);
-
- if (info->screen_base)
- dma_free_coherent(&pdev->dev, info->fix.smem_len,
- info->screen_base,
- priv->ch[i].dma_handle);
- fb_dealloc_cmap(&info->cmap);
- framebuffer_release(info);
- }
-
- for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
- if (priv->ch[i].bl)
- sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
- }
-
- if (priv->dot_clk)
- clk_put(priv->dot_clk);
-
- if (priv->dev)
- pm_runtime_disable(priv->dev);
-
- if (priv->base)
- iounmap(priv->base);
-
- if (priv->irq)
- free_irq(priv->irq, priv);
- kfree(priv);
- return 0;
-}
-
static struct platform_driver sh_mobile_lcdc_driver = {
.driver = {
.name = "sh_mobile_lcdc_fb",
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index aeed6687e6a7..a58a0f38848b 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv;
struct fb_info;
struct backlight_device;
+/*
+ * struct sh_mobile_lcdc_chan - LCDC display channel
+ *
+ * @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
+ */
struct sh_mobile_lcdc_chan {
struct sh_mobile_lcdc_priv *lcdc;
unsigned long *reg_offs;
@@ -25,7 +32,6 @@ struct sh_mobile_lcdc_chan {
unsigned long enabled; /* ME and SE in LDCNT2R */
struct sh_mobile_lcdc_chan_cfg cfg;
u32 pseudo_palette[PALETTE_NR];
- unsigned long saved_ch_regs[NR_CH_REGS];
struct fb_info *info;
struct backlight_device *bl;
dma_addr_t dma_handle;
@@ -40,6 +46,10 @@ struct sh_mobile_lcdc_chan {
int blank_status;
struct mutex open_lock; /* protects the use counter */
int meram_enabled;
+
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned int pitch;
};
#endif
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index cc7d7329dc15..4d63490209cd 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -12,29 +12,103 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
-
-#include "sh_mobile_meram.h"
+#include <video/sh_mobile_meram.h>
/* meram registers */
-#define MExxCTL 0x0
-#define MExxBSIZE 0x4
-#define MExxMNCF 0x8
-#define MExxSARA 0x10
-#define MExxSARB 0x14
-#define MExxSBSIZE 0x18
-
-#define MERAM_MExxCTL_VAL(ctl, next_icb, addr) \
- ((ctl) | (((next_icb) & 0x1f) << 11) | (((addr) & 0x7ff) << 16))
-#define MERAM_MExxBSIZE_VAL(a, b, c) \
- (((a) << 28) | ((b) << 16) | (c))
-
-#define MEVCR1 0x4
-#define MEACTS 0x10
-#define MEQSEL1 0x40
-#define MEQSEL2 0x44
+#define MEVCR1 0x4
+#define MEVCR1_RST (1 << 31)
+#define MEVCR1_WD (1 << 30)
+#define MEVCR1_AMD1 (1 << 29)
+#define MEVCR1_AMD0 (1 << 28)
+#define MEQSEL1 0x40
+#define MEQSEL2 0x44
+
+#define MExxCTL 0x400
+#define MExxCTL_BV (1 << 31)
+#define MExxCTL_BSZ_SHIFT 28
+#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
+#define MExxCTL_MSAR_SHIFT 16
+#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
+#define MExxCTL_NXT_SHIFT 11
+#define MExxCTL_WD1 (1 << 10)
+#define MExxCTL_WD0 (1 << 9)
+#define MExxCTL_WS (1 << 8)
+#define MExxCTL_CB (1 << 7)
+#define MExxCTL_WBF (1 << 6)
+#define MExxCTL_WF (1 << 5)
+#define MExxCTL_RF (1 << 4)
+#define MExxCTL_CM (1 << 3)
+#define MExxCTL_MD_READ (1 << 0)
+#define MExxCTL_MD_WRITE (2 << 0)
+#define MExxCTL_MD_ICB_WB (3 << 0)
+#define MExxCTL_MD_ICB (4 << 0)
+#define MExxCTL_MD_FB (7 << 0)
+#define MExxCTL_MD_MASK (7 << 0)
+#define MExxBSIZE 0x404
+#define MExxBSIZE_RCNT_SHIFT 28
+#define MExxBSIZE_YSZM1_SHIFT 16
+#define MExxBSIZE_XSZM1_SHIFT 0
+#define MExxMNCF 0x408
+#define MExxMNCF_KWBNM_SHIFT 28
+#define MExxMNCF_KRBNM_SHIFT 24
+#define MExxMNCF_BNM_SHIFT 16
+#define MExxMNCF_XBV (1 << 15)
+#define MExxMNCF_CPL_YCBCR444 (1 << 12)
+#define MExxMNCF_CPL_YCBCR420 (2 << 12)
+#define MExxMNCF_CPL_YCBCR422 (3 << 12)
+#define MExxMNCF_CPL_MSK (3 << 12)
+#define MExxMNCF_BL (1 << 2)
+#define MExxMNCF_LNM_SHIFT 0
+#define MExxSARA 0x410
+#define MExxSARB 0x414
+#define MExxSBSIZE 0x418
+#define MExxSBSIZE_HDV (1 << 31)
+#define MExxSBSIZE_HSZ16 (0 << 28)
+#define MExxSBSIZE_HSZ32 (1 << 28)
+#define MExxSBSIZE_HSZ64 (2 << 28)
+#define MExxSBSIZE_HSZ128 (3 << 28)
+#define MExxSBSIZE_SBSIZZ_SHIFT 0
+
+#define MERAM_MExxCTL_VAL(next, addr) \
+ ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
+ (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
+#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
+ (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
+ ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
+ ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
+
+#define SH_MOBILE_MERAM_ICB_NUM 32
+
+static unsigned long common_regs[] = {
+ MEVCR1,
+ MEQSEL1,
+ MEQSEL2,
+};
+#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
+
+static unsigned long icb_regs[] = {
+ MExxCTL,
+ MExxBSIZE,
+ MExxMNCF,
+ MExxSARA,
+ MExxSARB,
+ MExxSBSIZE,
+};
+#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
+
+struct sh_mobile_meram_priv {
+ void __iomem *base;
+ struct mutex lock;
+ unsigned long used_icb;
+ int used_meram_cache_regions;
+ unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
+ unsigned long cmn_saved_regs[CMN_REGS_SIZE];
+ unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
+};
/* settings */
#define MERAM_SEC_LINE 15
@@ -44,8 +118,7 @@
* MERAM/ICB access functions
*/
-#define MERAM_ICB_OFFSET(base, idx, off) \
- ((base) + (0x400 + ((idx) * 0x20) + (off)))
+#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
static inline void meram_write_icb(void __iomem *base, int idx, int off,
unsigned long val)
@@ -280,17 +353,18 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
/*
* Set MERAM for framebuffer
*
- * 0x70f: WD = 0x3, WS=0x1, CM=0x1, MD=FB mode
* we also chain the cache_icb and the marker_icb.
* we also split the allocated MERAM buffer between two ICBs.
*/
meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
- MERAM_MExxCTL_VAL(0x70f, icb->marker_icb,
- icb->meram_offset));
+ MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
+ MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+ MExxCTL_MD_FB);
meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
- MERAM_MExxCTL_VAL(0x70f, icb->cache_icb,
- icb->meram_offset +
- icb->meram_size / 2));
+ MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
+ icb->meram_size / 2) |
+ MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+ MExxCTL_MD_FB);
return 0;
}
@@ -299,8 +373,10 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb *icb)
{
/* disable ICB */
- meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 0);
- meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 0);
+ meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
+ MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
+ meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
+ MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
icb->cache_unit = 0;
}
@@ -337,24 +413,22 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
xres, yres, (!pixelformat) ? "yuv" : "rgb",
base_addr_y, base_addr_c);
- mutex_lock(&priv->lock);
-
/* we can't handle wider than 8192px */
if (xres > 8192) {
dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
- error = -EINVAL;
- goto err;
- }
-
- if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
- dev_err(&pdev->dev, "no more ICB available.");
- error = -EINVAL;
- goto err;
+ return -EINVAL;
}
/* do we have at least one ICB config? */
if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
dev_err(&pdev->dev, "at least one ICB is required.");
+ return -EINVAL;
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
+ dev_err(&pdev->dev, "no more ICB available.");
error = -EINVAL;
goto err;
}
@@ -460,6 +534,57 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
return 0;
}
+static int sh_mobile_meram_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
+ int k, j;
+
+ for (k = 0; k < CMN_REGS_SIZE; k++)
+ priv->cmn_saved_regs[k] = meram_read_reg(priv->base,
+ common_regs[k]);
+
+ for (j = 0; j < 32; j++) {
+ if (!test_bit(j, &priv->used_icb))
+ continue;
+ for (k = 0; k < ICB_REGS_SIZE; k++) {
+ priv->icb_saved_regs[j * ICB_REGS_SIZE + k] =
+ meram_read_icb(priv->base, j, icb_regs[k]);
+ /* Reset ICB on resume */
+ if (icb_regs[k] == MExxCTL)
+ priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |=
+ MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
+ }
+ }
+ return 0;
+}
+
+static int sh_mobile_meram_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
+ int k, j;
+
+ for (j = 0; j < 32; j++) {
+ if (!test_bit(j, &priv->used_icb))
+ continue;
+ for (k = 0; k < ICB_REGS_SIZE; k++) {
+ meram_write_icb(priv->base, j, icb_regs[k],
+ priv->icb_saved_regs[j * ICB_REGS_SIZE + k]);
+ }
+ }
+
+ for (k = 0; k < CMN_REGS_SIZE; k++)
+ meram_write_reg(priv->base, common_regs[k],
+ priv->cmn_saved_regs[k]);
+ return 0;
+}
+
+static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
+ .runtime_suspend = sh_mobile_meram_runtime_suspend,
+ .runtime_resume = sh_mobile_meram_runtime_resume,
+};
+
static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
.module = THIS_MODULE,
.meram_register = sh_mobile_meram_register,
@@ -513,7 +638,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
/* initialize ICB addressing mode */
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
- meram_write_reg(priv->base, MEVCR1, 1 << 29);
+ meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
+
+ pm_runtime_enable(&pdev->dev);
dev_info(&pdev->dev, "sh_mobile_meram initialized.");
@@ -530,6 +657,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev)
{
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+
if (priv->base)
iounmap(priv->base);
@@ -544,6 +673,7 @@ static struct platform_driver sh_mobile_meram_driver = {
.driver = {
.name = "sh_mobile_meram",
.owner = THIS_MODULE,
+ .pm = &sh_mobile_meram_dev_pm_ops,
},
.probe = sh_mobile_meram_probe,
.remove = sh_mobile_meram_remove,
diff --git a/drivers/video/sh_mobile_meram.h b/drivers/video/sh_mobile_meram.h
deleted file mode 100644
index 82c54fbce8bd..000000000000
--- a/drivers/video/sh_mobile_meram.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef __sh_mobile_meram_h__
-#define __sh_mobile_meram_h__
-
-#include <linux/mutex.h>
-#include <video/sh_mobile_meram.h>
-
-/*
- * MERAM private
- */
-
-#define MERAM_ICB_Y 0x1
-#define MERAM_ICB_C 0x2
-
-/* MERAM cache size */
-#define SH_MOBILE_MERAM_ICB_NUM 32
-
-#define SH_MOBILE_MERAM_CACHE_OFFSET(p) ((p) >> 16)
-#define SH_MOBILE_MERAM_CACHE_SIZE(p) ((p) & 0xffff)
-
-struct sh_mobile_meram_priv {
- void __iomem *base;
- struct mutex lock;
- unsigned long used_icb;
- int used_meram_cache_regions;
- unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
-};
-
-int sh_mobile_meram_alloc_icb(const struct sh_mobile_meram_cfg *cfg,
- int xres,
- int yres,
- unsigned int base_addr,
- int yuv_mode,
- int *marker_icb,
- int *out_pitch);
-
-void sh_mobile_meram_free_icb(int marker_icb);
-
-#define SH_MOBILE_MERAM_START(ind, ab) \
- (0xC0000000 | ((ab & 0x1) << 23) | ((ind & 0x1F) << 24))
-
-#endif /* !__sh_mobile_meram_h__ */
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index 75259845933d..078ca2167d6f 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -1333,19 +1333,14 @@ sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base)
}
static int
-sisfb_pan_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
+sisfb_pan_var(struct sis_video_info *ivideo, struct fb_info *info,
+ struct fb_var_screeninfo *var)
{
- if(var->xoffset > (var->xres_virtual - var->xres)) {
- return -EINVAL;
- }
- if(var->yoffset > (var->yres_virtual - var->yres)) {
- return -EINVAL;
- }
-
- ivideo->current_base = (var->yoffset * var->xres_virtual) + var->xoffset;
+ ivideo->current_base = var->yoffset * info->var.xres_virtual
+ + var->xoffset;
/* calculate base bpp dep. */
- switch(var->bits_per_pixel) {
+ switch (info->var.bits_per_pixel) {
case 32:
break;
case 16:
@@ -1635,20 +1630,15 @@ sisfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info)
struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
int err;
- if(var->xoffset > (var->xres_virtual - var->xres))
- return -EINVAL;
-
- if(var->yoffset > (var->yres_virtual - var->yres))
- return -EINVAL;
-
- if(var->vmode & FB_VMODE_YWRAP)
+ if (var->vmode & FB_VMODE_YWRAP)
return -EINVAL;
- if(var->xoffset + info->var.xres > info->var.xres_virtual ||
- var->yoffset + info->var.yres > info->var.yres_virtual)
+ if (var->xoffset + info->var.xres > info->var.xres_virtual ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
- if((err = sisfb_pan_var(ivideo, var)) < 0)
+ err = sisfb_pan_var(ivideo, info, var);
+ if (err < 0)
return err;
info->var.xoffset = var->xoffset;
diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c
index 89158bc71da2..30f7a815a62b 100644
--- a/drivers/video/skeletonfb.c
+++ b/drivers/video/skeletonfb.c
@@ -989,7 +989,7 @@ static struct platform_device *xxxfb_device;
*/
int __init xxxfb_setup(char *options)
{
- /* Parse user speficied options (`video=xxxfb:') */
+ /* Parse user specified options (`video=xxxfb:') */
}
#endif /* MODULE */
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c
index 6294dca95500..a78254cf8e83 100644
--- a/drivers/video/sm501fb.c
+++ b/drivers/video/sm501fb.c
@@ -582,7 +582,7 @@ static int sm501fb_pan_crt(struct fb_var_screeninfo *var,
{
struct sm501fb_par *par = info->par;
struct sm501fb_info *fbi = par->info;
- unsigned int bytes_pixel = var->bits_per_pixel / 8;
+ unsigned int bytes_pixel = info->var.bits_per_pixel / 8;
unsigned long reg;
unsigned long xoffs;
@@ -614,10 +614,10 @@ static int sm501fb_pan_pnl(struct fb_var_screeninfo *var,
struct sm501fb_info *fbi = par->info;
unsigned long reg;
- reg = var->xoffset | (var->xres_virtual << 16);
+ reg = var->xoffset | (info->var.xres_virtual << 16);
smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
- reg = var->yoffset | (var->yres_virtual << 16);
+ reg = var->yoffset | (info->var.yres_virtual << 16);
smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
sm501fb_sync_regs(fbi);
diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c
new file mode 100644
index 000000000000..aaccffac67ab
--- /dev/null
+++ b/drivers/video/smscufx.c
@@ -0,0 +1,1994 @@
+/*
+ * smscufx.c -- Framebuffer driver for SMSC UFX USB controller
+ *
+ * Copyright (C) 2011 Steve Glendinning <steve.glendinning@smsc.com>
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Based on udlfb, with work from Florian Echtler, Henrik Bjerregaard Pedersen,
+ * and others.
+ *
+ * Works well with Bernie Thompson's X DAMAGE patch to xf86-video-fbdev
+ * available from http://git.plugable.com
+ *
+ * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven,
+ * usb-skeleton by GregKH.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "edid.h"
+
+#define check_warn(status, fmt, args...) \
+ ({ if (status < 0) pr_warn(fmt, ##args); })
+
+#define check_warn_return(status, fmt, args...) \
+ ({ if (status < 0) { pr_warn(fmt, ##args); return status; } })
+
+#define check_warn_goto_error(status, fmt, args...) \
+ ({ if (status < 0) { pr_warn(fmt, ##args); goto error; } })
+
+#define all_bits_set(x, bits) (((x) & (bits)) == (bits))
+
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+
+/*
+ * TODO: Propose standard fb.h ioctl for reporting damage,
+ * using _IOWR() and one of the existing area structs from fb.h
+ * Consider these ioctls deprecated, but they're still used by the
+ * DisplayLink X server as yet - need both to be modified in tandem
+ * when new ioctl(s) are ready.
+ */
+#define UFX_IOCTL_RETURN_EDID (0xAD)
+#define UFX_IOCTL_REPORT_DAMAGE (0xAA)
+
+/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */
+#define BULK_SIZE (512)
+#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE)
+#define WRITES_IN_FLIGHT (4)
+
+#define GET_URB_TIMEOUT (HZ)
+#define FREE_URB_TIMEOUT (HZ*2)
+
+#define BPP 2
+
+#define UFX_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */
+#define UFX_DEFIO_WRITE_DISABLE (HZ*60) /* "disable" with long delay */
+
+struct dloarea {
+ int x, y;
+ int w, h;
+};
+
+struct urb_node {
+ struct list_head entry;
+ struct ufx_data *dev;
+ struct delayed_work release_urb_work;
+ struct urb *urb;
+};
+
+struct urb_list {
+ struct list_head list;
+ spinlock_t lock;
+ struct semaphore limit_sem;
+ int available;
+ int count;
+ size_t size;
+};
+
+struct ufx_data {
+ struct usb_device *udev;
+ struct device *gdev; /* &udev->dev */
+ struct fb_info *info;
+ struct urb_list urbs;
+ struct kref kref;
+ int fb_count;
+ bool virtualized; /* true when physical usb device not present */
+ struct delayed_work free_framebuffer_work;
+ atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
+ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
+ u8 *edid; /* null until we read edid from hw or get from sysfs */
+ size_t edid_size;
+ u32 pseudo_palette[256];
+};
+
+static struct fb_fix_screeninfo ufx_fix = {
+ .id = "smscufx",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const u32 smscufx_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
+ FBINFO_VIRTFB | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT |
+ FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR;
+
+static struct usb_device_id id_table[] = {
+ {USB_DEVICE(0x0424, 0x9d00),},
+ {USB_DEVICE(0x0424, 0x9d01),},
+ {},
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* module options */
+static int console; /* Optionally allow fbcon to consume first framebuffer */
+static int fb_defio = true; /* Optionally enable fb_defio mmap support */
+
+/* ufx keeps a list of urbs for efficient bulk transfers */
+static void ufx_urb_completion(struct urb *urb);
+static struct urb *ufx_get_urb(struct ufx_data *dev);
+static int ufx_submit_urb(struct ufx_data *dev, struct urb * urb, size_t len);
+static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size);
+static void ufx_free_urb_list(struct ufx_data *dev);
+
+/* reads a control register */
+static int ufx_reg_read(struct ufx_data *dev, u32 index, u32 *data)
+{
+ u32 *buf = kmalloc(4, GFP_KERNEL);
+ int ret;
+
+ BUG_ON(!dev);
+
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, buf, 4, USB_CTRL_GET_TIMEOUT);
+
+ le32_to_cpus(buf);
+ *data = *buf;
+ kfree(buf);
+
+ if (unlikely(ret < 0))
+ pr_warn("Failed to read register index 0x%08x\n", index);
+
+ return ret;
+}
+
+/* writes a control register */
+static int ufx_reg_write(struct ufx_data *dev, u32 index, u32 data)
+{
+ u32 *buf = kmalloc(4, GFP_KERNEL);
+ int ret;
+
+ BUG_ON(!dev);
+
+ if (!buf)
+ return -ENOMEM;
+
+ *buf = data;
+ cpu_to_le32s(buf);
+
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, buf, 4, USB_CTRL_SET_TIMEOUT);
+
+ kfree(buf);
+
+ if (unlikely(ret < 0))
+ pr_warn("Failed to write register index 0x%08x with value "
+ "0x%08x\n", index, data);
+
+ return ret;
+}
+
+static int ufx_reg_clear_and_set_bits(struct ufx_data *dev, u32 index,
+ u32 bits_to_clear, u32 bits_to_set)
+{
+ u32 data;
+ int status = ufx_reg_read(dev, index, &data);
+ check_warn_return(status, "ufx_reg_clear_and_set_bits error reading "
+ "0x%x", index);
+
+ data &= (~bits_to_clear);
+ data |= bits_to_set;
+
+ status = ufx_reg_write(dev, index, data);
+ check_warn_return(status, "ufx_reg_clear_and_set_bits error writing "
+ "0x%x", index);
+
+ return 0;
+}
+
+static int ufx_reg_set_bits(struct ufx_data *dev, u32 index, u32 bits)
+{
+ return ufx_reg_clear_and_set_bits(dev, index, 0, bits);
+}
+
+static int ufx_reg_clear_bits(struct ufx_data *dev, u32 index, u32 bits)
+{
+ return ufx_reg_clear_and_set_bits(dev, index, bits, 0);
+}
+
+static int ufx_lite_reset(struct ufx_data *dev)
+{
+ int status;
+ u32 value;
+
+ status = ufx_reg_write(dev, 0x3008, 0x00000001);
+ check_warn_return(status, "ufx_lite_reset error writing 0x3008");
+
+ status = ufx_reg_read(dev, 0x3008, &value);
+ check_warn_return(status, "ufx_lite_reset error reading 0x3008");
+
+ return (value == 0) ? 0 : -EIO;
+}
+
+/* If display is unblanked, then blank it */
+static int ufx_blank(struct ufx_data *dev, bool wait)
+{
+ u32 dc_ctrl, dc_sts;
+ int i;
+
+ int status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_blank error reading 0x2004");
+
+ status = ufx_reg_read(dev, 0x2000, &dc_ctrl);
+ check_warn_return(status, "ufx_blank error reading 0x2000");
+
+ /* return success if display is already blanked */
+ if ((dc_sts & 0x00000100) || (dc_ctrl & 0x00000100))
+ return 0;
+
+ /* request the DC to blank the display */
+ dc_ctrl |= 0x00000100;
+ status = ufx_reg_write(dev, 0x2000, dc_ctrl);
+ check_warn_return(status, "ufx_blank error writing 0x2000");
+
+ /* return success immediately if we don't have to wait */
+ if (!wait)
+ return 0;
+
+ for (i = 0; i < 250; i++) {
+ status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_blank error reading 0x2004");
+
+ if (dc_sts & 0x00000100)
+ return 0;
+ }
+
+ /* timed out waiting for display to blank */
+ return -EIO;
+}
+
+/* If display is blanked, then unblank it */
+static int ufx_unblank(struct ufx_data *dev, bool wait)
+{
+ u32 dc_ctrl, dc_sts;
+ int i;
+
+ int status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_unblank error reading 0x2004");
+
+ status = ufx_reg_read(dev, 0x2000, &dc_ctrl);
+ check_warn_return(status, "ufx_unblank error reading 0x2000");
+
+ /* return success if display is already unblanked */
+ if (((dc_sts & 0x00000100) == 0) || ((dc_ctrl & 0x00000100) == 0))
+ return 0;
+
+ /* request the DC to unblank the display */
+ dc_ctrl &= ~0x00000100;
+ status = ufx_reg_write(dev, 0x2000, dc_ctrl);
+ check_warn_return(status, "ufx_unblank error writing 0x2000");
+
+ /* return success immediately if we don't have to wait */
+ if (!wait)
+ return 0;
+
+ for (i = 0; i < 250; i++) {
+ status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_unblank error reading 0x2004");
+
+ if ((dc_sts & 0x00000100) == 0)
+ return 0;
+ }
+
+ /* timed out waiting for display to unblank */
+ return -EIO;
+}
+
+/* If display is enabled, then disable it */
+static int ufx_disable(struct ufx_data *dev, bool wait)
+{
+ u32 dc_ctrl, dc_sts;
+ int i;
+
+ int status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_disable error reading 0x2004");
+
+ status = ufx_reg_read(dev, 0x2000, &dc_ctrl);
+ check_warn_return(status, "ufx_disable error reading 0x2000");
+
+ /* return success if display is already disabled */
+ if (((dc_sts & 0x00000001) == 0) || ((dc_ctrl & 0x00000001) == 0))
+ return 0;
+
+ /* request the DC to disable the display */
+ dc_ctrl &= ~(0x00000001);
+ status = ufx_reg_write(dev, 0x2000, dc_ctrl);
+ check_warn_return(status, "ufx_disable error writing 0x2000");
+
+ /* return success immediately if we don't have to wait */
+ if (!wait)
+ return 0;
+
+ for (i = 0; i < 250; i++) {
+ status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_disable error reading 0x2004");
+
+ if ((dc_sts & 0x00000001) == 0)
+ return 0;
+ }
+
+ /* timed out waiting for display to disable */
+ return -EIO;
+}
+
+/* If display is disabled, then enable it */
+static int ufx_enable(struct ufx_data *dev, bool wait)
+{
+ u32 dc_ctrl, dc_sts;
+ int i;
+
+ int status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_enable error reading 0x2004");
+
+ status = ufx_reg_read(dev, 0x2000, &dc_ctrl);
+ check_warn_return(status, "ufx_enable error reading 0x2000");
+
+ /* return success if display is already enabled */
+ if ((dc_sts & 0x00000001) || (dc_ctrl & 0x00000001))
+ return 0;
+
+ /* request the DC to enable the display */
+ dc_ctrl |= 0x00000001;
+ status = ufx_reg_write(dev, 0x2000, dc_ctrl);
+ check_warn_return(status, "ufx_enable error writing 0x2000");
+
+ /* return success immediately if we don't have to wait */
+ if (!wait)
+ return 0;
+
+ for (i = 0; i < 250; i++) {
+ status = ufx_reg_read(dev, 0x2004, &dc_sts);
+ check_warn_return(status, "ufx_enable error reading 0x2004");
+
+ if (dc_sts & 0x00000001)
+ return 0;
+ }
+
+ /* timed out waiting for display to enable */
+ return -EIO;
+}
+
+static int ufx_config_sys_clk(struct ufx_data *dev)
+{
+ int status = ufx_reg_write(dev, 0x700C, 0x8000000F);
+ check_warn_return(status, "error writing 0x700C");
+
+ status = ufx_reg_write(dev, 0x7014, 0x0010024F);
+ check_warn_return(status, "error writing 0x7014");
+
+ status = ufx_reg_write(dev, 0x7010, 0x00000000);
+ check_warn_return(status, "error writing 0x7010");
+
+ status = ufx_reg_clear_bits(dev, 0x700C, 0x0000000A);
+ check_warn_return(status, "error clearing PLL1 bypass in 0x700C");
+ msleep(1);
+
+ status = ufx_reg_clear_bits(dev, 0x700C, 0x80000000);
+ check_warn_return(status, "error clearing output gate in 0x700C");
+
+ return 0;
+}
+
+static int ufx_config_ddr2(struct ufx_data *dev)
+{
+ int status, i = 0;
+ u32 tmp;
+
+ status = ufx_reg_write(dev, 0x0004, 0x001F0F77);
+ check_warn_return(status, "error writing 0x0004");
+
+ status = ufx_reg_write(dev, 0x0008, 0xFFF00000);
+ check_warn_return(status, "error writing 0x0008");
+
+ status = ufx_reg_write(dev, 0x000C, 0x0FFF2222);
+ check_warn_return(status, "error writing 0x000C");
+
+ status = ufx_reg_write(dev, 0x0010, 0x00030814);
+ check_warn_return(status, "error writing 0x0010");
+
+ status = ufx_reg_write(dev, 0x0014, 0x00500019);
+ check_warn_return(status, "error writing 0x0014");
+
+ status = ufx_reg_write(dev, 0x0018, 0x020D0F15);
+ check_warn_return(status, "error writing 0x0018");
+
+ status = ufx_reg_write(dev, 0x001C, 0x02532305);
+ check_warn_return(status, "error writing 0x001C");
+
+ status = ufx_reg_write(dev, 0x0020, 0x0B030905);
+ check_warn_return(status, "error writing 0x0020");
+
+ status = ufx_reg_write(dev, 0x0024, 0x00000827);
+ check_warn_return(status, "error writing 0x0024");
+
+ status = ufx_reg_write(dev, 0x0028, 0x00000000);
+ check_warn_return(status, "error writing 0x0028");
+
+ status = ufx_reg_write(dev, 0x002C, 0x00000042);
+ check_warn_return(status, "error writing 0x002C");
+
+ status = ufx_reg_write(dev, 0x0030, 0x09520000);
+ check_warn_return(status, "error writing 0x0030");
+
+ status = ufx_reg_write(dev, 0x0034, 0x02223314);
+ check_warn_return(status, "error writing 0x0034");
+
+ status = ufx_reg_write(dev, 0x0038, 0x00430043);
+ check_warn_return(status, "error writing 0x0038");
+
+ status = ufx_reg_write(dev, 0x003C, 0xF00F000F);
+ check_warn_return(status, "error writing 0x003C");
+
+ status = ufx_reg_write(dev, 0x0040, 0xF380F00F);
+ check_warn_return(status, "error writing 0x0040");
+
+ status = ufx_reg_write(dev, 0x0044, 0xF00F0496);
+ check_warn_return(status, "error writing 0x0044");
+
+ status = ufx_reg_write(dev, 0x0048, 0x03080406);
+ check_warn_return(status, "error writing 0x0048");
+
+ status = ufx_reg_write(dev, 0x004C, 0x00001000);
+ check_warn_return(status, "error writing 0x004C");
+
+ status = ufx_reg_write(dev, 0x005C, 0x00000007);
+ check_warn_return(status, "error writing 0x005C");
+
+ status = ufx_reg_write(dev, 0x0100, 0x54F00012);
+ check_warn_return(status, "error writing 0x0100");
+
+ status = ufx_reg_write(dev, 0x0104, 0x00004012);
+ check_warn_return(status, "error writing 0x0104");
+
+ status = ufx_reg_write(dev, 0x0118, 0x40404040);
+ check_warn_return(status, "error writing 0x0118");
+
+ status = ufx_reg_write(dev, 0x0000, 0x00000001);
+ check_warn_return(status, "error writing 0x0000");
+
+ while (i++ < 500) {
+ status = ufx_reg_read(dev, 0x0000, &tmp);
+ check_warn_return(status, "error reading 0x0000");
+
+ if (all_bits_set(tmp, 0xC0000000))
+ return 0;
+ }
+
+ pr_err("DDR2 initialisation timed out, reg 0x0000=0x%08x", tmp);
+ return -ETIMEDOUT;
+}
+
+struct pll_values {
+ u32 div_r0;
+ u32 div_f0;
+ u32 div_q0;
+ u32 range0;
+ u32 div_r1;
+ u32 div_f1;
+ u32 div_q1;
+ u32 range1;
+};
+
+static u32 ufx_calc_range(u32 ref_freq)
+{
+ if (ref_freq >= 88000000)
+ return 7;
+
+ if (ref_freq >= 54000000)
+ return 6;
+
+ if (ref_freq >= 34000000)
+ return 5;
+
+ if (ref_freq >= 21000000)
+ return 4;
+
+ if (ref_freq >= 13000000)
+ return 3;
+
+ if (ref_freq >= 8000000)
+ return 2;
+
+ return 1;
+}
+
+/* calculates PLL divider settings for a desired target frequency */
+static void ufx_calc_pll_values(const u32 clk_pixel_pll, struct pll_values *asic_pll)
+{
+ const u32 ref_clk = 25000000;
+ u32 div_r0, div_f0, div_q0, div_r1, div_f1, div_q1;
+ u32 min_error = clk_pixel_pll;
+
+ for (div_r0 = 1; div_r0 <= 32; div_r0++) {
+ u32 ref_freq0 = ref_clk / div_r0;
+ if (ref_freq0 < 5000000)
+ break;
+
+ if (ref_freq0 > 200000000)
+ continue;
+
+ for (div_f0 = 1; div_f0 <= 256; div_f0++) {
+ u32 vco_freq0 = ref_freq0 * div_f0;
+
+ if (vco_freq0 < 350000000)
+ continue;
+
+ if (vco_freq0 > 700000000)
+ break;
+
+ for (div_q0 = 0; div_q0 < 7; div_q0++) {
+ u32 pllout_freq0 = vco_freq0 / (1 << div_q0);
+
+ if (pllout_freq0 < 5000000)
+ break;
+
+ if (pllout_freq0 > 200000000)
+ continue;
+
+ for (div_r1 = 1; div_r1 <= 32; div_r1++) {
+ u32 ref_freq1 = pllout_freq0 / div_r1;
+
+ if (ref_freq1 < 5000000)
+ break;
+
+ for (div_f1 = 1; div_f1 <= 256; div_f1++) {
+ u32 vco_freq1 = ref_freq1 * div_f1;
+
+ if (vco_freq1 < 350000000)
+ continue;
+
+ if (vco_freq1 > 700000000)
+ break;
+
+ for (div_q1 = 0; div_q1 < 7; div_q1++) {
+ u32 pllout_freq1 = vco_freq1 / (1 << div_q1);
+ int error = abs(pllout_freq1 - clk_pixel_pll);
+
+ if (pllout_freq1 < 5000000)
+ break;
+
+ if (pllout_freq1 > 700000000)
+ continue;
+
+ if (error < min_error) {
+ min_error = error;
+
+ /* final returned value is equal to calculated value - 1
+ * because a value of 0 = divide by 1 */
+ asic_pll->div_r0 = div_r0 - 1;
+ asic_pll->div_f0 = div_f0 - 1;
+ asic_pll->div_q0 = div_q0;
+ asic_pll->div_r1 = div_r1 - 1;
+ asic_pll->div_f1 = div_f1 - 1;
+ asic_pll->div_q1 = div_q1;
+
+ asic_pll->range0 = ufx_calc_range(ref_freq0);
+ asic_pll->range1 = ufx_calc_range(ref_freq1);
+
+ if (min_error == 0)
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* sets analog bit PLL configuration values */
+static int ufx_config_pix_clk(struct ufx_data *dev, u32 pixclock)
+{
+ struct pll_values asic_pll = {0};
+ u32 value, clk_pixel, clk_pixel_pll;
+ int status;
+
+ /* convert pixclock (in ps) to frequency (in Hz) */
+ clk_pixel = PICOS2KHZ(pixclock) * 1000;
+ pr_debug("pixclock %d ps = clk_pixel %d Hz", pixclock, clk_pixel);
+
+ /* clk_pixel = 1/2 clk_pixel_pll */
+ clk_pixel_pll = clk_pixel * 2;
+
+ ufx_calc_pll_values(clk_pixel_pll, &asic_pll);
+
+ /* Keep BYPASS and RESET signals asserted until configured */
+ status = ufx_reg_write(dev, 0x7000, 0x8000000F);
+ check_warn_return(status, "error writing 0x7000");
+
+ value = (asic_pll.div_f1 | (asic_pll.div_r1 << 8) |
+ (asic_pll.div_q1 << 16) | (asic_pll.range1 << 20));
+ status = ufx_reg_write(dev, 0x7008, value);
+ check_warn_return(status, "error writing 0x7008");
+
+ value = (asic_pll.div_f0 | (asic_pll.div_r0 << 8) |
+ (asic_pll.div_q0 << 16) | (asic_pll.range0 << 20));
+ status = ufx_reg_write(dev, 0x7004, value);
+ check_warn_return(status, "error writing 0x7004");
+
+ status = ufx_reg_clear_bits(dev, 0x7000, 0x00000005);
+ check_warn_return(status,
+ "error clearing PLL0 bypass bits in 0x7000");
+ msleep(1);
+
+ status = ufx_reg_clear_bits(dev, 0x7000, 0x0000000A);
+ check_warn_return(status,
+ "error clearing PLL1 bypass bits in 0x7000");
+ msleep(1);
+
+ status = ufx_reg_clear_bits(dev, 0x7000, 0x80000000);
+ check_warn_return(status, "error clearing gate bits in 0x7000");
+
+ return 0;
+}
+
+static int ufx_set_vid_mode(struct ufx_data *dev, struct fb_var_screeninfo *var)
+{
+ u32 temp;
+ u16 h_total, h_active, h_blank_start, h_blank_end, h_sync_start, h_sync_end;
+ u16 v_total, v_active, v_blank_start, v_blank_end, v_sync_start, v_sync_end;
+
+ int status = ufx_reg_write(dev, 0x8028, 0);
+ check_warn_return(status, "ufx_set_vid_mode error disabling RGB pad");
+
+ status = ufx_reg_write(dev, 0x8024, 0);
+ check_warn_return(status, "ufx_set_vid_mode error disabling VDAC");
+
+ /* shut everything down before changing timing */
+ status = ufx_blank(dev, true);
+ check_warn_return(status, "ufx_set_vid_mode error blanking display");
+
+ status = ufx_disable(dev, true);
+ check_warn_return(status, "ufx_set_vid_mode error disabling display");
+
+ status = ufx_config_pix_clk(dev, var->pixclock);
+ check_warn_return(status, "ufx_set_vid_mode error configuring pixclock");
+
+ status = ufx_reg_write(dev, 0x2000, 0x00000104);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2000");
+
+ /* set horizontal timings */
+ h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+ h_active = var->xres;
+ h_blank_start = var->xres + var->right_margin;
+ h_blank_end = var->xres + var->right_margin + var->hsync_len;
+ h_sync_start = var->xres + var->right_margin;
+ h_sync_end = var->xres + var->right_margin + var->hsync_len;
+
+ temp = ((h_total - 1) << 16) | (h_active - 1);
+ status = ufx_reg_write(dev, 0x2008, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2008");
+
+ temp = ((h_blank_start - 1) << 16) | (h_blank_end - 1);
+ status = ufx_reg_write(dev, 0x200C, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x200C");
+
+ temp = ((h_sync_start - 1) << 16) | (h_sync_end - 1);
+ status = ufx_reg_write(dev, 0x2010, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2010");
+
+ /* set vertical timings */
+ v_total = var->upper_margin + var->yres + var->lower_margin + var->vsync_len;
+ v_active = var->yres;
+ v_blank_start = var->yres + var->lower_margin;
+ v_blank_end = var->yres + var->lower_margin + var->vsync_len;
+ v_sync_start = var->yres + var->lower_margin;
+ v_sync_end = var->yres + var->lower_margin + var->vsync_len;
+
+ temp = ((v_total - 1) << 16) | (v_active - 1);
+ status = ufx_reg_write(dev, 0x2014, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2014");
+
+ temp = ((v_blank_start - 1) << 16) | (v_blank_end - 1);
+ status = ufx_reg_write(dev, 0x2018, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2018");
+
+ temp = ((v_sync_start - 1) << 16) | (v_sync_end - 1);
+ status = ufx_reg_write(dev, 0x201C, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x201C");
+
+ status = ufx_reg_write(dev, 0x2020, 0x00000000);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2020");
+
+ status = ufx_reg_write(dev, 0x2024, 0x00000000);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2024");
+
+ /* Set the frame length register (#pix * 2 bytes/pixel) */
+ temp = var->xres * var->yres * 2;
+ temp = (temp + 7) & (~0x7);
+ status = ufx_reg_write(dev, 0x2028, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2028");
+
+ /* enable desired output interface & disable others */
+ status = ufx_reg_write(dev, 0x2040, 0);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2040");
+
+ status = ufx_reg_write(dev, 0x2044, 0);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2044");
+
+ status = ufx_reg_write(dev, 0x2048, 0);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2048");
+
+ /* set the sync polarities & enable bit */
+ temp = 0x00000001;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ temp |= 0x00000010;
+
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ temp |= 0x00000008;
+
+ status = ufx_reg_write(dev, 0x2040, temp);
+ check_warn_return(status, "ufx_set_vid_mode error writing 0x2040");
+
+ /* start everything back up */
+ status = ufx_enable(dev, true);
+ check_warn_return(status, "ufx_set_vid_mode error enabling display");
+
+ /* Unblank the display */
+ status = ufx_unblank(dev, true);
+ check_warn_return(status, "ufx_set_vid_mode error unblanking display");
+
+ /* enable RGB pad */
+ status = ufx_reg_write(dev, 0x8028, 0x00000003);
+ check_warn_return(status, "ufx_set_vid_mode error enabling RGB pad");
+
+ /* enable VDAC */
+ status = ufx_reg_write(dev, 0x8024, 0x00000007);
+ check_warn_return(status, "ufx_set_vid_mode error enabling VDAC");
+
+ return 0;
+}
+
+static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ pr_debug("mmap() framebuffer addr:%lu size:%lu\n",
+ pos, size);
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+}
+
+static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y,
+ int width, int height)
+{
+ size_t packed_line_len = ALIGN((width * 2), 4);
+ size_t packed_rect_len = packed_line_len * height;
+ int line;
+
+ BUG_ON(!dev);
+ BUG_ON(!dev->info);
+
+ /* command word */
+ *((u32 *)&cmd[0]) = cpu_to_le32(0x01);
+
+ /* length word */
+ *((u32 *)&cmd[2]) = cpu_to_le32(packed_rect_len + 16);
+
+ cmd[4] = cpu_to_le16(x);
+ cmd[5] = cpu_to_le16(y);
+ cmd[6] = cpu_to_le16(width);
+ cmd[7] = cpu_to_le16(height);
+
+ /* frame base address */
+ *((u32 *)&cmd[8]) = cpu_to_le32(0);
+
+ /* color mode and horizontal resolution */
+ cmd[10] = cpu_to_le16(0x4000 | dev->info->var.xres);
+
+ /* vertical resolution */
+ cmd[11] = cpu_to_le16(dev->info->var.yres);
+
+ /* packed data */
+ for (line = 0; line < height; line++) {
+ const int line_offset = dev->info->fix.line_length * (y + line);
+ const int byte_offset = line_offset + (x * BPP);
+ memcpy(&cmd[(24 + (packed_line_len * line)) / 2],
+ (char *)dev->info->fix.smem_start + byte_offset, width * BPP);
+ }
+}
+
+int ufx_handle_damage(struct ufx_data *dev, int x, int y,
+ int width, int height)
+{
+ size_t packed_line_len = ALIGN((width * 2), 4);
+ int len, status, urb_lines, start_line = 0;
+
+ if ((width <= 0) || (height <= 0) ||
+ (x + width > dev->info->var.xres) ||
+ (y + height > dev->info->var.yres))
+ return -EINVAL;
+
+ if (!atomic_read(&dev->usb_active))
+ return 0;
+
+ while (start_line < height) {
+ struct urb *urb = ufx_get_urb(dev);
+ if (!urb) {
+ pr_warn("ufx_handle_damage unable to get urb");
+ return 0;
+ }
+
+ /* assume we have enough space to transfer at least one line */
+ BUG_ON(urb->transfer_buffer_length < (24 + (width * 2)));
+
+ /* calculate the maximum number of lines we could fit in */
+ urb_lines = (urb->transfer_buffer_length - 24) / packed_line_len;
+
+ /* but we might not need this many */
+ urb_lines = min(urb_lines, (height - start_line));
+
+ memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+
+ ufx_raw_rect(dev, urb->transfer_buffer, x, (y + start_line), width, urb_lines);
+ len = 24 + (packed_line_len * urb_lines);
+
+ status = ufx_submit_urb(dev, urb, len);
+ check_warn_return(status, "Error submitting URB");
+
+ start_line += urb_lines;
+ }
+
+ return 0;
+}
+
+/* Path triggered by usermode clients who write to filesystem
+ * e.g. cat filename > /dev/fb1
+ * Not used by X Windows or text-mode console. But useful for testing.
+ * Slow because of extra copy and we must assume all pixels dirty. */
+static ssize_t ufx_ops_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t result;
+ struct ufx_data *dev = info->par;
+ u32 offset = (u32) *ppos;
+
+ result = fb_sys_write(info, buf, count, ppos);
+
+ if (result > 0) {
+ int start = max((int)(offset / info->fix.line_length) - 1, 0);
+ int lines = min((u32)((result / info->fix.line_length) + 1),
+ (u32)info->var.yres);
+
+ ufx_handle_damage(dev, 0, start, info->var.xres, lines);
+ }
+
+ return result;
+}
+
+static void ufx_ops_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+
+ struct ufx_data *dev = info->par;
+
+ sys_copyarea(info, area);
+
+ ufx_handle_damage(dev, area->dx, area->dy,
+ area->width, area->height);
+}
+
+static void ufx_ops_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ struct ufx_data *dev = info->par;
+
+ sys_imageblit(info, image);
+
+ ufx_handle_damage(dev, image->dx, image->dy,
+ image->width, image->height);
+}
+
+static void ufx_ops_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ struct ufx_data *dev = info->par;
+
+ sys_fillrect(info, rect);
+
+ ufx_handle_damage(dev, rect->dx, rect->dy, rect->width,
+ rect->height);
+}
+
+/* NOTE: fb_defio.c is holding info->fbdefio.mutex
+ * Touching ANY framebuffer memory that triggers a page fault
+ * in fb_defio will cause a deadlock, when it also tries to
+ * grab the same mutex. */
+static void ufx_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct page *cur;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct ufx_data *dev = info->par;
+
+ if (!fb_defio)
+ return;
+
+ if (!atomic_read(&dev->usb_active))
+ return;
+
+ /* walk the written page list and render each to device */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+ /* create a rectangle of full screen width that encloses the
+ * entire dirty framebuffer page */
+ const int x = 0;
+ const int width = dev->info->var.xres;
+ const int y = (cur->index << PAGE_SHIFT) / (width * 2);
+ int height = (PAGE_SIZE / (width * 2)) + 1;
+ height = min(height, (int)(dev->info->var.yres - y));
+
+ BUG_ON(y >= dev->info->var.yres);
+ BUG_ON((y + height) > dev->info->var.yres);
+
+ ufx_handle_damage(dev, x, y, width, height);
+ }
+}
+
+static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ufx_data *dev = info->par;
+ struct dloarea *area = NULL;
+
+ if (!atomic_read(&dev->usb_active))
+ return 0;
+
+ /* TODO: Update X server to get this from sysfs instead */
+ if (cmd == UFX_IOCTL_RETURN_EDID) {
+ u8 __user *edid = (u8 __user *)arg;
+ if (copy_to_user(edid, dev->edid, dev->edid_size))
+ return -EFAULT;
+ return 0;
+ }
+
+ /* TODO: Help propose a standard fb.h ioctl to report mmap damage */
+ if (cmd == UFX_IOCTL_REPORT_DAMAGE) {
+ /* If we have a damage-aware client, turn fb_defio "off"
+ * To avoid perf imact of unecessary page fault handling.
+ * Done by resetting the delay for this fb_info to a very
+ * long period. Pages will become writable and stay that way.
+ * Reset to normal value when all clients have closed this fb.
+ */
+ if (info->fbdefio)
+ info->fbdefio->delay = UFX_DEFIO_WRITE_DISABLE;
+
+ area = (struct dloarea *)arg;
+
+ if (area->x < 0)
+ area->x = 0;
+
+ if (area->x > info->var.xres)
+ area->x = info->var.xres;
+
+ if (area->y < 0)
+ area->y = 0;
+
+ if (area->y > info->var.yres)
+ area->y = info->var.yres;
+
+ ufx_handle_damage(dev, area->x, area->y, area->w, area->h);
+ }
+
+ return 0;
+}
+
+/* taken from vesafb */
+static int
+ufx_ops_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ int err = 0;
+
+ if (regno >= info->cmap.len)
+ return 1;
+
+ if (regno < 16) {
+ if (info->var.red.offset == 10) {
+ /* 1:5:5:5 */
+ ((u32 *) (info->pseudo_palette))[regno] =
+ ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11);
+ } else {
+ /* 0:5:6:5 */
+ ((u32 *) (info->pseudo_palette))[regno] =
+ ((red & 0xf800)) |
+ ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
+ }
+ }
+
+ return err;
+}
+
+/* It's common for several clients to have framebuffer open simultaneously.
+ * e.g. both fbcon and X. Makes things interesting.
+ * Assumes caller is holding info->lock (for open and release at least) */
+static int ufx_ops_open(struct fb_info *info, int user)
+{
+ struct ufx_data *dev = info->par;
+
+ /* fbcon aggressively connects to first framebuffer it finds,
+ * preventing other clients (X) from working properly. Usually
+ * not what the user wants. Fail by default with option to enable. */
+ if (user == 0 && !console)
+ return -EBUSY;
+
+ /* If the USB device is gone, we don't accept new opens */
+ if (dev->virtualized)
+ return -ENODEV;
+
+ dev->fb_count++;
+
+ kref_get(&dev->kref);
+
+ if (fb_defio && (info->fbdefio == NULL)) {
+ /* enable defio at last moment if not disabled by client */
+
+ struct fb_deferred_io *fbdefio;
+
+ fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);
+
+ if (fbdefio) {
+ fbdefio->delay = UFX_DEFIO_WRITE_DELAY;
+ fbdefio->deferred_io = ufx_dpy_deferred_io;
+ }
+
+ info->fbdefio = fbdefio;
+ fb_deferred_io_init(info);
+ }
+
+ pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d",
+ info->node, user, info, dev->fb_count);
+
+ return 0;
+}
+
+/*
+ * Called when all client interfaces to start transactions have been disabled,
+ * and all references to our device instance (ufx_data) are released.
+ * Every transaction must have a reference, so we know are fully spun down
+ */
+static void ufx_free(struct kref *kref)
+{
+ struct ufx_data *dev = container_of(kref, struct ufx_data, kref);
+
+ /* this function will wait for all in-flight urbs to complete */
+ if (dev->urbs.count > 0)
+ ufx_free_urb_list(dev);
+
+ pr_debug("freeing ufx_data %p", dev);
+
+ kfree(dev);
+}
+
+static void ufx_release_urb_work(struct work_struct *work)
+{
+ struct urb_node *unode = container_of(work, struct urb_node,
+ release_urb_work.work);
+
+ up(&unode->dev->urbs.limit_sem);
+}
+
+static void ufx_free_framebuffer_work(struct work_struct *work)
+{
+ struct ufx_data *dev = container_of(work, struct ufx_data,
+ free_framebuffer_work.work);
+ struct fb_info *info = dev->info;
+ int node = info->node;
+
+ unregister_framebuffer(info);
+
+ if (info->cmap.len != 0)
+ fb_dealloc_cmap(&info->cmap);
+ if (info->monspecs.modedb)
+ fb_destroy_modedb(info->monspecs.modedb);
+ if (info->screen_base)
+ vfree(info->screen_base);
+
+ fb_destroy_modelist(&info->modelist);
+
+ dev->info = 0;
+
+ /* Assume info structure is freed after this point */
+ framebuffer_release(info);
+
+ pr_debug("fb_info for /dev/fb%d has been freed", node);
+
+ /* ref taken in probe() as part of registering framebfufer */
+ kref_put(&dev->kref, ufx_free);
+}
+
+/*
+ * Assumes caller is holding info->lock mutex (for open and release at least)
+ */
+static int ufx_ops_release(struct fb_info *info, int user)
+{
+ struct ufx_data *dev = info->par;
+
+ dev->fb_count--;
+
+ /* We can't free fb_info here - fbmem will touch it when we return */
+ if (dev->virtualized && (dev->fb_count == 0))
+ schedule_delayed_work(&dev->free_framebuffer_work, HZ);
+
+ if ((dev->fb_count == 0) && (info->fbdefio)) {
+ fb_deferred_io_cleanup(info);
+ kfree(info->fbdefio);
+ info->fbdefio = NULL;
+ info->fbops->fb_mmap = ufx_ops_mmap;
+ }
+
+ pr_debug("released /dev/fb%d user=%d count=%d",
+ info->node, user, dev->fb_count);
+
+ kref_put(&dev->kref, ufx_free);
+
+ return 0;
+}
+
+/* Check whether a video mode is supported by the chip
+ * We start from monitor's modes, so don't need to filter that here */
+static int ufx_is_valid_mode(struct fb_videomode *mode,
+ struct fb_info *info)
+{
+ if ((mode->xres * mode->yres) > (2048 * 1152)) {
+ pr_debug("%dx%d too many pixels",
+ mode->xres, mode->yres);
+ return 0;
+ }
+
+ if (mode->pixclock < 5000) {
+ pr_debug("%dx%d %dps pixel clock too fast",
+ mode->xres, mode->yres, mode->pixclock);
+ return 0;
+ }
+
+ pr_debug("%dx%d (pixclk %dps %dMHz) valid mode", mode->xres, mode->yres,
+ mode->pixclock, (1000000 / mode->pixclock));
+ return 1;
+}
+
+static void ufx_var_color_format(struct fb_var_screeninfo *var)
+{
+ const struct fb_bitfield red = { 11, 5, 0 };
+ const struct fb_bitfield green = { 5, 6, 0 };
+ const struct fb_bitfield blue = { 0, 5, 0 };
+
+ var->bits_per_pixel = 16;
+ var->red = red;
+ var->green = green;
+ var->blue = blue;
+}
+
+static int ufx_ops_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct fb_videomode mode;
+
+ /* TODO: support dynamically changing framebuffer size */
+ if ((var->xres * var->yres * 2) > info->fix.smem_len)
+ return -EINVAL;
+
+ /* set device-specific elements of var unrelated to mode */
+ ufx_var_color_format(var);
+
+ fb_var_to_videomode(&mode, var);
+
+ if (!ufx_is_valid_mode(&mode, info))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ufx_ops_set_par(struct fb_info *info)
+{
+ struct ufx_data *dev = info->par;
+ int result;
+ u16 *pix_framebuffer;
+ int i;
+
+ pr_debug("set_par mode %dx%d", info->var.xres, info->var.yres);
+ result = ufx_set_vid_mode(dev, &info->var);
+
+ if ((result == 0) && (dev->fb_count == 0)) {
+ /* paint greenscreen */
+ pix_framebuffer = (u16 *) info->screen_base;
+ for (i = 0; i < info->fix.smem_len / 2; i++)
+ pix_framebuffer[i] = 0x37e6;
+
+ ufx_handle_damage(dev, 0, 0, info->var.xres, info->var.yres);
+ }
+
+ /* re-enable defio if previously disabled by damage tracking */
+ if (info->fbdefio)
+ info->fbdefio->delay = UFX_DEFIO_WRITE_DELAY;
+
+ return result;
+}
+
+/* In order to come back from full DPMS off, we need to set the mode again */
+static int ufx_ops_blank(int blank_mode, struct fb_info *info)
+{
+ struct ufx_data *dev = info->par;
+ ufx_set_vid_mode(dev, &info->var);
+ return 0;
+}
+
+static struct fb_ops ufx_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = ufx_ops_write,
+ .fb_setcolreg = ufx_ops_setcolreg,
+ .fb_fillrect = ufx_ops_fillrect,
+ .fb_copyarea = ufx_ops_copyarea,
+ .fb_imageblit = ufx_ops_imageblit,
+ .fb_mmap = ufx_ops_mmap,
+ .fb_ioctl = ufx_ops_ioctl,
+ .fb_open = ufx_ops_open,
+ .fb_release = ufx_ops_release,
+ .fb_blank = ufx_ops_blank,
+ .fb_check_var = ufx_ops_check_var,
+ .fb_set_par = ufx_ops_set_par,
+};
+
+/* Assumes &info->lock held by caller
+ * Assumes no active clients have framebuffer open */
+static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info)
+{
+ int retval = -ENOMEM;
+ int old_len = info->fix.smem_len;
+ int new_len;
+ unsigned char *old_fb = info->screen_base;
+ unsigned char *new_fb;
+
+ pr_debug("Reallocating framebuffer. Addresses will change!");
+
+ new_len = info->fix.line_length * info->var.yres;
+
+ if (PAGE_ALIGN(new_len) > old_len) {
+ /*
+ * Alloc system memory for virtual framebuffer
+ */
+ new_fb = vmalloc(new_len);
+ if (!new_fb) {
+ pr_err("Virtual framebuffer alloc failed");
+ goto error;
+ }
+
+ if (info->screen_base) {
+ memcpy(new_fb, old_fb, old_len);
+ vfree(info->screen_base);
+ }
+
+ info->screen_base = new_fb;
+ info->fix.smem_len = PAGE_ALIGN(new_len);
+ info->fix.smem_start = (unsigned long) new_fb;
+ info->flags = smscufx_info_flags;
+ }
+
+ retval = 0;
+
+error:
+ return retval;
+}
+
+/* sets up I2C Controller for 100 Kbps, std. speed, 7-bit addr, master,
+ * restart enabled, but no start byte, enable controller */
+static int ufx_i2c_init(struct ufx_data *dev)
+{
+ u32 tmp;
+
+ /* disable the controller before it can be reprogrammed */
+ int status = ufx_reg_write(dev, 0x106C, 0x00);
+ check_warn_return(status, "failed to disable I2C");
+
+ /* Setup the clock count registers
+ * (12+1) = 13 clks @ 2.5 MHz = 5.2 uS */
+ status = ufx_reg_write(dev, 0x1018, 12);
+ check_warn_return(status, "error writing 0x1018");
+
+ /* (6+8) = 14 clks @ 2.5 MHz = 5.6 uS */
+ status = ufx_reg_write(dev, 0x1014, 6);
+ check_warn_return(status, "error writing 0x1014");
+
+ status = ufx_reg_read(dev, 0x1000, &tmp);
+ check_warn_return(status, "error reading 0x1000");
+
+ /* set speed to std mode */
+ tmp &= ~(0x06);
+ tmp |= 0x02;
+
+ /* 7-bit (not 10-bit) addressing */
+ tmp &= ~(0x10);
+
+ /* enable restart conditions and master mode */
+ tmp |= 0x21;
+
+ status = ufx_reg_write(dev, 0x1000, tmp);
+ check_warn_return(status, "error writing 0x1000");
+
+ /* Set normal tx using target address 0 */
+ status = ufx_reg_clear_and_set_bits(dev, 0x1004, 0xC00, 0x000);
+ check_warn_return(status, "error setting TX mode bits in 0x1004");
+
+ /* Enable the controller */
+ status = ufx_reg_write(dev, 0x106C, 0x01);
+ check_warn_return(status, "failed to enable I2C");
+
+ return 0;
+}
+
+/* sets the I2C port mux and target address */
+static int ufx_i2c_configure(struct ufx_data *dev)
+{
+ int status = ufx_reg_write(dev, 0x106C, 0x00);
+ check_warn_return(status, "failed to disable I2C");
+
+ status = ufx_reg_write(dev, 0x3010, 0x00000000);
+ check_warn_return(status, "failed to write 0x3010");
+
+ /* A0h is std for any EDID, right shifted by one */
+ status = ufx_reg_clear_and_set_bits(dev, 0x1004, 0x3FF, (0xA0 >> 1));
+ check_warn_return(status, "failed to set TAR bits in 0x1004");
+
+ status = ufx_reg_write(dev, 0x106C, 0x01);
+ check_warn_return(status, "failed to enable I2C");
+
+ return 0;
+}
+
+/* wait for BUSY to clear, with a timeout of 50ms with 10ms sleeps. if no
+ * monitor is connected, there is no error except for timeout */
+static int ufx_i2c_wait_busy(struct ufx_data *dev)
+{
+ u32 tmp;
+ int i, status;
+
+ for (i = 0; i < 15; i++) {
+ status = ufx_reg_read(dev, 0x1100, &tmp);
+ check_warn_return(status, "0x1100 read failed");
+
+ /* if BUSY is clear, check for error */
+ if ((tmp & 0x80000000) == 0) {
+ if (tmp & 0x20000000) {
+ pr_warn("I2C read failed, 0x1100=0x%08x", tmp);
+ return -EIO;
+ }
+
+ return 0;
+ }
+
+ /* perform the first 10 retries without delay */
+ if (i >= 10)
+ msleep(10);
+ }
+
+ pr_warn("I2C access timed out, resetting I2C hardware");
+ status = ufx_reg_write(dev, 0x1100, 0x40000000);
+ check_warn_return(status, "0x1100 write failed");
+
+ return -ETIMEDOUT;
+}
+
+/* reads a 128-byte EDID block from the currently selected port and TAR */
+static int ufx_read_edid(struct ufx_data *dev, u8 *edid, int edid_len)
+{
+ int i, j, status;
+ u32 *edid_u32 = (u32 *)edid;
+
+ BUG_ON(edid_len != EDID_LENGTH);
+
+ status = ufx_i2c_configure(dev);
+ if (status < 0) {
+ pr_err("ufx_i2c_configure failed");
+ return status;
+ }
+
+ memset(edid, 0xff, EDID_LENGTH);
+
+ /* Read the 128-byte EDID as 2 bursts of 64 bytes */
+ for (i = 0; i < 2; i++) {
+ u32 temp = 0x28070000 | (63 << 20) | (((u32)(i * 64)) << 8);
+ status = ufx_reg_write(dev, 0x1100, temp);
+ check_warn_return(status, "Failed to write 0x1100");
+
+ temp |= 0x80000000;
+ status = ufx_reg_write(dev, 0x1100, temp);
+ check_warn_return(status, "Failed to write 0x1100");
+
+ status = ufx_i2c_wait_busy(dev);
+ check_warn_return(status, "Timeout waiting for I2C BUSY to clear");
+
+ for (j = 0; j < 16; j++) {
+ u32 data_reg_addr = 0x1110 + (j * 4);
+ status = ufx_reg_read(dev, data_reg_addr, edid_u32++);
+ check_warn_return(status, "Error reading i2c data");
+ }
+ }
+
+ /* all FF's in the first 16 bytes indicates nothing is connected */
+ for (i = 0; i < 16; i++) {
+ if (edid[i] != 0xFF) {
+ pr_debug("edid data read succesfully");
+ return EDID_LENGTH;
+ }
+ }
+
+ pr_warn("edid data contains all 0xff");
+ return -ETIMEDOUT;
+}
+
+/* 1) use sw default
+ * 2) Parse into various fb_info structs
+ * 3) Allocate virtual framebuffer memory to back highest res mode
+ *
+ * Parses EDID into three places used by various parts of fbdev:
+ * fb_var_screeninfo contains the timing of the monitor's preferred mode
+ * fb_info.monspecs is full parsed EDID info, including monspecs.modedb
+ * fb_info.modelist is a linked list of all monitor & VESA modes which work
+ *
+ * If EDID is not readable/valid, then modelist is all VESA modes,
+ * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode
+ * Returns 0 if successful */
+static int ufx_setup_modes(struct ufx_data *dev, struct fb_info *info,
+ char *default_edid, size_t default_edid_size)
+{
+ const struct fb_videomode *default_vmode = NULL;
+ u8 *edid;
+ int i, result = 0, tries = 3;
+
+ if (info->dev) /* only use mutex if info has been registered */
+ mutex_lock(&info->lock);
+
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid) {
+ result = -ENOMEM;
+ goto error;
+ }
+
+ fb_destroy_modelist(&info->modelist);
+ memset(&info->monspecs, 0, sizeof(info->monspecs));
+
+ /* Try to (re)read EDID from hardware first
+ * EDID data may return, but not parse as valid
+ * Try again a few times, in case of e.g. analog cable noise */
+ while (tries--) {
+ i = ufx_read_edid(dev, edid, EDID_LENGTH);
+
+ if (i >= EDID_LENGTH)
+ fb_edid_to_monspecs(edid, &info->monspecs);
+
+ if (info->monspecs.modedb_len > 0) {
+ dev->edid = edid;
+ dev->edid_size = i;
+ break;
+ }
+ }
+
+ /* If that fails, use a previously returned EDID if available */
+ if (info->monspecs.modedb_len == 0) {
+ pr_err("Unable to get valid EDID from device/display\n");
+
+ if (dev->edid) {
+ fb_edid_to_monspecs(dev->edid, &info->monspecs);
+ if (info->monspecs.modedb_len > 0)
+ pr_err("Using previously queried EDID\n");
+ }
+ }
+
+ /* If that fails, use the default EDID we were handed */
+ if (info->monspecs.modedb_len == 0) {
+ if (default_edid_size >= EDID_LENGTH) {
+ fb_edid_to_monspecs(default_edid, &info->monspecs);
+ if (info->monspecs.modedb_len > 0) {
+ memcpy(edid, default_edid, default_edid_size);
+ dev->edid = edid;
+ dev->edid_size = default_edid_size;
+ pr_err("Using default/backup EDID\n");
+ }
+ }
+ }
+
+ /* If we've got modes, let's pick a best default mode */
+ if (info->monspecs.modedb_len > 0) {
+
+ for (i = 0; i < info->monspecs.modedb_len; i++) {
+ if (ufx_is_valid_mode(&info->monspecs.modedb[i], info))
+ fb_add_videomode(&info->monspecs.modedb[i],
+ &info->modelist);
+ else /* if we've removed top/best mode */
+ info->monspecs.misc &= ~FB_MISC_1ST_DETAIL;
+ }
+
+ default_vmode = fb_find_best_display(&info->monspecs,
+ &info->modelist);
+ }
+
+ /* If everything else has failed, fall back to safe default mode */
+ if (default_vmode == NULL) {
+
+ struct fb_videomode fb_vmode = {0};
+
+ /* Add the standard VESA modes to our modelist
+ * Since we don't have EDID, there may be modes that
+ * overspec monitor and/or are incorrect aspect ratio, etc.
+ * But at least the user has a chance to choose
+ */
+ for (i = 0; i < VESA_MODEDB_SIZE; i++) {
+ if (ufx_is_valid_mode((struct fb_videomode *)
+ &vesa_modes[i], info))
+ fb_add_videomode(&vesa_modes[i],
+ &info->modelist);
+ }
+
+ /* default to resolution safe for projectors
+ * (since they are most common case without EDID)
+ */
+ fb_vmode.xres = 800;
+ fb_vmode.yres = 600;
+ fb_vmode.refresh = 60;
+ default_vmode = fb_find_nearest_mode(&fb_vmode,
+ &info->modelist);
+ }
+
+ /* If we have good mode and no active clients */
+ if ((default_vmode != NULL) && (dev->fb_count == 0)) {
+
+ fb_videomode_to_var(&info->var, default_vmode);
+ ufx_var_color_format(&info->var);
+
+ /* with mode size info, we can now alloc our framebuffer */
+ memcpy(&info->fix, &ufx_fix, sizeof(ufx_fix));
+ info->fix.line_length = info->var.xres *
+ (info->var.bits_per_pixel / 8);
+
+ result = ufx_realloc_framebuffer(dev, info);
+
+ } else
+ result = -EINVAL;
+
+error:
+ if (edid && (dev->edid != edid))
+ kfree(edid);
+
+ if (info->dev)
+ mutex_unlock(&info->lock);
+
+ return result;
+}
+
+static int ufx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev;
+ struct ufx_data *dev;
+ struct fb_info *info = 0;
+ int retval = -ENOMEM;
+ u32 id_rev, fpga_rev;
+
+ /* usb initialization */
+ usbdev = interface_to_usbdev(interface);
+ BUG_ON(!usbdev);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&usbdev->dev, "ufx_usb_probe: failed alloc of dev struct\n");
+ goto error;
+ }
+
+ /* we need to wait for both usb and fbdev to spin down on disconnect */
+ kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
+ kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
+
+ dev->udev = usbdev;
+ dev->gdev = &usbdev->dev; /* our generic struct device * */
+ usb_set_intfdata(interface, dev);
+
+ dev_dbg(dev->gdev, "%s %s - serial #%s\n",
+ usbdev->manufacturer, usbdev->product, usbdev->serial);
+ dev_dbg(dev->gdev, "vid_%04x&pid_%04x&rev_%04x driver's ufx_data struct at %p\n",
+ usbdev->descriptor.idVendor, usbdev->descriptor.idProduct,
+ usbdev->descriptor.bcdDevice, dev);
+ dev_dbg(dev->gdev, "console enable=%d\n", console);
+ dev_dbg(dev->gdev, "fb_defio enable=%d\n", fb_defio);
+
+ if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
+ retval = -ENOMEM;
+ dev_err(dev->gdev, "ufx_alloc_urb_list failed\n");
+ goto error;
+ }
+
+ /* We don't register a new USB class. Our client interface is fbdev */
+
+ /* allocates framebuffer driver structure, not framebuffer memory */
+ info = framebuffer_alloc(0, &usbdev->dev);
+ if (!info) {
+ retval = -ENOMEM;
+ dev_err(dev->gdev, "framebuffer_alloc failed\n");
+ goto error;
+ }
+
+ dev->info = info;
+ info->par = dev;
+ info->pseudo_palette = dev->pseudo_palette;
+ info->fbops = &ufx_ops;
+
+ retval = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (retval < 0) {
+ dev_err(dev->gdev, "fb_alloc_cmap failed %x\n", retval);
+ goto error;
+ }
+
+ INIT_DELAYED_WORK(&dev->free_framebuffer_work,
+ ufx_free_framebuffer_work);
+
+ INIT_LIST_HEAD(&info->modelist);
+
+ retval = ufx_reg_read(dev, 0x3000, &id_rev);
+ check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval);
+ dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev);
+
+ retval = ufx_reg_read(dev, 0x3004, &fpga_rev);
+ check_warn_goto_error(retval, "error %d reading 0x3004 register from device", retval);
+ dev_dbg(dev->gdev, "FPGA_REV register value 0x%08x", fpga_rev);
+
+ dev_dbg(dev->gdev, "resetting device");
+ retval = ufx_lite_reset(dev);
+ check_warn_goto_error(retval, "error %d resetting device", retval);
+
+ dev_dbg(dev->gdev, "configuring system clock");
+ retval = ufx_config_sys_clk(dev);
+ check_warn_goto_error(retval, "error %d configuring system clock", retval);
+
+ dev_dbg(dev->gdev, "configuring DDR2 controller");
+ retval = ufx_config_ddr2(dev);
+ check_warn_goto_error(retval, "error %d initialising DDR2 controller", retval);
+
+ dev_dbg(dev->gdev, "configuring I2C controller");
+ retval = ufx_i2c_init(dev);
+ check_warn_goto_error(retval, "error %d initialising I2C controller", retval);
+
+ dev_dbg(dev->gdev, "selecting display mode");
+ retval = ufx_setup_modes(dev, info, NULL, 0);
+ check_warn_goto_error(retval, "unable to find common mode for display and adapter");
+
+ retval = ufx_reg_set_bits(dev, 0x4000, 0x00000001);
+ check_warn_goto_error(retval, "error %d enabling graphics engine", retval);
+
+ /* ready to begin using device */
+ atomic_set(&dev->usb_active, 1);
+
+ dev_dbg(dev->gdev, "checking var");
+ retval = ufx_ops_check_var(&info->var, info);
+ check_warn_goto_error(retval, "error %d ufx_ops_check_var", retval);
+
+ dev_dbg(dev->gdev, "setting par");
+ retval = ufx_ops_set_par(info);
+ check_warn_goto_error(retval, "error %d ufx_ops_set_par", retval);
+
+ dev_dbg(dev->gdev, "registering framebuffer");
+ retval = register_framebuffer(info);
+ check_warn_goto_error(retval, "error %d register_framebuffer", retval);
+
+ dev_info(dev->gdev, "SMSC UDX USB device /dev/fb%d attached. %dx%d resolution."
+ " Using %dK framebuffer memory\n", info->node,
+ info->var.xres, info->var.yres, info->fix.smem_len >> 10);
+
+ return 0;
+
+error:
+ if (dev) {
+ if (info) {
+ if (info->cmap.len != 0)
+ fb_dealloc_cmap(&info->cmap);
+ if (info->monspecs.modedb)
+ fb_destroy_modedb(info->monspecs.modedb);
+ if (info->screen_base)
+ vfree(info->screen_base);
+
+ fb_destroy_modelist(&info->modelist);
+
+ framebuffer_release(info);
+ }
+
+ kref_put(&dev->kref, ufx_free); /* ref for framebuffer */
+ kref_put(&dev->kref, ufx_free); /* last ref from kref_init */
+
+ /* dev has been deallocated. Do not dereference */
+ }
+
+ return retval;
+}
+
+static void ufx_usb_disconnect(struct usb_interface *interface)
+{
+ struct ufx_data *dev;
+ struct fb_info *info;
+
+ dev = usb_get_intfdata(interface);
+ info = dev->info;
+
+ pr_debug("USB disconnect starting\n");
+
+ /* we virtualize until all fb clients release. Then we free */
+ dev->virtualized = true;
+
+ /* When non-active we'll update virtual framebuffer, but no new urbs */
+ atomic_set(&dev->usb_active, 0);
+
+ usb_set_intfdata(interface, NULL);
+
+ /* if clients still have us open, will be freed on last close */
+ if (dev->fb_count == 0)
+ schedule_delayed_work(&dev->free_framebuffer_work, 0);
+
+ /* release reference taken by kref_init in probe() */
+ kref_put(&dev->kref, ufx_free);
+
+ /* consider ufx_data freed */
+}
+
+static struct usb_driver ufx_driver = {
+ .name = "smscufx",
+ .probe = ufx_usb_probe,
+ .disconnect = ufx_usb_disconnect,
+ .id_table = id_table,
+};
+
+static int __init ufx_module_init(void)
+{
+ int res;
+
+ res = usb_register(&ufx_driver);
+ if (res)
+ err("usb_register failed. Error number %d", res);
+
+ return res;
+}
+
+static void __exit ufx_module_exit(void)
+{
+ usb_deregister(&ufx_driver);
+}
+
+module_init(ufx_module_init);
+module_exit(ufx_module_exit);
+
+static void ufx_urb_completion(struct urb *urb)
+{
+ struct urb_node *unode = urb->context;
+ struct ufx_data *dev = unode->dev;
+ unsigned long flags;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN)) {
+ pr_err("%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
+ atomic_set(&dev->lost_pixels, 1);
+ }
+ }
+
+ urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */
+
+ spin_lock_irqsave(&dev->urbs.lock, flags);
+ list_add_tail(&unode->entry, &dev->urbs.list);
+ dev->urbs.available++;
+ spin_unlock_irqrestore(&dev->urbs.lock, flags);
+
+ /* When using fb_defio, we deadlock if up() is called
+ * while another is waiting. So queue to another process */
+ if (fb_defio)
+ schedule_delayed_work(&unode->release_urb_work, 0);
+ else
+ up(&dev->urbs.limit_sem);
+}
+
+static void ufx_free_urb_list(struct ufx_data *dev)
+{
+ int count = dev->urbs.count;
+ struct list_head *node;
+ struct urb_node *unode;
+ struct urb *urb;
+ int ret;
+ unsigned long flags;
+
+ pr_debug("Waiting for completes and freeing all render urbs\n");
+
+ /* keep waiting and freeing, until we've got 'em all */
+ while (count--) {
+ /* Getting interrupted means a leak, but ok at shutdown*/
+ ret = down_interruptible(&dev->urbs.limit_sem);
+ if (ret)
+ break;
+
+ spin_lock_irqsave(&dev->urbs.lock, flags);
+
+ node = dev->urbs.list.next; /* have reserved one with sem */
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&dev->urbs.lock, flags);
+
+ unode = list_entry(node, struct urb_node, entry);
+ urb = unode->urb;
+
+ /* Free each separately allocated piece */
+ usb_free_coherent(urb->dev, dev->urbs.size,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ kfree(node);
+ }
+}
+
+static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size)
+{
+ int i = 0;
+ struct urb *urb;
+ struct urb_node *unode;
+ char *buf;
+
+ spin_lock_init(&dev->urbs.lock);
+
+ dev->urbs.size = size;
+ INIT_LIST_HEAD(&dev->urbs.list);
+
+ while (i < count) {
+ unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);
+ if (!unode)
+ break;
+ unode->dev = dev;
+
+ INIT_DELAYED_WORK(&unode->release_urb_work,
+ ufx_release_urb_work);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ kfree(unode);
+ break;
+ }
+ unode->urb = urb;
+
+ buf = usb_alloc_coherent(dev->udev, size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ kfree(unode);
+ usb_free_urb(urb);
+ break;
+ }
+
+ /* urb->transfer_buffer_length set to actual before submit */
+ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
+ buf, size, ufx_urb_completion, unode);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ list_add_tail(&unode->entry, &dev->urbs.list);
+
+ i++;
+ }
+
+ sema_init(&dev->urbs.limit_sem, i);
+ dev->urbs.count = i;
+ dev->urbs.available = i;
+
+ pr_debug("allocated %d %d byte urbs\n", i, (int) size);
+
+ return i;
+}
+
+static struct urb *ufx_get_urb(struct ufx_data *dev)
+{
+ int ret = 0;
+ struct list_head *entry;
+ struct urb_node *unode;
+ struct urb *urb = NULL;
+ unsigned long flags;
+
+ /* Wait for an in-flight buffer to complete and get re-queued */
+ ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT);
+ if (ret) {
+ atomic_set(&dev->lost_pixels, 1);
+ pr_warn("wait for urb interrupted: %x available: %d\n",
+ ret, dev->urbs.available);
+ goto error;
+ }
+
+ spin_lock_irqsave(&dev->urbs.lock, flags);
+
+ BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */
+ entry = dev->urbs.list.next;
+ list_del_init(entry);
+ dev->urbs.available--;
+
+ spin_unlock_irqrestore(&dev->urbs.lock, flags);
+
+ unode = list_entry(entry, struct urb_node, entry);
+ urb = unode->urb;
+
+error:
+ return urb;
+}
+
+static int ufx_submit_urb(struct ufx_data *dev, struct urb *urb, size_t len)
+{
+ int ret;
+
+ BUG_ON(len > dev->urbs.size);
+
+ urb->transfer_buffer_length = len; /* set to actual payload len */
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ ufx_urb_completion(urb); /* because no one else will */
+ atomic_set(&dev->lost_pixels, 1);
+ pr_err("usb_submit_urb error %x\n", ret);
+ }
+ return ret;
+}
+
+module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(console, "Allow fbcon to be used on this display");
+
+module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support");
+
+MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
+MODULE_DESCRIPTION("SMSC UFX kernel framebuffer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index cd1c4dcef8fd..8e4a446b5ed1 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -744,7 +744,7 @@ static int __devinit tmiofb_probe(struct platform_device *dev)
goto err_ioremap_vram;
}
- retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED,
+ retval = request_irq(irq, &tmiofb_irq, 0,
dev_name(&dev->dev), info);
if (retval)
diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c
index c6c77562839d..34cf019bba44 100644
--- a/drivers/video/tridentfb.c
+++ b/drivers/video/tridentfb.c
@@ -987,8 +987,8 @@ static int tridentfb_pan_display(struct fb_var_screeninfo *var,
unsigned int offset;
debug("enter\n");
- offset = (var->xoffset + (var->yoffset * var->xres_virtual))
- * var->bits_per_pixel / 32;
+ offset = (var->xoffset + (var->yoffset * info->var.xres_virtual))
+ * info->var.bits_per_pixel / 32;
set_screen_start(par, offset);
debug("exit\n");
return 0;
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
index 087fc9960bb9..3473e75ce785 100644
--- a/drivers/video/udlfb.c
+++ b/drivers/video/udlfb.c
@@ -48,20 +48,30 @@ static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR;
/*
- * There are many DisplayLink-based products, all with unique PIDs. We are able
- * to support all volume ones (circa 2009) with a single driver, so we match
- * globally on VID. TODO: Probe() needs to detect when we might be running
- * "future" chips, and bail on those, so a compatible driver can match.
+ * There are many DisplayLink-based graphics products, all with unique PIDs.
+ * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff)
+ * We also require a match on SubClass (0x00) and Protocol (0x00),
+ * which is compatible with all known USB 2.0 era graphics chips and firmware,
+ * but allows DisplayLink to increment those for any future incompatible chips
*/
static struct usb_device_id id_table[] = {
- {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
+ {.idVendor = 0x17e9,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS |
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL,
+ },
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
/* module options */
-static int console; /* Optionally allow fbcon to consume first framebuffer */
-static int fb_defio; /* Optionally enable experimental fb_defio mmap support */
+static int console = 1; /* Allow fbcon to open framebuffer */
+static int fb_defio = 1; /* Detect mmap writes using page faults */
+static int shadow = 1; /* Optionally disable shadow framebuffer */
/* dlfb keeps a list of urbs for efficient bulk transfers */
static void dlfb_urb_completion(struct urb *urb);
@@ -94,17 +104,39 @@ static char *dlfb_vidreg_unlock(char *buf)
}
/*
- * On/Off for driving the DisplayLink framebuffer to the display
- * 0x00 H and V sync on
- * 0x01 H and V sync off (screen blank but powered)
- * 0x07 DPMS powerdown (requires modeset to come back)
+ * Map FB_BLANK_* to DisplayLink register
+ * DLReg FB_BLANK_*
+ * ----- -----------------------------
+ * 0x00 FB_BLANK_UNBLANK (0)
+ * 0x01 FB_BLANK (1)
+ * 0x03 FB_BLANK_VSYNC_SUSPEND (2)
+ * 0x05 FB_BLANK_HSYNC_SUSPEND (3)
+ * 0x07 FB_BLANK_POWERDOWN (4) Note: requires modeset to come back
*/
-static char *dlfb_enable_hvsync(char *buf, bool enable)
+static char *dlfb_blanking(char *buf, int fb_blank)
{
- if (enable)
- return dlfb_set_register(buf, 0x1F, 0x00);
- else
- return dlfb_set_register(buf, 0x1F, 0x07);
+ u8 reg;
+
+ switch (fb_blank) {
+ case FB_BLANK_POWERDOWN:
+ reg = 0x07;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ reg = 0x05;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ reg = 0x03;
+ break;
+ case FB_BLANK_NORMAL:
+ reg = 0x01;
+ break;
+ default:
+ reg = 0x00;
+ }
+
+ buf = dlfb_set_register(buf, 0x1F, reg);
+
+ return buf;
}
static char *dlfb_set_color_depth(char *buf, u8 selection)
@@ -272,13 +304,15 @@ static int dlfb_set_video_mode(struct dlfb_data *dev,
wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len);
wrptr = dlfb_set_vid_cmds(wrptr, var);
- wrptr = dlfb_enable_hvsync(wrptr, true);
+ wrptr = dlfb_blanking(wrptr, FB_BLANK_UNBLANK);
wrptr = dlfb_vidreg_unlock(wrptr);
writesize = wrptr - buf;
retval = dlfb_submit_urb(dev, urb, writesize);
+ dev->blank_mode = FB_BLANK_UNBLANK;
+
return retval;
}
@@ -752,14 +786,13 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
{
struct dlfb_data *dev = info->par;
- struct dloarea *area = NULL;
if (!atomic_read(&dev->usb_active))
return 0;
/* TODO: Update X server to get this from sysfs instead */
if (cmd == DLFB_IOCTL_RETURN_EDID) {
- char *edid = (char *)arg;
+ void __user *edid = (void __user *)arg;
if (copy_to_user(edid, dev->edid, dev->edid_size))
return -EFAULT;
return 0;
@@ -767,6 +800,11 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
/* TODO: Help propose a standard fb.h ioctl to report mmap damage */
if (cmd == DLFB_IOCTL_REPORT_DAMAGE) {
+ struct dloarea area;
+
+ if (copy_from_user(&area, (void __user *)arg,
+ sizeof(struct dloarea)))
+ return -EFAULT;
/*
* If we have a damage-aware client, turn fb_defio "off"
@@ -778,21 +816,19 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
if (info->fbdefio)
info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE;
- area = (struct dloarea *)arg;
+ if (area.x < 0)
+ area.x = 0;
- if (area->x < 0)
- area->x = 0;
+ if (area.x > info->var.xres)
+ area.x = info->var.xres;
- if (area->x > info->var.xres)
- area->x = info->var.xres;
+ if (area.y < 0)
+ area.y = 0;
- if (area->y < 0)
- area->y = 0;
+ if (area.y > info->var.yres)
+ area.y = info->var.yres;
- if (area->y > info->var.yres)
- area->y = info->var.yres;
-
- dlfb_handle_damage(dev, area->x, area->y, area->w, area->h,
+ dlfb_handle_damage(dev, area.x, area.y, area.w, area.h,
info->screen_base);
}
@@ -840,7 +876,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
* preventing other clients (X) from working properly. Usually
* not what the user wants. Fail by default with option to enable.
*/
- if ((user == 0) & (!console))
+ if ((user == 0) && (!console))
return -EBUSY;
/* If the USB device is gone, we don't accept new opens */
@@ -1039,32 +1075,57 @@ static int dlfb_ops_set_par(struct fb_info *info)
return result;
}
+/* To fonzi the jukebox (e.g. make blanking changes take effect) */
+static char *dlfb_dummy_render(char *buf)
+{
+ *buf++ = 0xAF;
+ *buf++ = 0x6A; /* copy */
+ *buf++ = 0x00; /* from address*/
+ *buf++ = 0x00;
+ *buf++ = 0x00;
+ *buf++ = 0x01; /* one pixel */
+ *buf++ = 0x00; /* to address */
+ *buf++ = 0x00;
+ *buf++ = 0x00;
+ return buf;
+}
+
/*
* In order to come back from full DPMS off, we need to set the mode again
*/
static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
{
struct dlfb_data *dev = info->par;
+ char *bufptr;
+ struct urb *urb;
- if (blank_mode != FB_BLANK_UNBLANK) {
- char *bufptr;
- struct urb *urb;
-
- urb = dlfb_get_urb(dev);
- if (!urb)
- return 0;
+ pr_info("/dev/fb%d FB_BLANK mode %d --> %d\n",
+ info->node, dev->blank_mode, blank_mode);
- bufptr = (char *) urb->transfer_buffer;
- bufptr = dlfb_vidreg_lock(bufptr);
- bufptr = dlfb_enable_hvsync(bufptr, false);
- bufptr = dlfb_vidreg_unlock(bufptr);
+ if ((dev->blank_mode == FB_BLANK_POWERDOWN) &&
+ (blank_mode != FB_BLANK_POWERDOWN)) {
- dlfb_submit_urb(dev, urb, bufptr -
- (char *) urb->transfer_buffer);
- } else {
+ /* returning from powerdown requires a fresh modeset */
dlfb_set_video_mode(dev, &info->var);
}
+ urb = dlfb_get_urb(dev);
+ if (!urb)
+ return 0;
+
+ bufptr = (char *) urb->transfer_buffer;
+ bufptr = dlfb_vidreg_lock(bufptr);
+ bufptr = dlfb_blanking(bufptr, blank_mode);
+ bufptr = dlfb_vidreg_unlock(bufptr);
+
+ /* seems like a render op is needed to have blank change take effect */
+ bufptr = dlfb_dummy_render(bufptr);
+
+ dlfb_submit_urb(dev, urb, bufptr -
+ (char *) urb->transfer_buffer);
+
+ dev->blank_mode = blank_mode;
+
return 0;
}
@@ -1097,7 +1158,7 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info)
int new_len;
unsigned char *old_fb = info->screen_base;
unsigned char *new_fb;
- unsigned char *new_back;
+ unsigned char *new_back = 0;
pr_warn("Reallocating framebuffer. Addresses will change!\n");
@@ -1129,7 +1190,8 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info)
* But with imperfect damage info we may send pixels over USB
* that were, in fact, unchanged - wasting limited USB bandwidth
*/
- new_back = vzalloc(new_len);
+ if (shadow)
+ new_back = vzalloc(new_len);
if (!new_back)
pr_info("No shadow/backing buffer allocated\n");
else {
@@ -1430,21 +1492,30 @@ static int dlfb_select_std_channel(struct dlfb_data *dev)
}
static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev,
- struct usb_device *usbdev)
+ struct usb_interface *interface)
{
char *desc;
char *buf;
char *desc_end;
- u8 total_len = 0;
+ int total_len = 0;
buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL);
if (!buf)
return false;
desc = buf;
- total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */
- 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
+ total_len = usb_get_descriptor(interface_to_usbdev(interface),
+ 0x5f, /* vendor specific */
+ 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
+
+ /* if not found, look in configuration descriptor */
+ if (total_len < 0) {
+ if (0 == usb_get_extra_descriptor(interface->cur_altsetting,
+ 0x5f, &desc))
+ total_len = (int) desc[0];
+ }
+
if (total_len > 5) {
pr_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \
"%02x %02x %02x %02x %02x %02x %02x\n",
@@ -1485,6 +1556,8 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev,
}
desc += length;
}
+ } else {
+ pr_info("vendor descriptor not available (%d)\n", total_len);
}
goto success;
@@ -1531,10 +1604,11 @@ static int dlfb_usb_probe(struct usb_interface *interface,
usbdev->descriptor.bcdDevice, dev);
pr_info("console enable=%d\n", console);
pr_info("fb_defio enable=%d\n", fb_defio);
+ pr_info("shadow enable=%d\n", shadow);
dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */
- if (!dlfb_parse_vendor_descriptor(dev, usbdev)) {
+ if (!dlfb_parse_vendor_descriptor(dev, interface)) {
pr_err("firmware not recognized. Assume incompatible device\n");
goto error;
}
@@ -1548,7 +1622,7 @@ static int dlfb_usb_probe(struct usb_interface *interface,
/* We don't register a new USB class. Our client interface is fbdev */
/* allocates framebuffer driver structure, not framebuffer memory */
- info = framebuffer_alloc(0, &usbdev->dev);
+ info = framebuffer_alloc(0, &interface->dev);
if (!info) {
retval = -ENOMEM;
pr_err("framebuffer_alloc failed\n");
@@ -1883,10 +1957,13 @@ static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len)
}
module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
-MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found");
+MODULE_PARM_DESC(console, "Allow fbcon to open framebuffer");
module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
-MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*");
+MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes");
+
+module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf");
MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
"Jaya Kumar <jayakumar.lkml@gmail.com>, "
diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c
index 6b52bf65f0b5..3f5a041601da 100644
--- a/drivers/video/valkyriefb.c
+++ b/drivers/video/valkyriefb.c
@@ -555,7 +555,7 @@ static int __init valkyrie_init_info(struct fb_info *info,
/*
- * Parse user speficied options (`video=valkyriefb:')
+ * Parse user specified options (`video=valkyriefb:')
*/
int __init valkyriefb_setup(char *options)
{
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index bc67251f1a2f..bf2f78065cf9 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -395,8 +395,8 @@ static int vfb_pan_display(struct fb_var_screeninfo *var,
|| var->xoffset)
return -EINVAL;
} else {
- if (var->xoffset + var->xres > info->var.xres_virtual ||
- var->yoffset + var->yres > info->var.yres_virtual)
+ if (var->xoffset + info->var.xres > info->var.xres_virtual ||
+ var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
}
info->var.xoffset = var->xoffset;
diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
index 305c975b1787..0267acd8dc83 100644
--- a/drivers/video/vga16fb.c
+++ b/drivers/video/vga16fb.c
@@ -207,7 +207,7 @@ static void vga16fb_pan_var(struct fb_info *info,
* granularity if someone supports xoffset in bit resolution */
vga_io_r(VGA_IS1_RC); /* reset flip-flop */
vga_io_w(VGA_ATT_IW, VGA_ATC_PEL);
- if (var->bits_per_pixel == 8)
+ if (info->var.bits_per_pixel == 8)
vga_io_w(VGA_ATT_IW, (xoffset & 3) << 1);
else
vga_io_w(VGA_ATT_IW, xoffset & 7);
diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c
index b1f364745ca0..9138e517267c 100644
--- a/drivers/video/via/dvi.c
+++ b/drivers/video/via/dvi.c
@@ -172,30 +172,20 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len)
}
/* DVI Set Mode */
-void viafb_dvi_set_mode(struct VideoModeTable *mode, int mode_bpp,
- int set_iga)
+void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga)
{
- struct VideoModeTable *rb_mode;
- struct crt_mode_table *pDviTiming;
- unsigned long desirePixelClock, maxPixelClock;
- pDviTiming = mode->crtc;
- desirePixelClock = pDviTiming->refresh_rate
- * pDviTiming->crtc.hor_total * pDviTiming->crtc.ver_total
- / 1000000;
- maxPixelClock = (unsigned long)viaparinfo->
- tmds_setting_info->max_pixel_clock;
-
- DEBUG_MSG(KERN_INFO "\nDVI_set_mode!!\n");
-
- if ((maxPixelClock != 0) && (desirePixelClock > maxPixelClock)) {
- rb_mode = viafb_get_rb_mode(mode->crtc[0].crtc.hor_addr,
- mode->crtc[0].crtc.ver_addr);
- if (rb_mode) {
- mode = rb_mode;
- pDviTiming = rb_mode->crtc;
- }
+ struct fb_var_screeninfo dvi_var = *var;
+ struct crt_mode_table *rb_mode;
+ int maxPixelClock;
+
+ maxPixelClock = viaparinfo->shared->tmds_setting_info.max_pixel_clock;
+ if (maxPixelClock && PICOS2KHZ(var->pixclock) / 1000 > maxPixelClock) {
+ rb_mode = viafb_get_best_rb_mode(var->xres, var->yres, 60);
+ if (rb_mode)
+ viafb_fill_var_timing_info(&dvi_var, rb_mode);
}
- viafb_fill_crtc_timing(pDviTiming, mode, mode_bpp / 8, set_iga);
+
+ viafb_fill_crtc_timing(&dvi_var, iga);
}
/* Sense DVI Connector */
diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h
index f473dd010977..e2116aaf797a 100644
--- a/drivers/video/via/dvi.h
+++ b/drivers/video/via/dvi.h
@@ -59,7 +59,6 @@ void viafb_dvi_enable(void);
bool __devinit viafb_tmds_trasmitter_identify(void);
void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip,
struct tmds_setting_information *tmds_setting);
-void viafb_dvi_set_mode(struct VideoModeTable *videoMode, int mode_bpp,
- int set_iga);
+void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga);
#endif /* __DVI_H__ */
diff --git a/drivers/video/via/global.c b/drivers/video/via/global.c
index e10d8249534c..3102171c1674 100644
--- a/drivers/video/via/global.c
+++ b/drivers/video/via/global.c
@@ -35,6 +35,8 @@ int viafb_LCD_ON ;
int viafb_LCD2_ON;
int viafb_SAMM_ON;
int viafb_dual_fb;
+unsigned int viafb_second_xres = 640;
+unsigned int viafb_second_yres = 480;
int viafb_hotplug_Xres = 640;
int viafb_hotplug_Yres = 480;
int viafb_hotplug_bpp = 32;
diff --git a/drivers/video/via/global.h b/drivers/video/via/global.h
index ff969dc34593..275dbbbd6b81 100644
--- a/drivers/video/via/global.h
+++ b/drivers/video/via/global.h
@@ -67,6 +67,8 @@ extern int viafb_lcd_dsp_method;
extern int viafb_lcd_mode;
extern int viafb_CRT_ON;
+extern unsigned int viafb_second_xres;
+extern unsigned int viafb_second_yres;
extern int viafb_hotplug_Xres;
extern int viafb_hotplug_Yres;
extern int viafb_hotplug_bpp;
diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c
index 47b13535ed2b..d5aaca9cfa7e 100644
--- a/drivers/video/via/hw.c
+++ b/drivers/video/via/hw.c
@@ -191,67 +191,6 @@ static struct fetch_count fetch_count_reg = {
{IGA2_FETCH_COUNT_REG_NUM, {{CR65, 0, 7}, {CR67, 2, 3} } }
};
-static struct iga1_crtc_timing iga1_crtc_reg = {
- /* IGA1 Horizontal Total */
- {IGA1_HOR_TOTAL_REG_NUM, {{CR00, 0, 7}, {CR36, 3, 3} } },
- /* IGA1 Horizontal Addressable Video */
- {IGA1_HOR_ADDR_REG_NUM, {{CR01, 0, 7} } },
- /* IGA1 Horizontal Blank Start */
- {IGA1_HOR_BLANK_START_REG_NUM, {{CR02, 0, 7} } },
- /* IGA1 Horizontal Blank End */
- {IGA1_HOR_BLANK_END_REG_NUM,
- {{CR03, 0, 4}, {CR05, 7, 7}, {CR33, 5, 5} } },
- /* IGA1 Horizontal Sync Start */
- {IGA1_HOR_SYNC_START_REG_NUM, {{CR04, 0, 7}, {CR33, 4, 4} } },
- /* IGA1 Horizontal Sync End */
- {IGA1_HOR_SYNC_END_REG_NUM, {{CR05, 0, 4} } },
- /* IGA1 Vertical Total */
- {IGA1_VER_TOTAL_REG_NUM,
- {{CR06, 0, 7}, {CR07, 0, 0}, {CR07, 5, 5}, {CR35, 0, 0} } },
- /* IGA1 Vertical Addressable Video */
- {IGA1_VER_ADDR_REG_NUM,
- {{CR12, 0, 7}, {CR07, 1, 1}, {CR07, 6, 6}, {CR35, 2, 2} } },
- /* IGA1 Vertical Blank Start */
- {IGA1_VER_BLANK_START_REG_NUM,
- {{CR15, 0, 7}, {CR07, 3, 3}, {CR09, 5, 5}, {CR35, 3, 3} } },
- /* IGA1 Vertical Blank End */
- {IGA1_VER_BLANK_END_REG_NUM, {{CR16, 0, 7} } },
- /* IGA1 Vertical Sync Start */
- {IGA1_VER_SYNC_START_REG_NUM,
- {{CR10, 0, 7}, {CR07, 2, 2}, {CR07, 7, 7}, {CR35, 1, 1} } },
- /* IGA1 Vertical Sync End */
- {IGA1_VER_SYNC_END_REG_NUM, {{CR11, 0, 3} } }
-};
-
-static struct iga2_crtc_timing iga2_crtc_reg = {
- /* IGA2 Horizontal Total */
- {IGA2_HOR_TOTAL_REG_NUM, {{CR50, 0, 7}, {CR55, 0, 3} } },
- /* IGA2 Horizontal Addressable Video */
- {IGA2_HOR_ADDR_REG_NUM, {{CR51, 0, 7}, {CR55, 4, 6} } },
- /* IGA2 Horizontal Blank Start */
- {IGA2_HOR_BLANK_START_REG_NUM, {{CR52, 0, 7}, {CR54, 0, 2} } },
- /* IGA2 Horizontal Blank End */
- {IGA2_HOR_BLANK_END_REG_NUM,
- {{CR53, 0, 7}, {CR54, 3, 5}, {CR5D, 6, 6} } },
- /* IGA2 Horizontal Sync Start */
- {IGA2_HOR_SYNC_START_REG_NUM,
- {{CR56, 0, 7}, {CR54, 6, 7}, {CR5C, 7, 7}, {CR5D, 7, 7} } },
- /* IGA2 Horizontal Sync End */
- {IGA2_HOR_SYNC_END_REG_NUM, {{CR57, 0, 7}, {CR5C, 6, 6} } },
- /* IGA2 Vertical Total */
- {IGA2_VER_TOTAL_REG_NUM, {{CR58, 0, 7}, {CR5D, 0, 2} } },
- /* IGA2 Vertical Addressable Video */
- {IGA2_VER_ADDR_REG_NUM, {{CR59, 0, 7}, {CR5D, 3, 5} } },
- /* IGA2 Vertical Blank Start */
- {IGA2_VER_BLANK_START_REG_NUM, {{CR5A, 0, 7}, {CR5C, 0, 2} } },
- /* IGA2 Vertical Blank End */
- {IGA2_VER_BLANK_END_REG_NUM, {{CR5B, 0, 7}, {CR5C, 3, 5} } },
- /* IGA2 Vertical Sync Start */
- {IGA2_VER_SYNC_START_REG_NUM, {{CR5E, 0, 7}, {CR5F, 5, 7} } },
- /* IGA2 Vertical Sync End */
- {IGA2_VER_SYNC_END_REG_NUM, {{CR5F, 0, 4} } }
-};
-
static struct rgbLUT palLUT_table[] = {
/* {R,G,B} */
/* Index 0x00~0x03 */
@@ -1528,302 +1467,40 @@ void viafb_set_vclock(u32 clk, int set_iga)
via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */
}
-void viafb_load_crtc_timing(struct display_timing device_timing,
- int set_iga)
+static struct display_timing var_to_timing(const struct fb_var_screeninfo *var)
{
- int i;
- int viafb_load_reg_num = 0;
- int reg_value = 0;
- struct io_register *reg = NULL;
-
- viafb_unlock_crt();
-
- for (i = 0; i < 12; i++) {
- if (set_iga == IGA1) {
- switch (i) {
- case H_TOTAL_INDEX:
- reg_value =
- IGA1_HOR_TOTAL_FORMULA(device_timing.
- hor_total);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_total.reg_num;
- reg = iga1_crtc_reg.hor_total.reg;
- break;
- case H_ADDR_INDEX:
- reg_value =
- IGA1_HOR_ADDR_FORMULA(device_timing.
- hor_addr);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_addr.reg_num;
- reg = iga1_crtc_reg.hor_addr.reg;
- break;
- case H_BLANK_START_INDEX:
- reg_value =
- IGA1_HOR_BLANK_START_FORMULA
- (device_timing.hor_blank_start);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_blank_start.reg_num;
- reg = iga1_crtc_reg.hor_blank_start.reg;
- break;
- case H_BLANK_END_INDEX:
- reg_value =
- IGA1_HOR_BLANK_END_FORMULA
- (device_timing.hor_blank_start,
- device_timing.hor_blank_end);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_blank_end.reg_num;
- reg = iga1_crtc_reg.hor_blank_end.reg;
- break;
- case H_SYNC_START_INDEX:
- reg_value =
- IGA1_HOR_SYNC_START_FORMULA
- (device_timing.hor_sync_start);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_sync_start.reg_num;
- reg = iga1_crtc_reg.hor_sync_start.reg;
- break;
- case H_SYNC_END_INDEX:
- reg_value =
- IGA1_HOR_SYNC_END_FORMULA
- (device_timing.hor_sync_start,
- device_timing.hor_sync_end);
- viafb_load_reg_num =
- iga1_crtc_reg.hor_sync_end.reg_num;
- reg = iga1_crtc_reg.hor_sync_end.reg;
- break;
- case V_TOTAL_INDEX:
- reg_value =
- IGA1_VER_TOTAL_FORMULA(device_timing.
- ver_total);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_total.reg_num;
- reg = iga1_crtc_reg.ver_total.reg;
- break;
- case V_ADDR_INDEX:
- reg_value =
- IGA1_VER_ADDR_FORMULA(device_timing.
- ver_addr);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_addr.reg_num;
- reg = iga1_crtc_reg.ver_addr.reg;
- break;
- case V_BLANK_START_INDEX:
- reg_value =
- IGA1_VER_BLANK_START_FORMULA
- (device_timing.ver_blank_start);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_blank_start.reg_num;
- reg = iga1_crtc_reg.ver_blank_start.reg;
- break;
- case V_BLANK_END_INDEX:
- reg_value =
- IGA1_VER_BLANK_END_FORMULA
- (device_timing.ver_blank_start,
- device_timing.ver_blank_end);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_blank_end.reg_num;
- reg = iga1_crtc_reg.ver_blank_end.reg;
- break;
- case V_SYNC_START_INDEX:
- reg_value =
- IGA1_VER_SYNC_START_FORMULA
- (device_timing.ver_sync_start);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_sync_start.reg_num;
- reg = iga1_crtc_reg.ver_sync_start.reg;
- break;
- case V_SYNC_END_INDEX:
- reg_value =
- IGA1_VER_SYNC_END_FORMULA
- (device_timing.ver_sync_start,
- device_timing.ver_sync_end);
- viafb_load_reg_num =
- iga1_crtc_reg.ver_sync_end.reg_num;
- reg = iga1_crtc_reg.ver_sync_end.reg;
- break;
-
- }
- }
-
- if (set_iga == IGA2) {
- switch (i) {
- case H_TOTAL_INDEX:
- reg_value =
- IGA2_HOR_TOTAL_FORMULA(device_timing.
- hor_total);
- viafb_load_reg_num =
- iga2_crtc_reg.hor_total.reg_num;
- reg = iga2_crtc_reg.hor_total.reg;
- break;
- case H_ADDR_INDEX:
- reg_value =
- IGA2_HOR_ADDR_FORMULA(device_timing.
- hor_addr);
- viafb_load_reg_num =
- iga2_crtc_reg.hor_addr.reg_num;
- reg = iga2_crtc_reg.hor_addr.reg;
- break;
- case H_BLANK_START_INDEX:
- reg_value =
- IGA2_HOR_BLANK_START_FORMULA
- (device_timing.hor_blank_start);
- viafb_load_reg_num =
- iga2_crtc_reg.hor_blank_start.reg_num;
- reg = iga2_crtc_reg.hor_blank_start.reg;
- break;
- case H_BLANK_END_INDEX:
- reg_value =
- IGA2_HOR_BLANK_END_FORMULA
- (device_timing.hor_blank_start,
- device_timing.hor_blank_end);
- viafb_load_reg_num =
- iga2_crtc_reg.hor_blank_end.reg_num;
- reg = iga2_crtc_reg.hor_blank_end.reg;
- break;
- case H_SYNC_START_INDEX:
- reg_value =
- IGA2_HOR_SYNC_START_FORMULA
- (device_timing.hor_sync_start);
- if (UNICHROME_CN700 <=
- viaparinfo->chip_info->gfx_chip_name)
- viafb_load_reg_num =
- iga2_crtc_reg.hor_sync_start.
- reg_num;
- else
- viafb_load_reg_num = 3;
- reg = iga2_crtc_reg.hor_sync_start.reg;
- break;
- case H_SYNC_END_INDEX:
- reg_value =
- IGA2_HOR_SYNC_END_FORMULA
- (device_timing.hor_sync_start,
- device_timing.hor_sync_end);
- viafb_load_reg_num =
- iga2_crtc_reg.hor_sync_end.reg_num;
- reg = iga2_crtc_reg.hor_sync_end.reg;
- break;
- case V_TOTAL_INDEX:
- reg_value =
- IGA2_VER_TOTAL_FORMULA(device_timing.
- ver_total);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_total.reg_num;
- reg = iga2_crtc_reg.ver_total.reg;
- break;
- case V_ADDR_INDEX:
- reg_value =
- IGA2_VER_ADDR_FORMULA(device_timing.
- ver_addr);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_addr.reg_num;
- reg = iga2_crtc_reg.ver_addr.reg;
- break;
- case V_BLANK_START_INDEX:
- reg_value =
- IGA2_VER_BLANK_START_FORMULA
- (device_timing.ver_blank_start);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_blank_start.reg_num;
- reg = iga2_crtc_reg.ver_blank_start.reg;
- break;
- case V_BLANK_END_INDEX:
- reg_value =
- IGA2_VER_BLANK_END_FORMULA
- (device_timing.ver_blank_start,
- device_timing.ver_blank_end);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_blank_end.reg_num;
- reg = iga2_crtc_reg.ver_blank_end.reg;
- break;
- case V_SYNC_START_INDEX:
- reg_value =
- IGA2_VER_SYNC_START_FORMULA
- (device_timing.ver_sync_start);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_sync_start.reg_num;
- reg = iga2_crtc_reg.ver_sync_start.reg;
- break;
- case V_SYNC_END_INDEX:
- reg_value =
- IGA2_VER_SYNC_END_FORMULA
- (device_timing.ver_sync_start,
- device_timing.ver_sync_end);
- viafb_load_reg_num =
- iga2_crtc_reg.ver_sync_end.reg_num;
- reg = iga2_crtc_reg.ver_sync_end.reg;
- break;
-
- }
- }
- viafb_load_reg(reg_value, viafb_load_reg_num, reg, VIACR);
- }
-
- viafb_lock_crt();
+ struct display_timing timing;
+
+ timing.hor_addr = var->xres;
+ timing.hor_sync_start = timing.hor_addr + var->right_margin;
+ timing.hor_sync_end = timing.hor_sync_start + var->hsync_len;
+ timing.hor_total = timing.hor_sync_end + var->left_margin;
+ timing.hor_blank_start = timing.hor_addr;
+ timing.hor_blank_end = timing.hor_total;
+ timing.ver_addr = var->yres;
+ timing.ver_sync_start = timing.ver_addr + var->lower_margin;
+ timing.ver_sync_end = timing.ver_sync_start + var->vsync_len;
+ timing.ver_total = timing.ver_sync_end + var->upper_margin;
+ timing.ver_blank_start = timing.ver_addr;
+ timing.ver_blank_end = timing.ver_total;
+ return timing;
}
-void viafb_fill_crtc_timing(struct crt_mode_table *crt_table,
- struct VideoModeTable *video_mode, int bpp_byte, int set_iga)
+void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga)
{
- struct display_timing crt_reg;
- int i;
- int index = 0;
- int h_addr, v_addr;
- u32 clock, refresh = viafb_refresh;
-
- if (viafb_SAMM_ON && set_iga == IGA2)
- refresh = viafb_refresh1;
-
- for (i = 0; i < video_mode->mode_array; i++) {
- index = i;
-
- if (crt_table[i].refresh_rate == refresh)
- break;
- }
+ struct display_timing crt_reg = var_to_timing(var);
- crt_reg = crt_table[index].crtc;
+ if (iga == IGA1)
+ via_set_primary_timing(&crt_reg);
+ else if (iga == IGA2)
+ via_set_secondary_timing(&crt_reg);
- /* Mode 640x480 has border, but LCD/DFP didn't have border. */
- /* So we would delete border. */
- if ((viafb_LCD_ON | viafb_DVI_ON)
- && video_mode->crtc[0].crtc.hor_addr == 640
- && video_mode->crtc[0].crtc.ver_addr == 480
- && refresh == 60) {
- /* The border is 8 pixels. */
- crt_reg.hor_blank_start = crt_reg.hor_blank_start - 8;
-
- /* Blanking time should add left and right borders. */
- crt_reg.hor_blank_end = crt_reg.hor_blank_end + 16;
- }
-
- h_addr = crt_reg.hor_addr;
- v_addr = crt_reg.ver_addr;
- if (set_iga == IGA1) {
- viafb_unlock_crt();
- viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7);
- }
-
- switch (set_iga) {
- case IGA1:
- viafb_load_crtc_timing(crt_reg, IGA1);
- break;
- case IGA2:
- viafb_load_crtc_timing(crt_reg, IGA2);
- break;
- }
-
- viafb_lock_crt();
- viafb_write_reg_mask(CR17, VIACR, 0x80, BIT7);
- viafb_load_fetch_count_reg(h_addr, bpp_byte, set_iga);
-
- /* load FIFO */
- if ((viaparinfo->chip_info->gfx_chip_name != UNICHROME_CLE266)
- && (viaparinfo->chip_info->gfx_chip_name != UNICHROME_K400))
- viafb_load_FIFO_reg(set_iga, h_addr, v_addr);
-
- clock = crt_reg.hor_total * crt_reg.ver_total
- * crt_table[index].refresh_rate;
- viafb_set_vclock(clock, set_iga);
+ viafb_load_fetch_count_reg(var->xres, var->bits_per_pixel / 8, iga);
+ if (viaparinfo->chip_info->gfx_chip_name != UNICHROME_CLE266
+ && viaparinfo->chip_info->gfx_chip_name != UNICHROME_K400)
+ viafb_load_FIFO_reg(iga, var->xres, var->yres);
+ viafb_set_vclock(PICOS2KHZ(var->pixclock) * 1000, iga);
}
void __devinit viafb_init_chip_info(int chip_type)
@@ -2092,23 +1769,9 @@ static u8 get_sync(struct fb_info *info)
return polarity;
}
-int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
- struct VideoModeTable *vmode_tbl1, int video_bpp1)
+static void hw_init(void)
{
- int i, j;
- int port;
- u32 devices = viaparinfo->shared->iga1_devices
- | viaparinfo->shared->iga2_devices;
- u8 value, index, mask;
- struct crt_mode_table *crt_timing;
- struct crt_mode_table *crt_timing1 = NULL;
-
- device_screen_off();
- crt_timing = vmode_tbl->crtc;
-
- if (viafb_SAMM_ON == 1) {
- crt_timing1 = vmode_tbl1->crtc;
- }
+ int i;
inb(VIAStatus);
outb(0x00, VIAAR);
@@ -2147,9 +1810,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
break;
}
+ /* probably this should go to the scaling code one day */
viafb_write_regx(scaling_parameters, ARRAY_SIZE(scaling_parameters));
- device_off();
- via_set_state(devices, VIA_STATE_OFF);
/* Fill VPIT Parameters */
/* Write Misc Register */
@@ -2175,12 +1837,29 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
inb(VIAStatus);
outb(0x20, VIAAR);
+ load_fix_bit_crtc_reg();
+}
+
+int viafb_setmode(int video_bpp, int video_bpp1)
+{
+ int j;
+ int port;
+ u32 devices = viaparinfo->shared->iga1_devices
+ | viaparinfo->shared->iga2_devices;
+ u8 value, index, mask;
+ struct fb_var_screeninfo var2;
+
+ device_screen_off();
+ device_off();
+ via_set_state(devices, VIA_STATE_OFF);
+
+ hw_init();
+
/* Update Patch Register */
if ((viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266
- || viaparinfo->chip_info->gfx_chip_name == UNICHROME_K400)
- && vmode_tbl->crtc[0].crtc.hor_addr == 1024
- && vmode_tbl->crtc[0].crtc.ver_addr == 768) {
+ || viaparinfo->chip_info->gfx_chip_name == UNICHROME_K400)
+ && viafbinfo->var.xres == 1024 && viafbinfo->var.yres == 768) {
for (j = 0; j < res_patch_table[0].table_length; j++) {
index = res_patch_table[0].io_reg_table[j].index;
port = res_patch_table[0].io_reg_table[j].port;
@@ -2190,7 +1869,6 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
}
}
- load_fix_bit_crtc_reg();
via_set_primary_pitch(viafbinfo->fix.line_length);
via_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length
: viafbinfo->fix.line_length);
@@ -2208,23 +1886,28 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
/* Clear On Screen */
+ if (viafb_dual_fb) {
+ var2 = viafbinfo1->var;
+ } else if (viafb_SAMM_ON) {
+ viafb_fill_var_timing_info(&var2, viafb_get_best_mode(
+ viafb_second_xres, viafb_second_yres, viafb_refresh1));
+ var2.bits_per_pixel = viafbinfo->var.bits_per_pixel;
+ }
+
/* CRT set mode */
if (viafb_CRT_ON) {
- if (viafb_SAMM_ON &&
- viaparinfo->shared->iga2_devices & VIA_CRT) {
- viafb_fill_crtc_timing(crt_timing1, vmode_tbl1,
- video_bpp1 / 8, IGA2);
- } else {
- viafb_fill_crtc_timing(crt_timing, vmode_tbl,
- video_bpp / 8,
+ if (viaparinfo->shared->iga2_devices & VIA_CRT
+ && viafb_SAMM_ON)
+ viafb_fill_crtc_timing(&var2, IGA2);
+ else
+ viafb_fill_crtc_timing(&viafbinfo->var,
(viaparinfo->shared->iga1_devices & VIA_CRT)
? IGA1 : IGA2);
- }
/* Patch if set_hres is not 8 alignment (1366) to viafb_setmode
to 8 alignment (1368),there is several pixels (2 pixels)
on right side of screen. */
- if (vmode_tbl->crtc[0].crtc.hor_addr % 8) {
+ if (viafbinfo->var.xres % 8) {
viafb_unlock_crt();
viafb_write_reg(CR02, VIACR,
viafb_read_reg(VIACR, CR02) - 1);
@@ -2233,31 +1916,20 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
}
if (viafb_DVI_ON) {
- if (viafb_SAMM_ON &&
- (viaparinfo->tmds_setting_info->iga_path == IGA2)) {
- viafb_dvi_set_mode(viafb_get_mode
- (viaparinfo->tmds_setting_info->h_active,
- viaparinfo->tmds_setting_info->
- v_active),
- video_bpp1, viaparinfo->
- tmds_setting_info->iga_path);
- } else {
- viafb_dvi_set_mode(viafb_get_mode
- (viaparinfo->tmds_setting_info->h_active,
- viaparinfo->
- tmds_setting_info->v_active),
- video_bpp, viaparinfo->
- tmds_setting_info->iga_path);
- }
+ if (viaparinfo->shared->tmds_setting_info.iga_path == IGA2
+ && viafb_SAMM_ON)
+ viafb_dvi_set_mode(&var2, IGA2);
+ else
+ viafb_dvi_set_mode(&viafbinfo->var,
+ viaparinfo->tmds_setting_info->iga_path);
}
if (viafb_LCD_ON) {
if (viafb_SAMM_ON &&
(viaparinfo->lvds_setting_info->iga_path == IGA2)) {
viaparinfo->lvds_setting_info->bpp = video_bpp1;
- viafb_lcd_set_mode(crt_timing1, viaparinfo->
- lvds_setting_info,
- &viaparinfo->chip_info->lvds_chip_info);
+ viafb_lcd_set_mode(viaparinfo->lvds_setting_info,
+ &viaparinfo->chip_info->lvds_chip_info);
} else {
/* IGA1 doesn't have LCD scaling, so set it center. */
if (viaparinfo->lvds_setting_info->iga_path == IGA1) {
@@ -2265,18 +1937,16 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
LCD_CENTERING;
}
viaparinfo->lvds_setting_info->bpp = video_bpp;
- viafb_lcd_set_mode(crt_timing, viaparinfo->
- lvds_setting_info,
- &viaparinfo->chip_info->lvds_chip_info);
+ viafb_lcd_set_mode(viaparinfo->lvds_setting_info,
+ &viaparinfo->chip_info->lvds_chip_info);
}
}
if (viafb_LCD2_ON) {
if (viafb_SAMM_ON &&
(viaparinfo->lvds_setting_info2->iga_path == IGA2)) {
viaparinfo->lvds_setting_info2->bpp = video_bpp1;
- viafb_lcd_set_mode(crt_timing1, viaparinfo->
- lvds_setting_info2,
- &viaparinfo->chip_info->lvds_chip_info2);
+ viafb_lcd_set_mode(viaparinfo->lvds_setting_info2,
+ &viaparinfo->chip_info->lvds_chip_info2);
} else {
/* IGA1 doesn't have LCD scaling, so set it center. */
if (viaparinfo->lvds_setting_info2->iga_path == IGA1) {
@@ -2284,9 +1954,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
LCD_CENTERING;
}
viaparinfo->lvds_setting_info2->bpp = video_bpp;
- viafb_lcd_set_mode(crt_timing, viaparinfo->
- lvds_setting_info2,
- &viaparinfo->chip_info->lvds_chip_info2);
+ viafb_lcd_set_mode(viaparinfo->lvds_setting_info2,
+ &viaparinfo->chip_info->lvds_chip_info2);
}
}
@@ -2296,8 +1965,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
/* If set mode normally, save resolution information for hot-plug . */
if (!viafb_hotplug) {
- viafb_hotplug_Xres = vmode_tbl->crtc[0].crtc.hor_addr;
- viafb_hotplug_Yres = vmode_tbl->crtc[0].crtc.ver_addr;
+ viafb_hotplug_Xres = viafbinfo->var.xres;
+ viafb_hotplug_Yres = viafbinfo->var.yres;
viafb_hotplug_bpp = video_bpp;
viafb_hotplug_refresh = viafb_refresh;
@@ -2348,42 +2017,14 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
return 1;
}
-int viafb_get_pixclock(int hres, int vres, int vmode_refresh)
-{
- int i;
- struct crt_mode_table *best;
- struct VideoModeTable *vmode = viafb_get_mode(hres, vres);
-
- if (!vmode)
- return RES_640X480_60HZ_PIXCLOCK;
-
- best = &vmode->crtc[0];
- for (i = 1; i < vmode->mode_array; i++) {
- if (abs(vmode->crtc[i].refresh_rate - vmode_refresh)
- < abs(best->refresh_rate - vmode_refresh))
- best = &vmode->crtc[i];
- }
-
- return 1000000000 / (best->crtc.hor_total * best->crtc.ver_total)
- * 1000 / best->refresh_rate;
-}
-
int viafb_get_refresh(int hres, int vres, u32 long_refresh)
{
- int i;
struct crt_mode_table *best;
- struct VideoModeTable *vmode = viafb_get_mode(hres, vres);
- if (!vmode)
+ best = viafb_get_best_mode(hres, vres, long_refresh);
+ if (!best)
return 60;
- best = &vmode->crtc[0];
- for (i = 1; i < vmode->mode_array; i++) {
- if (abs(vmode->crtc[i].refresh_rate - long_refresh)
- < abs(best->refresh_rate - long_refresh))
- best = &vmode->crtc[i];
- }
-
if (abs(best->refresh_rate - long_refresh) > 3) {
if (hres == 1200 && vres == 900)
return 49; /* OLPC DCON only supports 50 Hz */
@@ -2485,21 +2126,14 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\
}
/*According var's xres, yres fill var's other timing information*/
-void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh,
- struct VideoModeTable *vmode_tbl)
+void viafb_fill_var_timing_info(struct fb_var_screeninfo *var,
+ struct crt_mode_table *mode)
{
- struct crt_mode_table *crt_timing = NULL;
struct display_timing crt_reg;
- int i = 0, index = 0;
- crt_timing = vmode_tbl->crtc;
- for (i = 0; i < vmode_tbl->mode_array; i++) {
- index = i;
- if (crt_timing[i].refresh_rate == refresh)
- break;
- }
- crt_reg = crt_timing[index].crtc;
- var->pixclock = viafb_get_pixclock(var->xres, var->yres, refresh);
+ crt_reg = mode->crtc;
+ var->pixclock = 1000000000 / (crt_reg.hor_total * crt_reg.ver_total)
+ * 1000 / mode->refresh_rate;
var->left_margin =
crt_reg.hor_total - (crt_reg.hor_sync_start + crt_reg.hor_sync_end);
var->right_margin = crt_reg.hor_sync_start - crt_reg.hor_addr;
@@ -2509,8 +2143,8 @@ void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh,
var->lower_margin = crt_reg.ver_sync_start - crt_reg.ver_addr;
var->vsync_len = crt_reg.ver_sync_end;
var->sync = 0;
- if (crt_timing[index].h_sync_polarity == POSITIVE)
+ if (mode->h_sync_polarity == POSITIVE)
var->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (crt_timing[index].v_sync_polarity == POSITIVE)
+ if (mode->v_sync_polarity == POSITIVE)
var->sync |= FB_SYNC_VERT_HIGH_ACT;
}
diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h
index c7239eb83bae..4db5b6e8d8d0 100644
--- a/drivers/video/via/hw.h
+++ b/drivers/video/via/hw.h
@@ -51,40 +51,6 @@
#define VIA_HSYNC_NEGATIVE 0x01
#define VIA_VSYNC_NEGATIVE 0x02
-/***************************************************
-* Definition IGA1 Design Method of CRTC Registers *
-****************************************************/
-#define IGA1_HOR_TOTAL_FORMULA(x) (((x)/8)-5)
-#define IGA1_HOR_ADDR_FORMULA(x) (((x)/8)-1)
-#define IGA1_HOR_BLANK_START_FORMULA(x) (((x)/8)-1)
-#define IGA1_HOR_BLANK_END_FORMULA(x, y) (((x+y)/8)-1)
-#define IGA1_HOR_SYNC_START_FORMULA(x) ((x)/8)
-#define IGA1_HOR_SYNC_END_FORMULA(x, y) ((x+y)/8)
-
-#define IGA1_VER_TOTAL_FORMULA(x) ((x)-2)
-#define IGA1_VER_ADDR_FORMULA(x) ((x)-1)
-#define IGA1_VER_BLANK_START_FORMULA(x) ((x)-1)
-#define IGA1_VER_BLANK_END_FORMULA(x, y) ((x+y)-1)
-#define IGA1_VER_SYNC_START_FORMULA(x) ((x)-1)
-#define IGA1_VER_SYNC_END_FORMULA(x, y) ((x+y)-1)
-
-/***************************************************
-** Definition IGA2 Design Method of CRTC Registers *
-****************************************************/
-#define IGA2_HOR_TOTAL_FORMULA(x) ((x)-1)
-#define IGA2_HOR_ADDR_FORMULA(x) ((x)-1)
-#define IGA2_HOR_BLANK_START_FORMULA(x) ((x)-1)
-#define IGA2_HOR_BLANK_END_FORMULA(x, y) ((x+y)-1)
-#define IGA2_HOR_SYNC_START_FORMULA(x) ((x)-1)
-#define IGA2_HOR_SYNC_END_FORMULA(x, y) ((x+y)-1)
-
-#define IGA2_VER_TOTAL_FORMULA(x) ((x)-1)
-#define IGA2_VER_ADDR_FORMULA(x) ((x)-1)
-#define IGA2_VER_BLANK_START_FORMULA(x) ((x)-1)
-#define IGA2_VER_BLANK_END_FORMULA(x, y) ((x+y)-1)
-#define IGA2_VER_SYNC_START_FORMULA(x) ((x)-1)
-#define IGA2_VER_SYNC_END_FORMULA(x, y) ((x+y)-1)
-
/**********************************************************/
/* Definition IGA2 Design Method of CRTC Shadow Registers */
/**********************************************************/
@@ -97,33 +63,6 @@
#define IGA2_VER_SYNC_START_SHADOW_FORMULA(x) (x)
#define IGA2_VER_SYNC_END_SHADOW_FORMULA(x, y) (x+y)
-/* Define Register Number for IGA1 CRTC Timing */
-
-/* location: {CR00,0,7},{CR36,3,3} */
-#define IGA1_HOR_TOTAL_REG_NUM 2
-/* location: {CR01,0,7} */
-#define IGA1_HOR_ADDR_REG_NUM 1
-/* location: {CR02,0,7} */
-#define IGA1_HOR_BLANK_START_REG_NUM 1
-/* location: {CR03,0,4},{CR05,7,7},{CR33,5,5} */
-#define IGA1_HOR_BLANK_END_REG_NUM 3
-/* location: {CR04,0,7},{CR33,4,4} */
-#define IGA1_HOR_SYNC_START_REG_NUM 2
-/* location: {CR05,0,4} */
-#define IGA1_HOR_SYNC_END_REG_NUM 1
-/* location: {CR06,0,7},{CR07,0,0},{CR07,5,5},{CR35,0,0} */
-#define IGA1_VER_TOTAL_REG_NUM 4
-/* location: {CR12,0,7},{CR07,1,1},{CR07,6,6},{CR35,2,2} */
-#define IGA1_VER_ADDR_REG_NUM 4
-/* location: {CR15,0,7},{CR07,3,3},{CR09,5,5},{CR35,3,3} */
-#define IGA1_VER_BLANK_START_REG_NUM 4
-/* location: {CR16,0,7} */
-#define IGA1_VER_BLANK_END_REG_NUM 1
-/* location: {CR10,0,7},{CR07,2,2},{CR07,7,7},{CR35,1,1} */
-#define IGA1_VER_SYNC_START_REG_NUM 4
-/* location: {CR11,0,3} */
-#define IGA1_VER_SYNC_END_REG_NUM 1
-
/* Define Register Number for IGA2 Shadow CRTC Timing */
/* location: {CR6D,0,7},{CR71,3,3} */
@@ -143,37 +82,6 @@
/* location: {CR76,0,3} */
#define IGA2_SHADOW_VER_SYNC_END_REG_NUM 1
-/* Define Register Number for IGA2 CRTC Timing */
-
-/* location: {CR50,0,7},{CR55,0,3} */
-#define IGA2_HOR_TOTAL_REG_NUM 2
-/* location: {CR51,0,7},{CR55,4,6} */
-#define IGA2_HOR_ADDR_REG_NUM 2
-/* location: {CR52,0,7},{CR54,0,2} */
-#define IGA2_HOR_BLANK_START_REG_NUM 2
-/* location: CLE266: {CR53,0,7},{CR54,3,5} => CLE266's CR5D[6]
-is reserved, so it may have problem to set 1600x1200 on IGA2. */
-/* Others: {CR53,0,7},{CR54,3,5},{CR5D,6,6} */
-#define IGA2_HOR_BLANK_END_REG_NUM 3
-/* location: {CR56,0,7},{CR54,6,7},{CR5C,7,7} */
-/* VT3314 and Later: {CR56,0,7},{CR54,6,7},{CR5C,7,7}, {CR5D,7,7} */
-#define IGA2_HOR_SYNC_START_REG_NUM 4
-
-/* location: {CR57,0,7},{CR5C,6,6} */
-#define IGA2_HOR_SYNC_END_REG_NUM 2
-/* location: {CR58,0,7},{CR5D,0,2} */
-#define IGA2_VER_TOTAL_REG_NUM 2
-/* location: {CR59,0,7},{CR5D,3,5} */
-#define IGA2_VER_ADDR_REG_NUM 2
-/* location: {CR5A,0,7},{CR5C,0,2} */
-#define IGA2_VER_BLANK_START_REG_NUM 2
-/* location: {CR5E,0,7},{CR5C,3,5} */
-#define IGA2_VER_BLANK_END_REG_NUM 2
-/* location: {CR5E,0,7},{CR5F,5,7} */
-#define IGA2_VER_SYNC_START_REG_NUM 2
-/* location: {CR5F,0,4} */
-#define IGA2_VER_SYNC_END_REG_NUM 1
-
/* Define Fetch Count Register*/
/* location: {SR1C,0,7},{SR1D,0,1} */
@@ -446,87 +354,12 @@ is reserved, so it may have problem to set 1600x1200 on IGA2. */
/* location: {CR78,0,7},{CR79,6,7} */
#define LCD_VER_SCALING_FACTOR_REG_NUM_CLE 2
-/************************************************
- ***** Define IGA1 Display Timing *****
- ************************************************/
struct io_register {
u8 io_addr;
u8 start_bit;
u8 end_bit;
};
-/* IGA1 Horizontal Total */
-struct iga1_hor_total {
- int reg_num;
- struct io_register reg[IGA1_HOR_TOTAL_REG_NUM];
-};
-
-/* IGA1 Horizontal Addressable Video */
-struct iga1_hor_addr {
- int reg_num;
- struct io_register reg[IGA1_HOR_ADDR_REG_NUM];
-};
-
-/* IGA1 Horizontal Blank Start */
-struct iga1_hor_blank_start {
- int reg_num;
- struct io_register reg[IGA1_HOR_BLANK_START_REG_NUM];
-};
-
-/* IGA1 Horizontal Blank End */
-struct iga1_hor_blank_end {
- int reg_num;
- struct io_register reg[IGA1_HOR_BLANK_END_REG_NUM];
-};
-
-/* IGA1 Horizontal Sync Start */
-struct iga1_hor_sync_start {
- int reg_num;
- struct io_register reg[IGA1_HOR_SYNC_START_REG_NUM];
-};
-
-/* IGA1 Horizontal Sync End */
-struct iga1_hor_sync_end {
- int reg_num;
- struct io_register reg[IGA1_HOR_SYNC_END_REG_NUM];
-};
-
-/* IGA1 Vertical Total */
-struct iga1_ver_total {
- int reg_num;
- struct io_register reg[IGA1_VER_TOTAL_REG_NUM];
-};
-
-/* IGA1 Vertical Addressable Video */
-struct iga1_ver_addr {
- int reg_num;
- struct io_register reg[IGA1_VER_ADDR_REG_NUM];
-};
-
-/* IGA1 Vertical Blank Start */
-struct iga1_ver_blank_start {
- int reg_num;
- struct io_register reg[IGA1_VER_BLANK_START_REG_NUM];
-};
-
-/* IGA1 Vertical Blank End */
-struct iga1_ver_blank_end {
- int reg_num;
- struct io_register reg[IGA1_VER_BLANK_END_REG_NUM];
-};
-
-/* IGA1 Vertical Sync Start */
-struct iga1_ver_sync_start {
- int reg_num;
- struct io_register reg[IGA1_VER_SYNC_START_REG_NUM];
-};
-
-/* IGA1 Vertical Sync End */
-struct iga1_ver_sync_end {
- int reg_num;
- struct io_register reg[IGA1_VER_SYNC_END_REG_NUM];
-};
-
/*****************************************************
** Define IGA2 Shadow Display Timing ****
*****************************************************/
@@ -579,82 +412,6 @@ struct iga2_shadow_ver_sync_end {
struct io_register reg[IGA2_SHADOW_VER_SYNC_END_REG_NUM];
};
-/*****************************************************
-** Define IGA2 Display Timing ****
-******************************************************/
-
-/* IGA2 Horizontal Total */
-struct iga2_hor_total {
- int reg_num;
- struct io_register reg[IGA2_HOR_TOTAL_REG_NUM];
-};
-
-/* IGA2 Horizontal Addressable Video */
-struct iga2_hor_addr {
- int reg_num;
- struct io_register reg[IGA2_HOR_ADDR_REG_NUM];
-};
-
-/* IGA2 Horizontal Blank Start */
-struct iga2_hor_blank_start {
- int reg_num;
- struct io_register reg[IGA2_HOR_BLANK_START_REG_NUM];
-};
-
-/* IGA2 Horizontal Blank End */
-struct iga2_hor_blank_end {
- int reg_num;
- struct io_register reg[IGA2_HOR_BLANK_END_REG_NUM];
-};
-
-/* IGA2 Horizontal Sync Start */
-struct iga2_hor_sync_start {
- int reg_num;
- struct io_register reg[IGA2_HOR_SYNC_START_REG_NUM];
-};
-
-/* IGA2 Horizontal Sync End */
-struct iga2_hor_sync_end {
- int reg_num;
- struct io_register reg[IGA2_HOR_SYNC_END_REG_NUM];
-};
-
-/* IGA2 Vertical Total */
-struct iga2_ver_total {
- int reg_num;
- struct io_register reg[IGA2_VER_TOTAL_REG_NUM];
-};
-
-/* IGA2 Vertical Addressable Video */
-struct iga2_ver_addr {
- int reg_num;
- struct io_register reg[IGA2_VER_ADDR_REG_NUM];
-};
-
-/* IGA2 Vertical Blank Start */
-struct iga2_ver_blank_start {
- int reg_num;
- struct io_register reg[IGA2_VER_BLANK_START_REG_NUM];
-};
-
-/* IGA2 Vertical Blank End */
-struct iga2_ver_blank_end {
- int reg_num;
- struct io_register reg[IGA2_VER_BLANK_END_REG_NUM];
-};
-
-/* IGA2 Vertical Sync Start */
-struct iga2_ver_sync_start {
- int reg_num;
- struct io_register reg[IGA2_VER_SYNC_START_REG_NUM];
-};
-
-/* IGA2 Vertical Sync End */
-struct iga2_ver_sync_end {
- int reg_num;
- struct io_register reg[IGA2_VER_SYNC_END_REG_NUM];
-};
-
/* IGA1 Fetch Count Register */
struct iga1_fetch_count {
int reg_num;
@@ -817,21 +574,6 @@ struct display_queue_expire_num {
iga2_display_queue_expire_num_reg;
};
-struct iga1_crtc_timing {
- struct iga1_hor_total hor_total;
- struct iga1_hor_addr hor_addr;
- struct iga1_hor_blank_start hor_blank_start;
- struct iga1_hor_blank_end hor_blank_end;
- struct iga1_hor_sync_start hor_sync_start;
- struct iga1_hor_sync_end hor_sync_end;
- struct iga1_ver_total ver_total;
- struct iga1_ver_addr ver_addr;
- struct iga1_ver_blank_start ver_blank_start;
- struct iga1_ver_blank_end ver_blank_end;
- struct iga1_ver_sync_start ver_sync_start;
- struct iga1_ver_sync_end ver_sync_end;
-};
-
struct iga2_shadow_crtc_timing {
struct iga2_shadow_hor_total hor_total_shadow;
struct iga2_shadow_hor_blank_end hor_blank_end_shadow;
@@ -843,21 +585,6 @@ struct iga2_shadow_crtc_timing {
struct iga2_shadow_ver_sync_end ver_sync_end_shadow;
};
-struct iga2_crtc_timing {
- struct iga2_hor_total hor_total;
- struct iga2_hor_addr hor_addr;
- struct iga2_hor_blank_start hor_blank_start;
- struct iga2_hor_blank_end hor_blank_end;
- struct iga2_hor_sync_start hor_sync_start;
- struct iga2_hor_sync_end hor_sync_end;
- struct iga2_ver_total ver_total;
- struct iga2_ver_addr ver_addr;
- struct iga2_ver_blank_start ver_blank_start;
- struct iga2_ver_blank_end ver_blank_end;
- struct iga2_ver_sync_start ver_sync_start;
- struct iga2_ver_sync_end ver_sync_end;
-};
-
/* device ID */
#define CLE266_FUNCTION3 0x3123
#define KM400_FUNCTION3 0x3205
@@ -910,9 +637,7 @@ extern int viafb_LCD_ON;
extern int viafb_DVI_ON;
extern int viafb_hotplug;
-void viafb_fill_crtc_timing(struct crt_mode_table *crt_table,
- struct VideoModeTable *video_mode, int bpp_byte, int set_iga);
-
+void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga);
void viafb_set_vclock(u32 CLK, int set_iga);
void viafb_load_reg(int timing_value, int viafb_load_reg_num,
struct io_register *reg,
@@ -932,13 +657,11 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active);
void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\
*p_gfx_dpa_setting);
-int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp,
- struct VideoModeTable *vmode_tbl1, int video_bpp1);
-void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh,
- struct VideoModeTable *vmode_tbl);
+int viafb_setmode(int video_bpp, int video_bpp1);
+void viafb_fill_var_timing_info(struct fb_var_screeninfo *var,
+ struct crt_mode_table *mode);
void __devinit viafb_init_chip_info(int chip_type);
void __devinit viafb_init_dac(int set_iga);
-int viafb_get_pixclock(int hres, int vres, int vmode_refresh);
int viafb_get_refresh(int hres, int vres, u32 float_refresh);
void viafb_update_device_setting(int hres, int vres, int bpp, int flag);
diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c
index 6e06981d638b..5f3b4e394e82 100644
--- a/drivers/video/via/lcd.c
+++ b/drivers/video/via/lcd.c
@@ -548,9 +548,8 @@ static void lcd_patch_skew(struct lvds_setting_information
}
/* LCD Set Mode */
-void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table,
- struct lvds_setting_information *plvds_setting_info,
- struct lvds_chip_information *plvds_chip_info)
+void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info,
+ struct lvds_chip_information *plvds_chip_info)
{
int set_iga = plvds_setting_info->iga_path;
int mode_bpp = plvds_setting_info->bpp;
@@ -559,16 +558,15 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table,
int panel_hres = plvds_setting_info->lcd_panel_hres;
int panel_vres = plvds_setting_info->lcd_panel_vres;
u32 clock;
- struct display_timing mode_crt_reg, panel_crt_reg;
- struct crt_mode_table *panel_crt_table = NULL;
- struct VideoModeTable *vmode_tbl = viafb_get_mode(panel_hres,
- panel_vres);
+ struct display_timing mode_crt_reg, panel_crt_reg, timing;
+ struct crt_mode_table *mode_crt_table, *panel_crt_table;
DEBUG_MSG(KERN_INFO "viafb_lcd_set_mode!!\n");
/* Get mode table */
+ mode_crt_table = viafb_get_best_mode(set_hres, set_vres, 60);
mode_crt_reg = mode_crt_table->crtc;
/* Get panel table Pointer */
- panel_crt_table = vmode_tbl->crtc;
+ panel_crt_table = viafb_get_best_mode(panel_hres, panel_vres, 60);
panel_crt_reg = panel_crt_table->crtc;
DEBUG_MSG(KERN_INFO "bellow viafb_lcd_set_mode!!\n");
if (VT1636_LVDS == plvds_chip_info->lvds_chip_name)
@@ -576,31 +574,28 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table,
clock = panel_crt_reg.hor_total * panel_crt_reg.ver_total
* panel_crt_table->refresh_rate;
plvds_setting_info->vclk = clock;
- if (set_iga == IGA1) {
- /* IGA1 doesn't have LCD scaling, so set it as centering. */
- viafb_load_crtc_timing(lcd_centering_timging
- (mode_crt_reg, panel_crt_reg), IGA1);
+
+ if (set_iga == IGA2 && (set_hres < panel_hres || set_vres < panel_vres)
+ && plvds_setting_info->display_method == LCD_EXPANDSION) {
+ timing = panel_crt_reg;
+ load_lcd_scaling(set_hres, set_vres, panel_hres, panel_vres);
} else {
- /* Expansion */
- if (plvds_setting_info->display_method == LCD_EXPANDSION
- && (set_hres < panel_hres || set_vres < panel_vres)) {
- /* expansion timing IGA2 loaded panel set timing*/
- viafb_load_crtc_timing(panel_crt_reg, IGA2);
- DEBUG_MSG(KERN_INFO "viafb_load_crtc_timing!!\n");
- load_lcd_scaling(set_hres, set_vres, panel_hres,
- panel_vres);
- DEBUG_MSG(KERN_INFO "load_lcd_scaling!!\n");
- } else { /* Centering */
- /* centering timing IGA2 always loaded panel
- and mode releative timing */
- viafb_load_crtc_timing(lcd_centering_timging
- (mode_crt_reg, panel_crt_reg), IGA2);
- viafb_write_reg_mask(CR79, VIACR, 0x00,
+ timing = lcd_centering_timging(mode_crt_reg, panel_crt_reg);
+ if (set_iga == IGA2)
+ /* disable scaling */
+ via_write_reg_mask(VIACR, 0x79, 0x00,
BIT0 + BIT1 + BIT2);
- /* LCD scaling disabled */
- }
}
+ timing.hor_blank_end += timing.hor_blank_start;
+ timing.hor_sync_end += timing.hor_sync_start;
+ timing.ver_blank_end += timing.ver_blank_start;
+ timing.ver_sync_end += timing.ver_sync_start;
+ if (set_iga == IGA1)
+ via_set_primary_timing(&timing);
+ else if (set_iga == IGA2)
+ via_set_secondary_timing(&timing);
+
/* Fetch count for IGA2 only */
viafb_load_fetch_count_reg(set_hres, mode_bpp / 8, set_iga);
diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h
index 75f60a655b0e..77ca7b862e68 100644
--- a/drivers/video/via/lcd.h
+++ b/drivers/video/via/lcd.h
@@ -76,16 +76,13 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information
*plvds_chip_info,
struct lvds_setting_information
*plvds_setting_info);
-void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table,
- struct lvds_setting_information *plvds_setting_info,
- struct lvds_chip_information *plvds_chip_info);
+void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info,
+ struct lvds_chip_information *plvds_chip_info);
bool __devinit viafb_lvds_trasmitter_identify(void);
void viafb_init_lvds_output_interface(struct lvds_chip_information
*plvds_chip_info,
struct lvds_setting_information
*plvds_setting_info);
bool viafb_lcd_get_mobile_state(bool *mobile);
-void viafb_load_crtc_timing(struct display_timing device_timing,
- int set_iga);
#endif /* __LCD_H__ */
diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h
index 61b0bd596b85..69d882cbe709 100644
--- a/drivers/video/via/share.h
+++ b/drivers/video/via/share.h
@@ -22,6 +22,8 @@
#ifndef __SHARE_H__
#define __SHARE_H__
+#include "via_modesetting.h"
+
/* Define Bit Field */
#define BIT0 0x01
#define BIT1 0x02
@@ -634,10 +636,6 @@
#define V_SYNC_SATRT_SHADOW_INDEX 18
#define V_SYNC_END_SHADOW_INDEX 19
-/* Definition Video Mode Pixel Clock (picoseconds)
-*/
-#define RES_640X480_60HZ_PIXCLOCK 39722
-
/* LCD display method
*/
#define LCD_EXPANDSION 0x00
@@ -648,23 +646,6 @@
#define LCD_OPENLDI 0x00
#define LCD_SPWG 0x01
-/* Define display timing
-*/
-struct display_timing {
- u16 hor_total;
- u16 hor_addr;
- u16 hor_blank_start;
- u16 hor_blank_end;
- u16 hor_sync_start;
- u16 hor_sync_end;
- u16 ver_total;
- u16 ver_addr;
- u16 ver_blank_start;
- u16 ver_blank_end;
- u16 ver_sync_start;
- u16 ver_sync_end;
-};
-
struct crt_mode_table {
int refresh_rate;
int h_sync_polarity;
diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c
index eb112b621735..dd58b530c0df 100644
--- a/drivers/video/via/via-core.c
+++ b/drivers/video/via/via-core.c
@@ -35,7 +35,7 @@ static struct via_port_cfg adap_configs[] = {
* The OLPC XO-1.5 puts the camera power and reset lines onto
* GPIO 2C.
*/
-static const struct via_port_cfg olpc_adap_configs[] = {
+static struct via_port_cfg olpc_adap_configs[] = {
[VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 },
[VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 },
[VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c
index 3cddcff88ab9..0e431aee17bb 100644
--- a/drivers/video/via/via_modesetting.c
+++ b/drivers/video/via/via_modesetting.c
@@ -29,6 +29,110 @@
#include "share.h"
#include "debug.h"
+
+void via_set_primary_timing(const struct display_timing *timing)
+{
+ struct display_timing raw;
+
+ raw.hor_total = timing->hor_total / 8 - 5;
+ raw.hor_addr = timing->hor_addr / 8 - 1;
+ raw.hor_blank_start = timing->hor_blank_start / 8 - 1;
+ raw.hor_blank_end = timing->hor_blank_end / 8 - 1;
+ raw.hor_sync_start = timing->hor_sync_start / 8;
+ raw.hor_sync_end = timing->hor_sync_end / 8;
+ raw.ver_total = timing->ver_total - 2;
+ raw.ver_addr = timing->ver_addr - 1;
+ raw.ver_blank_start = timing->ver_blank_start - 1;
+ raw.ver_blank_end = timing->ver_blank_end - 1;
+ raw.ver_sync_start = timing->ver_sync_start - 1;
+ raw.ver_sync_end = timing->ver_sync_end - 1;
+
+ /* unlock timing registers */
+ via_write_reg_mask(VIACR, 0x11, 0x00, 0x80);
+
+ via_write_reg(VIACR, 0x00, raw.hor_total & 0xFF);
+ via_write_reg(VIACR, 0x01, raw.hor_addr & 0xFF);
+ via_write_reg(VIACR, 0x02, raw.hor_blank_start & 0xFF);
+ via_write_reg_mask(VIACR, 0x03, raw.hor_blank_end & 0x1F, 0x1F);
+ via_write_reg(VIACR, 0x04, raw.hor_sync_start & 0xFF);
+ via_write_reg_mask(VIACR, 0x05, (raw.hor_sync_end & 0x1F)
+ | (raw.hor_blank_end << (7 - 5) & 0x80), 0x9F);
+ via_write_reg(VIACR, 0x06, raw.ver_total & 0xFF);
+ via_write_reg_mask(VIACR, 0x07, (raw.ver_total >> 8 & 0x01)
+ | (raw.ver_addr >> (8 - 1) & 0x02)
+ | (raw.ver_sync_start >> (8 - 2) & 0x04)
+ | (raw.ver_blank_start >> (8 - 3) & 0x08)
+ | (raw.ver_total >> (9 - 5) & 0x20)
+ | (raw.ver_addr >> (9 - 6) & 0x40)
+ | (raw.ver_sync_start >> (9 - 7) & 0x80), 0xEF);
+ via_write_reg_mask(VIACR, 0x09, raw.ver_blank_start >> (9 - 5) & 0x20,
+ 0x20);
+ via_write_reg(VIACR, 0x10, raw.ver_sync_start & 0xFF);
+ via_write_reg_mask(VIACR, 0x11, raw.ver_sync_end & 0x0F, 0x0F);
+ via_write_reg(VIACR, 0x12, raw.ver_addr & 0xFF);
+ via_write_reg(VIACR, 0x15, raw.ver_blank_start & 0xFF);
+ via_write_reg(VIACR, 0x16, raw.ver_blank_end & 0xFF);
+ via_write_reg_mask(VIACR, 0x33, (raw.hor_sync_start >> (8 - 4) & 0x10)
+ | (raw.hor_blank_end >> (6 - 5) & 0x20), 0x30);
+ via_write_reg_mask(VIACR, 0x35, (raw.ver_total >> 10 & 0x01)
+ | (raw.ver_sync_start >> (10 - 1) & 0x02)
+ | (raw.ver_addr >> (10 - 2) & 0x04)
+ | (raw.ver_blank_start >> (10 - 3) & 0x08), 0x0F);
+ via_write_reg_mask(VIACR, 0x36, raw.hor_total >> (8 - 3) & 0x08, 0x08);
+
+ /* lock timing registers */
+ via_write_reg_mask(VIACR, 0x11, 0x80, 0x80);
+
+ /* reset timing control */
+ via_write_reg_mask(VIACR, 0x17, 0x00, 0x80);
+ via_write_reg_mask(VIACR, 0x17, 0x80, 0x80);
+}
+
+void via_set_secondary_timing(const struct display_timing *timing)
+{
+ struct display_timing raw;
+
+ raw.hor_total = timing->hor_total - 1;
+ raw.hor_addr = timing->hor_addr - 1;
+ raw.hor_blank_start = timing->hor_blank_start - 1;
+ raw.hor_blank_end = timing->hor_blank_end - 1;
+ raw.hor_sync_start = timing->hor_sync_start - 1;
+ raw.hor_sync_end = timing->hor_sync_end - 1;
+ raw.ver_total = timing->ver_total - 1;
+ raw.ver_addr = timing->ver_addr - 1;
+ raw.ver_blank_start = timing->ver_blank_start - 1;
+ raw.ver_blank_end = timing->ver_blank_end - 1;
+ raw.ver_sync_start = timing->ver_sync_start - 1;
+ raw.ver_sync_end = timing->ver_sync_end - 1;
+
+ via_write_reg(VIACR, 0x50, raw.hor_total & 0xFF);
+ via_write_reg(VIACR, 0x51, raw.hor_addr & 0xFF);
+ via_write_reg(VIACR, 0x52, raw.hor_blank_start & 0xFF);
+ via_write_reg(VIACR, 0x53, raw.hor_blank_end & 0xFF);
+ via_write_reg(VIACR, 0x54, (raw.hor_blank_start >> 8 & 0x07)
+ | (raw.hor_blank_end >> (8 - 3) & 0x38)
+ | (raw.hor_sync_start >> (8 - 6) & 0xC0));
+ via_write_reg_mask(VIACR, 0x55, (raw.hor_total >> 8 & 0x0F)
+ | (raw.hor_addr >> (8 - 4) & 0x70), 0x7F);
+ via_write_reg(VIACR, 0x56, raw.hor_sync_start & 0xFF);
+ via_write_reg(VIACR, 0x57, raw.hor_sync_end & 0xFF);
+ via_write_reg(VIACR, 0x58, raw.ver_total & 0xFF);
+ via_write_reg(VIACR, 0x59, raw.ver_addr & 0xFF);
+ via_write_reg(VIACR, 0x5A, raw.ver_blank_start & 0xFF);
+ via_write_reg(VIACR, 0x5B, raw.ver_blank_end & 0xFF);
+ via_write_reg(VIACR, 0x5C, (raw.ver_blank_start >> 8 & 0x07)
+ | (raw.ver_blank_end >> (8 - 3) & 0x38)
+ | (raw.hor_sync_end >> (8 - 6) & 0x40)
+ | (raw.hor_sync_start >> (10 - 7) & 0x80));
+ via_write_reg(VIACR, 0x5D, (raw.ver_total >> 8 & 0x07)
+ | (raw.ver_addr >> (8 - 3) & 0x38)
+ | (raw.hor_blank_end >> (11 - 6) & 0x40)
+ | (raw.hor_sync_start >> (11 - 7) & 0x80));
+ via_write_reg(VIACR, 0x5E, raw.ver_sync_start & 0xFF);
+ via_write_reg(VIACR, 0x5F, (raw.ver_sync_end & 0x1F)
+ | (raw.ver_sync_start >> (8 - 5) & 0xE0));
+}
+
void via_set_primary_address(u32 addr)
{
DEBUG_MSG(KERN_DEBUG "via_set_primary_address(0x%08X)\n", addr);
diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h
index ae35cfdeb37c..06e09fe351ae 100644
--- a/drivers/video/via/via_modesetting.h
+++ b/drivers/video/via/via_modesetting.h
@@ -28,6 +28,29 @@
#include <linux/types.h>
+
+#define VIA_PITCH_SIZE (1<<3)
+#define VIA_PITCH_MAX 0x3FF8
+
+
+struct display_timing {
+ u16 hor_total;
+ u16 hor_addr;
+ u16 hor_blank_start;
+ u16 hor_blank_end;
+ u16 hor_sync_start;
+ u16 hor_sync_end;
+ u16 ver_total;
+ u16 ver_addr;
+ u16 ver_blank_start;
+ u16 ver_blank_end;
+ u16 ver_sync_start;
+ u16 ver_sync_end;
+};
+
+
+void via_set_primary_timing(const struct display_timing *timing);
+void via_set_secondary_timing(const struct display_timing *timing);
void via_set_primary_address(u32 addr);
void via_set_secondary_address(u32 addr);
void via_set_primary_pitch(u32 pitch);
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index 53aa4430d86e..a13c258bd32f 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -38,8 +38,6 @@ static char *viafb_mode1;
static int viafb_bpp = 32;
static int viafb_bpp1 = 32;
-static unsigned int viafb_second_xres = 640;
-static unsigned int viafb_second_yres = 480;
static unsigned int viafb_second_offset;
static int viafb_second_size;
@@ -151,7 +149,8 @@ static void viafb_update_fix(struct fb_info *info)
info->fix.visual =
bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
- info->fix.line_length = (info->var.xres_virtual * bpp / 8 + 7) & ~7;
+ info->fix.line_length = ALIGN(info->var.xres_virtual * bpp / 8,
+ VIA_PITCH_SIZE);
}
static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix,
@@ -200,7 +199,6 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
int depth, refresh;
- struct VideoModeTable *vmode_entry;
struct viafb_par *ppar = info->par;
u32 line;
@@ -210,8 +208,10 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
if (var->vmode & FB_VMODE_INTERLACED || var->vmode & FB_VMODE_DOUBLE)
return -EINVAL;
- vmode_entry = viafb_get_mode(var->xres, var->yres);
- if (!vmode_entry) {
+ /* the refresh rate is not important here, as we only want to know
+ * whether the resolution exists
+ */
+ if (!viafb_get_best_mode(var->xres, var->yres, 60)) {
DEBUG_MSG(KERN_INFO
"viafb: Mode %dx%dx%d not supported!!\n",
var->xres, var->yres, var->bits_per_pixel);
@@ -238,8 +238,12 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
depth = 24;
viafb_fill_var_color_info(var, depth);
- line = (var->xres_virtual * var->bits_per_pixel / 8 + 7) & ~7;
- if (line * var->yres_virtual > ppar->memsize)
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+
+ line = ALIGN(var->xres_virtual * var->bits_per_pixel / 8,
+ VIA_PITCH_SIZE);
+ if (line > VIA_PITCH_MAX || line * var->yres_virtual > ppar->memsize)
return -EINVAL;
/* Based on var passed in to calculate the refresh,
@@ -249,7 +253,8 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
get_var_refresh(var));
/* Adjust var according to our driver's own table */
- viafb_fill_var_timing_info(var, refresh, vmode_entry);
+ viafb_fill_var_timing_info(var,
+ viafb_get_best_mode(var->xres, var->yres, refresh));
if (var->accel_flags & FB_ACCELF_TEXT &&
!ppar->shared->vdev->engine_mmio)
var->accel_flags = 0;
@@ -260,7 +265,6 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
static int viafb_set_par(struct fb_info *info)
{
struct viafb_par *viapar = info->par;
- struct VideoModeTable *vmode_entry, *vmode_entry1 = NULL;
int refresh;
DEBUG_MSG(KERN_INFO "viafb_set_par!\n");
@@ -269,10 +273,7 @@ static int viafb_set_par(struct fb_info *info)
viafb_update_device_setting(viafbinfo->var.xres, viafbinfo->var.yres,
viafbinfo->var.bits_per_pixel, 0);
- vmode_entry = viafb_get_mode(viafbinfo->var.xres, viafbinfo->var.yres);
if (viafb_dual_fb) {
- vmode_entry1 = viafb_get_mode(viafbinfo1->var.xres,
- viafbinfo1->var.yres);
viafb_update_device_setting(viafbinfo1->var.xres,
viafbinfo1->var.yres, viafbinfo1->var.bits_per_pixel,
1);
@@ -280,8 +281,6 @@ static int viafb_set_par(struct fb_info *info)
DEBUG_MSG(KERN_INFO
"viafb_second_xres = %d, viafb_second_yres = %d, bpp = %d\n",
viafb_second_xres, viafb_second_yres, viafb_bpp1);
- vmode_entry1 = viafb_get_mode(viafb_second_xres,
- viafb_second_yres);
viafb_update_device_setting(viafb_second_xres,
viafb_second_yres, viafb_bpp1, 1);
@@ -289,7 +288,8 @@ static int viafb_set_par(struct fb_info *info)
refresh = viafb_get_refresh(info->var.xres, info->var.yres,
get_var_refresh(&info->var));
- if (vmode_entry) {
+ if (viafb_get_best_mode(viafbinfo->var.xres, viafbinfo->var.yres,
+ refresh)) {
if (viafb_dual_fb && viapar->iga_path == IGA2) {
viafb_bpp1 = info->var.bits_per_pixel;
viafb_refresh1 = refresh;
@@ -302,8 +302,7 @@ static int viafb_set_par(struct fb_info *info)
info->flags &= ~FBINFO_HWACCEL_DISABLED;
else
info->flags |= FBINFO_HWACCEL_DISABLED;
- viafb_setmode(vmode_entry, info->var.bits_per_pixel,
- vmode_entry1, viafb_bpp1);
+ viafb_setmode(info->var.bits_per_pixel, viafb_bpp1);
viafb_pan_display(&info->var, info);
}
@@ -348,8 +347,9 @@ static int viafb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct viafb_par *viapar = info->par;
- u32 vram_addr = (var->yoffset * var->xres_virtual + var->xoffset)
- * (var->bits_per_pixel / 8) + viapar->vram_addr;
+ u32 vram_addr = viapar->vram_addr
+ + var->yoffset * info->fix.line_length
+ + var->xoffset * info->var.bits_per_pixel / 8;
DEBUG_MSG(KERN_DEBUG "viafb_pan_display, address = %d\n", vram_addr);
if (!viafb_dual_fb) {
@@ -1158,7 +1158,8 @@ static ssize_t viafb_dvp0_proc_write(struct file *file,
for (i = 0; i < 3; i++) {
value = strsep(&pbuf, " ");
if (value != NULL) {
- strict_strtoul(value, 0, (unsigned long *)&reg_val);
+ if (kstrtou8(value, 0, &reg_val) < 0)
+ return -EINVAL;
DEBUG_MSG(KERN_INFO "DVP0:reg_val[%l]=:%x\n", i,
reg_val);
switch (i) {
@@ -1228,7 +1229,8 @@ static ssize_t viafb_dvp1_proc_write(struct file *file,
for (i = 0; i < 3; i++) {
value = strsep(&pbuf, " ");
if (value != NULL) {
- strict_strtoul(value, 0, (unsigned long *)&reg_val);
+ if (kstrtou8(value, 0, &reg_val) < 0)
+ return -EINVAL;
switch (i) {
case 0:
viafb_write_reg_mask(CR9B, VIACR,
@@ -1286,7 +1288,8 @@ static ssize_t viafb_dfph_proc_write(struct file *file,
if (copy_from_user(&buf[0], buffer, length))
return -EFAULT;
buf[length - 1] = '\0'; /*Ensure end string */
- strict_strtoul(&buf[0], 0, (unsigned long *)&reg_val);
+ if (kstrtou8(buf, 0, &reg_val) < 0)
+ return -EINVAL;
viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f);
return count;
}
@@ -1325,7 +1328,8 @@ static ssize_t viafb_dfpl_proc_write(struct file *file,
if (copy_from_user(&buf[0], buffer, length))
return -EFAULT;
buf[length - 1] = '\0'; /*Ensure end string */
- strict_strtoul(&buf[0], 0, (unsigned long *)&reg_val);
+ if (kstrtou8(buf, 0, &reg_val) < 0)
+ return -EINVAL;
viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f);
return count;
}
@@ -1394,8 +1398,8 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
for (i = 0; i < 2; i++) {
value = strsep(&pbuf, " ");
if (value != NULL) {
- strict_strtoul(value, 0,
- (unsigned long *)&reg_val.Data);
+ if (kstrtou8(value, 0, &reg_val.Data) < 0)
+ return -EINVAL;
switch (i) {
case 0:
reg_val.Index = 0x08;
@@ -1431,8 +1435,8 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
for (i = 0; i < 2; i++) {
value = strsep(&pbuf, " ");
if (value != NULL) {
- strict_strtoul(value, 0,
- (unsigned long *)&reg_val.Data);
+ if (kstrtou8(value, 0, &reg_val.Data) < 0)
+ return -EINVAL;
switch (i) {
case 0:
reg_val.Index = 0x08;
@@ -1729,7 +1733,6 @@ static struct viafb_pm_hooks viafb_fb_pm_hooks = {
int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
{
u32 default_xres, default_yres;
- struct VideoModeTable *vmode_entry;
struct fb_var_screeninfo default_var;
int rc;
u32 viafb_par_length;
@@ -1802,7 +1805,6 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
}
parse_mode(viafb_mode, &default_xres, &default_yres);
- vmode_entry = viafb_get_mode(default_xres, default_yres);
if (viafb_SAMM_ON == 1)
parse_mode(viafb_mode1, &viafb_second_xres,
&viafb_second_yres);
@@ -1812,9 +1814,8 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
default_var.xres_virtual = default_xres;
default_var.yres_virtual = default_yres;
default_var.bits_per_pixel = viafb_bpp;
- viafb_fill_var_timing_info(&default_var, viafb_get_refresh(
- default_var.xres, default_var.yres, viafb_refresh),
- viafb_get_mode(default_var.xres, default_var.yres));
+ viafb_fill_var_timing_info(&default_var, viafb_get_best_mode(
+ default_var.xres, default_var.yres, viafb_refresh));
viafb_setup_fixinfo(&viafbinfo->fix, viaparinfo);
viafbinfo->var = default_var;
@@ -1853,9 +1854,8 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
default_var.xres_virtual = viafb_second_xres;
default_var.yres_virtual = viafb_second_yres;
default_var.bits_per_pixel = viafb_bpp1;
- viafb_fill_var_timing_info(&default_var, viafb_get_refresh(
- default_var.xres, default_var.yres, viafb_refresh1),
- viafb_get_mode(default_var.xres, default_var.yres));
+ viafb_fill_var_timing_info(&default_var, viafb_get_best_mode(
+ default_var.xres, default_var.yres, viafb_refresh1));
viafb_setup_fixinfo(&viafbinfo1->fix, viaparinfo1);
viafb_check_var(&default_var, viafbinfo1);
@@ -1950,61 +1950,67 @@ static int __init viafb_setup(void)
if (!*this_opt)
continue;
- if (!strncmp(this_opt, "viafb_mode1=", 12))
+ if (!strncmp(this_opt, "viafb_mode1=", 12)) {
viafb_mode1 = kstrdup(this_opt + 12, GFP_KERNEL);
- else if (!strncmp(this_opt, "viafb_mode=", 11))
+ } else if (!strncmp(this_opt, "viafb_mode=", 11)) {
viafb_mode = kstrdup(this_opt + 11, GFP_KERNEL);
- else if (!strncmp(this_opt, "viafb_bpp1=", 11))
- strict_strtoul(this_opt + 11, 0,
- (unsigned long *)&viafb_bpp1);
- else if (!strncmp(this_opt, "viafb_bpp=", 10))
- strict_strtoul(this_opt + 10, 0,
- (unsigned long *)&viafb_bpp);
- else if (!strncmp(this_opt, "viafb_refresh1=", 15))
- strict_strtoul(this_opt + 15, 0,
- (unsigned long *)&viafb_refresh1);
- else if (!strncmp(this_opt, "viafb_refresh=", 14))
- strict_strtoul(this_opt + 14, 0,
- (unsigned long *)&viafb_refresh);
- else if (!strncmp(this_opt, "viafb_lcd_dsp_method=", 21))
- strict_strtoul(this_opt + 21, 0,
- (unsigned long *)&viafb_lcd_dsp_method);
- else if (!strncmp(this_opt, "viafb_lcd_panel_id=", 19))
- strict_strtoul(this_opt + 19, 0,
- (unsigned long *)&viafb_lcd_panel_id);
- else if (!strncmp(this_opt, "viafb_accel=", 12))
- strict_strtoul(this_opt + 12, 0,
- (unsigned long *)&viafb_accel);
- else if (!strncmp(this_opt, "viafb_SAMM_ON=", 14))
- strict_strtoul(this_opt + 14, 0,
- (unsigned long *)&viafb_SAMM_ON);
- else if (!strncmp(this_opt, "viafb_active_dev=", 17))
+ } else if (!strncmp(this_opt, "viafb_bpp1=", 11)) {
+ if (kstrtouint(this_opt + 11, 0, &viafb_bpp1) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_bpp=", 10)) {
+ if (kstrtouint(this_opt + 10, 0, &viafb_bpp) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_refresh1=", 15)) {
+ if (kstrtoint(this_opt + 15, 0, &viafb_refresh1) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_refresh=", 14)) {
+ if (kstrtoint(this_opt + 14, 0, &viafb_refresh) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_lcd_dsp_method=", 21)) {
+ if (kstrtoint(this_opt + 21, 0,
+ &viafb_lcd_dsp_method) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_lcd_panel_id=", 19)) {
+ if (kstrtoint(this_opt + 19, 0,
+ &viafb_lcd_panel_id) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_accel=", 12)) {
+ if (kstrtoint(this_opt + 12, 0, &viafb_accel) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_SAMM_ON=", 14)) {
+ if (kstrtoint(this_opt + 14, 0, &viafb_SAMM_ON) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_active_dev=", 17)) {
viafb_active_dev = kstrdup(this_opt + 17, GFP_KERNEL);
- else if (!strncmp(this_opt,
- "viafb_display_hardware_layout=", 30))
- strict_strtoul(this_opt + 30, 0,
- (unsigned long *)&viafb_display_hardware_layout);
- else if (!strncmp(this_opt, "viafb_second_size=", 18))
- strict_strtoul(this_opt + 18, 0,
- (unsigned long *)&viafb_second_size);
- else if (!strncmp(this_opt,
- "viafb_platform_epia_dvi=", 24))
- strict_strtoul(this_opt + 24, 0,
- (unsigned long *)&viafb_platform_epia_dvi);
- else if (!strncmp(this_opt,
- "viafb_device_lcd_dualedge=", 26))
- strict_strtoul(this_opt + 26, 0,
- (unsigned long *)&viafb_device_lcd_dualedge);
- else if (!strncmp(this_opt, "viafb_bus_width=", 16))
- strict_strtoul(this_opt + 16, 0,
- (unsigned long *)&viafb_bus_width);
- else if (!strncmp(this_opt, "viafb_lcd_mode=", 15))
- strict_strtoul(this_opt + 15, 0,
- (unsigned long *)&viafb_lcd_mode);
- else if (!strncmp(this_opt, "viafb_lcd_port=", 15))
+ } else if (!strncmp(this_opt,
+ "viafb_display_hardware_layout=", 30)) {
+ if (kstrtoint(this_opt + 30, 0,
+ &viafb_display_hardware_layout) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_second_size=", 18)) {
+ if (kstrtoint(this_opt + 18, 0, &viafb_second_size) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt,
+ "viafb_platform_epia_dvi=", 24)) {
+ if (kstrtoint(this_opt + 24, 0,
+ &viafb_platform_epia_dvi) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt,
+ "viafb_device_lcd_dualedge=", 26)) {
+ if (kstrtoint(this_opt + 26, 0,
+ &viafb_device_lcd_dualedge) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_bus_width=", 16)) {
+ if (kstrtoint(this_opt + 16, 0, &viafb_bus_width) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_lcd_mode=", 15)) {
+ if (kstrtoint(this_opt + 15, 0, &viafb_lcd_mode) < 0)
+ return -EINVAL;
+ } else if (!strncmp(this_opt, "viafb_lcd_port=", 15)) {
viafb_lcd_port = kstrdup(this_opt + 15, GFP_KERNEL);
- else if (!strncmp(this_opt, "viafb_dvi_port=", 15))
+ } else if (!strncmp(this_opt, "viafb_dvi_port=", 15)) {
viafb_dvi_port = kstrdup(this_opt + 15, GFP_KERNEL);
+ }
}
return 0;
}
@@ -2028,9 +2034,9 @@ int __init viafb_init(void)
return r;
#endif
if (parse_mode(viafb_mode, &dummy_x, &dummy_y)
- || !viafb_get_mode(dummy_x, dummy_y)
+ || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh)
|| parse_mode(viafb_mode1, &dummy_x, &dummy_y)
- || !viafb_get_mode(dummy_x, dummy_y)
+ || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1)
|| viafb_bpp < 0 || viafb_bpp > 32
|| viafb_bpp1 < 0 || viafb_bpp1 > 32
|| parse_active_dev())
diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c
index 58df74e1417e..0911cac1b2ff 100644
--- a/drivers/video/via/viamode.c
+++ b/drivers/video/via/viamode.c
@@ -281,7 +281,7 @@ static struct crt_mode_table CRTM640x480[] = {
/*r_rate,hsp,vsp */
/*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
{REFRESH_60, M640X480_R60_HSP, M640X480_R60_VSP,
- {800, 640, 648, 144, 656, 96, 525, 480, 480, 45, 490, 2} },
+ {800, 640, 640, 160, 656, 96, 525, 480, 480, 45, 490, 2} },
{REFRESH_75, M640X480_R75_HSP, M640X480_R75_VSP,
{840, 640, 640, 200, 656, 64, 500, 480, 480, 20, 481, 3} },
{REFRESH_85, M640X480_R85_HSP, M640X480_R85_VSP,
@@ -863,26 +863,56 @@ int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs);
int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table);
-struct VideoModeTable *viafb_get_mode(int hres, int vres)
+static struct VideoModeTable *get_modes(struct VideoModeTable *vmt, int n,
+ int hres, int vres)
{
- u32 i;
- for (i = 0; i < ARRAY_SIZE(viafb_modes); i++)
- if (viafb_modes[i].mode_array &&
- viafb_modes[i].crtc[0].crtc.hor_addr == hres &&
- viafb_modes[i].crtc[0].crtc.ver_addr == vres)
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (vmt[i].mode_array &&
+ vmt[i].crtc[0].crtc.hor_addr == hres &&
+ vmt[i].crtc[0].crtc.ver_addr == vres)
return &viafb_modes[i];
return NULL;
}
-struct VideoModeTable *viafb_get_rb_mode(int hres, int vres)
+static struct crt_mode_table *get_best_mode(struct VideoModeTable *vmt,
+ int refresh)
{
- u32 i;
- for (i = 0; i < ARRAY_SIZE(viafb_rb_modes); i++)
- if (viafb_rb_modes[i].mode_array &&
- viafb_rb_modes[i].crtc[0].crtc.hor_addr == hres &&
- viafb_rb_modes[i].crtc[0].crtc.ver_addr == vres)
- return &viafb_rb_modes[i];
+ struct crt_mode_table *best;
+ int i;
- return NULL;
+ if (!vmt)
+ return NULL;
+
+ best = &vmt->crtc[0];
+ for (i = 1; i < vmt->mode_array; i++) {
+ if (abs(vmt->crtc[i].refresh_rate - refresh)
+ < abs(best->refresh_rate - refresh))
+ best = &vmt->crtc[i];
+ }
+
+ return best;
+}
+
+static struct VideoModeTable *viafb_get_mode(int hres, int vres)
+{
+ return get_modes(viafb_modes, ARRAY_SIZE(viafb_modes), hres, vres);
+}
+
+struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh)
+{
+ return get_best_mode(viafb_get_mode(hres, vres), refresh);
+}
+
+static struct VideoModeTable *viafb_get_rb_mode(int hres, int vres)
+{
+ return get_modes(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), hres,
+ vres);
+}
+
+struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh)
+{
+ return get_best_mode(viafb_get_rb_mode(hres, vres), refresh);
}
diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h
index 3751289eb450..5917a2b00e1b 100644
--- a/drivers/video/via/viamode.h
+++ b/drivers/video/via/viamode.h
@@ -60,7 +60,7 @@ extern struct io_reg PM1024x768[];
extern struct patch_table res_patch_table[];
extern struct VPITTable VPIT;
-struct VideoModeTable *viafb_get_mode(int hres, int vres);
-struct VideoModeTable *viafb_get_rb_mode(int hres, int vres);
+struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh);
+struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh);
#endif /* __VIAMODE_H__ */
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
index 0e120d67eb65..777c21dd7a6b 100644
--- a/drivers/video/vt8500lcdfb.c
+++ b/drivers/video/vt8500lcdfb.c
@@ -210,8 +210,8 @@ static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
writel((1 << 31)
- | (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
- | (off >> 2), fbi->regbase + 0x20);
+ | (((info->var.xres_virtual - info->var.xres) * pixlen / 4) << 20)
+ | (off >> 2), fbi->regbase + 0x20);
return 0;
}
@@ -355,7 +355,7 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
goto failed_free_palette;
}
- ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+ ret = request_irq(irq, vt8500lcd_handle_irq, 0, "LCD", fbi);
if (ret) {
dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
ret = -EBUSY;
diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c
index f9b3e3dc2421..4e74d262cf3e 100644
--- a/drivers/video/vt8623fb.c
+++ b/drivers/video/vt8623fb.c
@@ -620,13 +620,14 @@ static int vt8623fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *i
unsigned int offset;
/* Calculate the offset */
- if (var->bits_per_pixel == 0) {
- offset = (var->yoffset / 16) * var->xres_virtual + var->xoffset;
+ if (info->var.bits_per_pixel == 0) {
+ offset = (var->yoffset / 16) * info->var.xres_virtual
+ + var->xoffset;
offset = offset >> 3;
} else {
offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset * var->bits_per_pixel / 8);
- offset = offset >> ((var->bits_per_pixel == 4) ? 2 : 1);
+ (var->xoffset * info->var.bits_per_pixel / 8);
+ offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 1);
}
/* Set the offset */
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 77dea015ff69..fcb6cd90f64d 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -23,7 +23,6 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 57e493b1bd20..816ed08e7cf3 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -35,4 +35,15 @@ config VIRTIO_BALLOON
If unsure, say M.
+ config VIRTIO_MMIO
+ tristate "Platform bus driver for memory mapped virtio devices (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ select VIRTIO
+ select VIRTIO_RING
+ ---help---
+ This drivers provides support for memory mapped virtio
+ platform device driver.
+
+ If unsure, say N.
+
endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6738c446c199..5a4c63cfd380 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_VIRTIO) += virtio.o
obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
+obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
new file mode 100644
index 000000000000..acc5e43c373e
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.c
@@ -0,0 +1,479 @@
+/*
+ * Virtio memory mapped device driver
+ *
+ * Copyright 2011, ARM Ltd.
+ *
+ * This module allows virtio devices to be used over a virtual, memory mapped
+ * platform device.
+ *
+ * Registers layout (all 32-bit wide):
+ *
+ * offset d. name description
+ * ------ -- ---------------- -----------------
+ *
+ * 0x000 R MagicValue Magic value "virt"
+ * 0x004 R Version Device version (current max. 1)
+ * 0x008 R DeviceID Virtio device ID
+ * 0x00c R VendorID Virtio vendor ID
+ *
+ * 0x010 R HostFeatures Features supported by the host
+ * 0x014 W HostFeaturesSel Set of host features to access via HostFeatures
+ *
+ * 0x020 W GuestFeatures Features activated by the guest
+ * 0x024 W GuestFeaturesSel Set of activated features to set via GuestFeatures
+ * 0x028 W GuestPageSize Size of guest's memory page in bytes
+ *
+ * 0x030 W QueueSel Queue selector
+ * 0x034 R QueueNumMax Maximum size of the currently selected queue
+ * 0x038 W QueueNum Queue size for the currently selected queue
+ * 0x03c W QueueAlign Used Ring alignment for the current queue
+ * 0x040 RW QueuePFN PFN for the currently selected queue
+ *
+ * 0x050 W QueueNotify Queue notifier
+ * 0x060 R InterruptStatus Interrupt status register
+ * 0x060 W InterruptACK Interrupt acknowledge register
+ * 0x070 RW Status Device status register
+ *
+ * 0x100+ RW Device-specific configuration space
+ *
+ * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_mmio.h>
+#include <linux/virtio_ring.h>
+
+
+
+/* The alignment to use between consumer and producer parts of vring.
+ * Currently hardcoded to the page size. */
+#define VIRTIO_MMIO_VRING_ALIGN PAGE_SIZE
+
+
+
+#define to_virtio_mmio_device(_plat_dev) \
+ container_of(_plat_dev, struct virtio_mmio_device, vdev)
+
+struct virtio_mmio_device {
+ struct virtio_device vdev;
+ struct platform_device *pdev;
+
+ void __iomem *base;
+ unsigned long version;
+
+ /* a list of queues so we can dispatch IRQs */
+ spinlock_t lock;
+ struct list_head virtqueues;
+};
+
+struct virtio_mmio_vq_info {
+ /* the actual virtqueue */
+ struct virtqueue *vq;
+
+ /* the number of entries in the queue */
+ unsigned int num;
+
+ /* the index of the queue */
+ int queue_index;
+
+ /* the virtual address of the ring queue */
+ void *queue;
+
+ /* the list node for the virtqueues list */
+ struct list_head node;
+};
+
+
+
+/* Configuration interface */
+
+static u32 vm_get_features(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* TODO: Features > 32 bits */
+ writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL);
+
+ return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES);
+}
+
+static void vm_finalize_features(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ int i;
+
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
+ for (i = 0; i < ARRAY_SIZE(vdev->features); i++) {
+ writel(i, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SET);
+ writel(vdev->features[i],
+ vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
+ }
+}
+
+static void vm_get(struct virtio_device *vdev, unsigned offset,
+ void *buf, unsigned len)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static void vm_set(struct virtio_device *vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ const u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static u8 vm_get_status(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ return readl(vm_dev->base + VIRTIO_MMIO_STATUS) & 0xff;
+}
+
+static void vm_set_status(struct virtio_device *vdev, u8 status)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* We should never be setting status to 0. */
+ BUG_ON(status == 0);
+
+ writel(status, vm_dev->base + VIRTIO_MMIO_STATUS);
+}
+
+static void vm_reset(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* 0 status means a reset. */
+ writel(0, vm_dev->base + VIRTIO_MMIO_STATUS);
+}
+
+
+
+/* Transport interface */
+
+/* the notify function used when creating a virt queue */
+static void vm_notify(struct virtqueue *vq)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
+ struct virtio_mmio_vq_info *info = vq->priv;
+
+ /* We write the queue's selector into the notification register to
+ * signal the other end */
+ writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
+}
+
+/* Notify all virtqueues on an interrupt. */
+static irqreturn_t vm_interrupt(int irq, void *opaque)
+{
+ struct virtio_mmio_device *vm_dev = opaque;
+ struct virtio_mmio_vq_info *info;
+ struct virtio_driver *vdrv = container_of(vm_dev->vdev.dev.driver,
+ struct virtio_driver, driver);
+ unsigned long status;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+
+ /* Read and acknowledge interrupts */
+ status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS);
+ writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK);
+
+ if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)
+ && vdrv && vdrv->config_changed) {
+ vdrv->config_changed(&vm_dev->vdev);
+ ret = IRQ_HANDLED;
+ }
+
+ if (likely(status & VIRTIO_MMIO_INT_VRING)) {
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_for_each_entry(info, &vm_dev->virtqueues, node)
+ ret |= vring_interrupt(irq, info->vq);
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+ }
+
+ return ret;
+}
+
+
+
+static void vm_del_vq(struct virtqueue *vq)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
+ struct virtio_mmio_vq_info *info = vq->priv;
+ unsigned long flags, size;
+
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_del(&info->node);
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+
+ vring_del_virtqueue(vq);
+
+ /* Select and deactivate the queue */
+ writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+ writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+
+ size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN));
+ free_pages_exact(info->queue, size);
+ kfree(info);
+}
+
+static void vm_del_vqs(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ vm_del_vq(vq);
+
+ free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
+}
+
+
+
+static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ struct virtio_mmio_vq_info *info;
+ struct virtqueue *vq;
+ unsigned long flags, size;
+ int err;
+
+ /* Select the queue we're interested in */
+ writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+
+ /* Queue shouldn't already be set up. */
+ if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN)) {
+ err = -ENOENT;
+ goto error_available;
+ }
+
+ /* Allocate and fill out our active queue description */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ err = -ENOMEM;
+ goto error_kmalloc;
+ }
+ info->queue_index = index;
+
+ /* Allocate pages for the queue - start with a queue as big as
+ * possible (limited by maximum size allowed by device), drop down
+ * to a minimal size, just big enough to fit descriptor table
+ * and two rings (which makes it "alignment_size * 2")
+ */
+ info->num = readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX);
+ while (1) {
+ size = PAGE_ALIGN(vring_size(info->num,
+ VIRTIO_MMIO_VRING_ALIGN));
+ /* Already smallest possible allocation? */
+ if (size <= VIRTIO_MMIO_VRING_ALIGN * 2) {
+ err = -ENOMEM;
+ goto error_alloc_pages;
+ }
+
+ info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (info->queue)
+ break;
+
+ info->num /= 2;
+ }
+
+ /* Activate the queue */
+ writel(info->num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
+ writel(VIRTIO_MMIO_VRING_ALIGN,
+ vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
+ writel(virt_to_phys(info->queue) >> PAGE_SHIFT,
+ vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+
+ /* Create the vring */
+ vq = vring_new_virtqueue(info->num, VIRTIO_MMIO_VRING_ALIGN,
+ vdev, info->queue, vm_notify, callback, name);
+ if (!vq) {
+ err = -ENOMEM;
+ goto error_new_virtqueue;
+ }
+
+ vq->priv = info;
+ info->vq = vq;
+
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_add(&info->node, &vm_dev->virtqueues);
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+
+ return vq;
+
+error_new_virtqueue:
+ writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+ free_pages_exact(info->queue, size);
+error_alloc_pages:
+ kfree(info);
+error_kmalloc:
+error_available:
+ return ERR_PTR(err);
+}
+
+static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ unsigned int irq = platform_get_irq(vm_dev->pdev, 0);
+ int i, err;
+
+ err = request_irq(irq, vm_interrupt, IRQF_SHARED,
+ dev_name(&vdev->dev), vm_dev);
+ if (err)
+ return err;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = vm_setup_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i])) {
+ vm_del_vqs(vdev);
+ return PTR_ERR(vqs[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+
+static struct virtio_config_ops virtio_mmio_config_ops = {
+ .get = vm_get,
+ .set = vm_set,
+ .get_status = vm_get_status,
+ .set_status = vm_set_status,
+ .reset = vm_reset,
+ .find_vqs = vm_find_vqs,
+ .del_vqs = vm_del_vqs,
+ .get_features = vm_get_features,
+ .finalize_features = vm_finalize_features,
+};
+
+
+
+/* Platform device */
+
+static int __devinit virtio_mmio_probe(struct platform_device *pdev)
+{
+ struct virtio_mmio_device *vm_dev;
+ struct resource *mem;
+ unsigned long magic;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+
+ if (!devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), pdev->name))
+ return -EBUSY;
+
+ vm_dev = devm_kzalloc(&pdev->dev, sizeof(*vm_dev), GFP_KERNEL);
+ if (!vm_dev)
+ return -ENOMEM;
+
+ vm_dev->vdev.dev.parent = &pdev->dev;
+ vm_dev->vdev.config = &virtio_mmio_config_ops;
+ vm_dev->pdev = pdev;
+ INIT_LIST_HEAD(&vm_dev->virtqueues);
+ spin_lock_init(&vm_dev->lock);
+
+ vm_dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (vm_dev->base == NULL)
+ return -EFAULT;
+
+ /* Check magic value */
+ magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE);
+ if (memcmp(&magic, "virt", 4) != 0) {
+ dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic);
+ return -ENODEV;
+ }
+
+ /* Check device version */
+ vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION);
+ if (vm_dev->version != 1) {
+ dev_err(&pdev->dev, "Version %ld not supported!\n",
+ vm_dev->version);
+ return -ENXIO;
+ }
+
+ vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
+ vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
+
+ writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+
+ platform_set_drvdata(pdev, vm_dev);
+
+ return register_virtio_device(&vm_dev->vdev);
+}
+
+static int __devexit virtio_mmio_remove(struct platform_device *pdev)
+{
+ struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
+
+ unregister_virtio_device(&vm_dev->vdev);
+
+ return 0;
+}
+
+
+
+/* Platform driver */
+
+static struct of_device_id virtio_mmio_match[] = {
+ { .compatible = "virtio,mmio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, virtio_mmio_match);
+
+static struct platform_driver virtio_mmio_driver = {
+ .probe = virtio_mmio_probe,
+ .remove = __devexit_p(virtio_mmio_remove),
+ .driver = {
+ .name = "virtio-mmio",
+ .owner = THIS_MODULE,
+ .of_match_table = virtio_mmio_match,
+ },
+};
+
+static int __init virtio_mmio_init(void)
+{
+ return platform_driver_register(&virtio_mmio_driver);
+}
+
+static void __exit virtio_mmio_exit(void)
+{
+ platform_driver_unregister(&virtio_mmio_driver);
+}
+
+module_init(virtio_mmio_init);
+module_exit(virtio_mmio_exit);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+MODULE_DESCRIPTION("Platform bus driver for memory mapped virtio devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 4bcc8b82640b..79a31e5b4b68 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -415,9 +415,13 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
}
}
- spin_lock_irqsave(&vp_dev->lock, flags);
- list_add(&info->node, &vp_dev->virtqueues);
- spin_unlock_irqrestore(&vp_dev->lock, flags);
+ if (callback) {
+ spin_lock_irqsave(&vp_dev->lock, flags);
+ list_add(&info->node, &vp_dev->virtqueues);
+ spin_unlock_irqrestore(&vp_dev->lock, flags);
+ } else {
+ INIT_LIST_HEAD(&info->node);
+ }
return vq;
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c
index 483d45180911..5754c9a4f58b 100644
--- a/drivers/w1/slaves/w1_ds2760.c
+++ b/drivers/w1/slaves/w1_ds2760.c
@@ -114,43 +114,7 @@ static struct bin_attribute w1_ds2760_bin_attr = {
.read = w1_ds2760_read_bin,
};
-static DEFINE_IDR(bat_idr);
-static DEFINE_MUTEX(bat_idr_lock);
-
-static int new_bat_id(void)
-{
- int ret;
-
- while (1) {
- int id;
-
- ret = idr_pre_get(&bat_idr, GFP_KERNEL);
- if (ret == 0)
- return -ENOMEM;
-
- mutex_lock(&bat_idr_lock);
- ret = idr_get_new(&bat_idr, NULL, &id);
- mutex_unlock(&bat_idr_lock);
-
- if (ret == 0) {
- ret = id & MAX_ID_MASK;
- break;
- } else if (ret == -EAGAIN) {
- continue;
- } else {
- break;
- }
- }
-
- return ret;
-}
-
-static void release_bat_id(int id)
-{
- mutex_lock(&bat_idr_lock);
- idr_remove(&bat_idr, id);
- mutex_unlock(&bat_idr_lock);
-}
+static DEFINE_IDA(bat_ida);
static int w1_ds2760_add_slave(struct w1_slave *sl)
{
@@ -158,7 +122,7 @@ static int w1_ds2760_add_slave(struct w1_slave *sl)
int id;
struct platform_device *pdev;
- id = new_bat_id();
+ id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto noid;
@@ -187,7 +151,7 @@ bin_attr_failed:
pdev_add_failed:
platform_device_unregister(pdev);
pdev_alloc_failed:
- release_bat_id(id);
+ ida_simple_remove(&bat_ida, id);
noid:
success:
return ret;
@@ -199,7 +163,7 @@ static void w1_ds2760_remove_slave(struct w1_slave *sl)
int id = pdev->id;
platform_device_unregister(pdev);
- release_bat_id(id);
+ ida_simple_remove(&bat_ida, id);
sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
}
@@ -217,14 +181,14 @@ static int __init w1_ds2760_init(void)
{
printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor "
" chip - (c) 2004-2005, Szabolcs Gyurko\n");
- idr_init(&bat_idr);
+ ida_init(&bat_ida);
return w1_register_family(&w1_ds2760_family);
}
static void __exit w1_ds2760_exit(void)
{
w1_unregister_family(&w1_ds2760_family);
- idr_destroy(&bat_idr);
+ ida_destroy(&bat_ida);
}
EXPORT_SYMBOL(w1_ds2760_read);
diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c
index 274c8f38303f..39f78c0b143c 100644
--- a/drivers/w1/slaves/w1_ds2780.c
+++ b/drivers/w1/slaves/w1_ds2780.c
@@ -26,20 +26,14 @@
#include "../w1_family.h"
#include "w1_ds2780.h"
-int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
- int io)
+static int w1_ds2780_do_io(struct device *dev, char *buf, int addr,
+ size_t count, int io)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
- if (!dev)
- return -ENODEV;
+ if (addr > DS2780_DATA_SIZE || addr < 0)
+ return 0;
- mutex_lock(&sl->master->mutex);
-
- if (addr > DS2780_DATA_SIZE || addr < 0) {
- count = 0;
- goto out;
- }
count = min_t(int, count, DS2780_DATA_SIZE - addr);
if (w1_reset_select_slave(sl) == 0) {
@@ -47,7 +41,6 @@ int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
w1_write_8(sl->master, addr);
w1_write_block(sl->master, buf, count);
- /* XXX w1_write_block returns void, not n_written */
} else {
w1_write_8(sl->master, W1_DS2780_READ_DATA);
w1_write_8(sl->master, addr);
@@ -55,13 +48,42 @@ int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
}
}
-out:
+ return count;
+}
+
+int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+ int ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&sl->master->mutex);
+
+ ret = w1_ds2780_do_io(dev, buf, addr, count, io);
+
mutex_unlock(&sl->master->mutex);
- return count;
+ return ret;
}
EXPORT_SYMBOL(w1_ds2780_io);
+int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ int ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ ret = w1_ds2780_do_io(dev, buf, addr, count, io);
+
+ return ret;
+}
+EXPORT_SYMBOL(w1_ds2780_io_nolock);
+
int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
@@ -99,43 +121,7 @@ static struct bin_attribute w1_ds2780_bin_attr = {
.read = w1_ds2780_read_bin,
};
-static DEFINE_IDR(bat_idr);
-static DEFINE_MUTEX(bat_idr_lock);
-
-static int new_bat_id(void)
-{
- int ret;
-
- while (1) {
- int id;
-
- ret = idr_pre_get(&bat_idr, GFP_KERNEL);
- if (ret == 0)
- return -ENOMEM;
-
- mutex_lock(&bat_idr_lock);
- ret = idr_get_new(&bat_idr, NULL, &id);
- mutex_unlock(&bat_idr_lock);
-
- if (ret == 0) {
- ret = id & MAX_ID_MASK;
- break;
- } else if (ret == -EAGAIN) {
- continue;
- } else {
- break;
- }
- }
-
- return ret;
-}
-
-static void release_bat_id(int id)
-{
- mutex_lock(&bat_idr_lock);
- idr_remove(&bat_idr, id);
- mutex_unlock(&bat_idr_lock);
-}
+static DEFINE_IDA(bat_ida);
static int w1_ds2780_add_slave(struct w1_slave *sl)
{
@@ -143,7 +129,7 @@ static int w1_ds2780_add_slave(struct w1_slave *sl)
int id;
struct platform_device *pdev;
- id = new_bat_id();
+ id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto noid;
@@ -172,7 +158,7 @@ bin_attr_failed:
pdev_add_failed:
platform_device_unregister(pdev);
pdev_alloc_failed:
- release_bat_id(id);
+ ida_simple_remove(&bat_ida, id);
noid:
return ret;
}
@@ -183,7 +169,7 @@ static void w1_ds2780_remove_slave(struct w1_slave *sl)
int id = pdev->id;
platform_device_unregister(pdev);
- release_bat_id(id);
+ ida_simple_remove(&bat_ida, id);
sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
}
@@ -199,14 +185,14 @@ static struct w1_family w1_ds2780_family = {
static int __init w1_ds2780_init(void)
{
- idr_init(&bat_idr);
+ ida_init(&bat_ida);
return w1_register_family(&w1_ds2780_family);
}
static void __exit w1_ds2780_exit(void)
{
w1_unregister_family(&w1_ds2780_family);
- idr_destroy(&bat_idr);
+ ida_destroy(&bat_ida);
}
module_init(w1_ds2780_init);
diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h
index a1fba79eb1b5..737379365021 100644
--- a/drivers/w1/slaves/w1_ds2780.h
+++ b/drivers/w1/slaves/w1_ds2780.h
@@ -124,6 +124,8 @@
extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
int io);
+extern int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr,
+ size_t count, int io);
extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd);
#endif /* !_W1_DS2780_H */
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
index d220bce2cee4..f79e62e54e8d 100644
--- a/drivers/w1/w1_int.c
+++ b/drivers/w1/w1_int.c
@@ -78,6 +78,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
memcpy(&dev->dev, device, sizeof(struct device));
dev_set_name(&dev->dev, "w1_bus_master%u", dev->id);
snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id);
+ dev->dev.init_name = dev->name;
dev->driver = driver;
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
index 765b37b62a4f..3135b2c63998 100644
--- a/drivers/w1/w1_io.c
+++ b/drivers/w1/w1_io.c
@@ -158,13 +158,18 @@ EXPORT_SYMBOL_GPL(w1_write_8);
static u8 w1_read_bit(struct w1_master *dev)
{
int result;
+ unsigned long flags;
+ /* sample timing is critical here */
+ local_irq_save(flags);
dev->bus_master->write_bit(dev->bus_master->data, 0);
w1_delay(6);
dev->bus_master->write_bit(dev->bus_master->data, 1);
w1_delay(9);
result = dev->bus_master->read_bit(dev->bus_master->data);
+ local_irq_restore(flags);
+
w1_delay(55);
return result & 0x1;
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 86b0735e6aa0..64c6752ea2c6 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -726,7 +726,7 @@ config SBC8360_WDT
config SBC7240_WDT
tristate "SBC Nano 7240 Watchdog Timer"
- depends on X86_32
+ depends on X86_32 && !UML
---help---
This is the driver for the hardware watchdog found on the IEI
single board computers EPIC Nano 7240 (and likely others). This
@@ -1174,6 +1174,10 @@ config XEN_WDT
by Xen 4.0 and newer. The watchdog timeout period is normally one
minute but can be changed with a boot-time parameter.
+config UML_WATCHDOG
+ tristate "UML watchdog"
+ depends on UML
+
#
# ISA-based Watchdog Cards
#