summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acorn/block/fd1772.c4
-rw-r--r--drivers/acorn/char/i2c.c2
-rw-r--r--drivers/ata/pdc_adma.c4
-rw-r--r--drivers/atm/.gitignore2
-rw-r--r--drivers/atm/eni.c4
-rw-r--r--drivers/atm/he.c2
-rw-r--r--drivers/atm/lanai.c2
-rw-r--r--drivers/atm/nicstar.c4
-rw-r--r--drivers/atm/zatm.c4
-rw-r--r--drivers/base/dmapool.c2
-rw-r--r--drivers/block/Kconfig7
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/cciss.c23
-rw-r--r--drivers/block/cpqarray.c10
-rw-r--r--drivers/block/swim_iop.c578
-rw-r--r--drivers/cdrom/cdrom.c8
-rw-r--r--drivers/cdrom/cm206.c2
-rw-r--r--drivers/char/Kconfig10
-rw-r--r--drivers/char/agp/Kconfig4
-rw-r--r--drivers/char/agp/generic.c25
-rw-r--r--drivers/char/consolemap.c2
-rw-r--r--drivers/char/ipmi/ipmi_bt_sm.c4
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c2
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c12
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c6
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c132
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c18
-rw-r--r--drivers/char/lcd.c2
-rw-r--r--drivers/char/lp.c2
-rw-r--r--drivers/char/mem.c12
-rw-r--r--drivers/char/mxser_new.c8
-rw-r--r--drivers/char/n_r3964.c37
-rw-r--r--drivers/char/n_tty.c1
-rw-r--r--drivers/char/pcmcia/synclink_cs.c2
-rw-r--r--drivers/char/random.c2
-rw-r--r--drivers/char/rio/riocmd.c2
-rw-r--r--drivers/char/rtc.c38
-rw-r--r--drivers/char/sx.c8
-rw-r--r--drivers/char/synclink.c2
-rw-r--r--drivers/char/synclinkmp.c4
-rw-r--r--drivers/char/sysrq.c37
-rw-r--r--drivers/char/tty_io.c8
-rw-r--r--drivers/char/viocons.c10
-rw-r--r--drivers/char/vt.c2
-rw-r--r--drivers/char/vt_ioctl.c10
-rw-r--r--drivers/char/watchdog/at91rm9200_wdt.c6
-rw-r--r--drivers/char/watchdog/mpcore_wdt.c2
-rw-r--r--drivers/char/watchdog/omap_wdt.c2
-rw-r--r--drivers/char/watchdog/pcwd_usb.c5
-rw-r--r--drivers/char/watchdog/rm9k_wdt.c44
-rw-r--r--drivers/clocksource/acpi_pm.c6
-rw-r--r--drivers/cpufreq/cpufreq.c153
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c33
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c33
-rw-r--r--drivers/cpufreq/cpufreq_performance.c9
-rw-r--r--drivers/cpufreq/cpufreq_powersave.c9
-rw-r--r--drivers/cpufreq/cpufreq_stats.c11
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c11
-rw-r--r--drivers/cpufreq/freq_table.c28
-rw-r--r--drivers/crypto/Kconfig2
-rw-r--r--drivers/fc4/fc.c10
-rw-r--r--drivers/i2c/algos/Kconfig11
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c8
-rw-r--r--drivers/i2c/algos/i2c-algo-ite.c806
-rw-r--r--drivers/i2c/algos/i2c-algo-ite.h117
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c7
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c8
-rw-r--r--drivers/i2c/algos/i2c-algo-sgi.c8
-rw-r--r--drivers/i2c/busses/Kconfig49
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-at91.c325
-rw-r--r--drivers/i2c/busses/i2c-elektor.c2
-rw-r--r--drivers/i2c/busses/i2c-hydra.c2
-rw-r--r--drivers/i2c/busses/i2c-i801.c16
-rw-r--r--drivers/i2c/busses/i2c-i810.c6
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c9
-rw-r--r--drivers/i2c/busses/i2c-ite.c278
-rw-r--r--drivers/i2c/busses/i2c-ixp2000.c2
-rw-r--r--drivers/i2c/busses/i2c-ixp4xx.c2
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c89
-rw-r--r--drivers/i2c/busses/i2c-omap.c4
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c2
-rw-r--r--drivers/i2c/busses/i2c-parport.c2
-rw-r--r--drivers/i2c/busses/i2c-pca-isa.c2
-rw-r--r--drivers/i2c/busses/i2c-pnx.c708
-rw-r--r--drivers/i2c/busses/i2c-prosavage.c2
-rw-r--r--drivers/i2c/busses/i2c-savage4.c2
-rw-r--r--drivers/i2c/busses/i2c-versatile.c153
-rw-r--r--drivers/i2c/busses/i2c-via.c2
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c6
-rw-r--r--drivers/i2c/busses/scx200_i2c.c2
-rw-r--r--drivers/i2c/chips/ds1337.c8
-rw-r--r--drivers/i2c/chips/tps65010.c21
-rw-r--r--drivers/i2c/i2c-core.c67
-rw-r--r--drivers/i2c/i2c-dev.c44
-rw-r--r--drivers/ide/Kconfig2
-rw-r--r--drivers/ide/ide-cd.c7
-rw-r--r--drivers/ide/ide-floppy.c2
-rw-r--r--drivers/ide/ide-probe.c4
-rw-r--r--drivers/ide/ide-tape.c8
-rw-r--r--drivers/ide/pci/hpt366.c886
-rw-r--r--drivers/ide/pci/pdc202xx_new.c483
-rw-r--r--drivers/ide/pci/piix.c4
-rw-r--r--drivers/ide/setup-pci.c4
-rw-r--r--drivers/ieee1394/pcilynx.c2
-rw-r--r--drivers/infiniband/core/Makefile6
-rw-r--r--drivers/infiniband/core/cm.c4
-rw-r--r--drivers/infiniband/core/cma.c416
-rw-r--r--drivers/infiniband/core/fmr_pool.c12
-rw-r--r--drivers/infiniband/core/mad.c90
-rw-r--r--drivers/infiniband/core/mad_priv.h6
-rw-r--r--drivers/infiniband/core/ucma.c874
-rw-r--r--drivers/infiniband/core/uverbs_marshall.c5
-rw-r--r--drivers/infiniband/core/uverbs_mem.c12
-rw-r--r--drivers/infiniband/hw/amso1100/c2_qp.c13
-rw-r--r--drivers/infiniband/hw/ipath/Makefile1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_dma.c189
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c5
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_intr.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_keys.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mr.c7
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sysfs.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h4
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c75
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c3
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h2
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c4
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c125
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c81
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h2
-rw-r--r--drivers/input/keyboard/hilkbd.c5
-rw-r--r--drivers/input/touchscreen/Kconfig2
-rw-r--r--drivers/isdn/act2000/act2000_isa.c2
-rw-r--r--drivers/isdn/capi/capidrv.c2
-rw-r--r--drivers/isdn/divert/divert_procfs.c2
-rw-r--r--drivers/isdn/divert/isdn_divert.c6
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c118
-rw-r--r--drivers/isdn/hisax/Kconfig10
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c2
-rw-r--r--drivers/isdn/hysdn/hysdn_proclog.c2
-rw-r--r--drivers/isdn/i4l/isdn_audio.c6
-rw-r--r--drivers/isdn/i4l/isdn_net.c2
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c2
-rw-r--r--drivers/isdn/pcbit/layer2.c2
-rw-r--r--drivers/kvm/Kconfig37
-rw-r--r--drivers/kvm/Makefile10
-rw-r--r--drivers/kvm/kvm.h551
-rw-r--r--drivers/kvm/kvm_main.c1917
-rw-r--r--drivers/kvm/kvm_svm.h44
-rw-r--r--drivers/kvm/kvm_vmx.h14
-rw-r--r--drivers/kvm/mmu.c686
-rw-r--r--drivers/kvm/paging_tmpl.h391
-rw-r--r--drivers/kvm/segment_descriptor.h17
-rw-r--r--drivers/kvm/svm.c1641
-rw-r--r--drivers/kvm/svm.h315
-rw-r--r--drivers/kvm/vmx.c2014
-rw-r--r--drivers/kvm/vmx.h296
-rw-r--r--drivers/kvm/x86_emulate.c1409
-rw-r--r--drivers/kvm/x86_emulate.h185
-rw-r--r--drivers/leds/Kconfig22
-rw-r--r--drivers/macintosh/adb.c2
-rw-r--r--drivers/macintosh/apm_emu.c2
-rw-r--r--drivers/macintosh/smu.c2
-rw-r--r--drivers/macintosh/via-pmu68k.c2
-rw-r--r--drivers/md/Kconfig1
-rw-r--r--drivers/md/faulty.c2
-rw-r--r--drivers/md/md.c17
-rw-r--r--drivers/md/raid1.c3
-rw-r--r--drivers/md/raid10.c6
-rw-r--r--drivers/md/raid5.c367
-rw-r--r--drivers/media/Kconfig2
-rw-r--r--drivers/media/common/ir-keymaps.c55
-rw-r--r--drivers/media/common/saa7146_i2c.c16
-rw-r--r--drivers/media/dvb/b2c2/Kconfig1
-rw-r--r--drivers/media/dvb/b2c2/flexcop-fe-tuner.c10
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig2
-rw-r--r--drivers/media/dvb/bt8xx/dst_ca.c2
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c11
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.h2
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c13
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig14
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c36
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c271
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700.h5
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c40
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c200
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c113
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c26
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c22
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c24
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h14
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c37
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c22
-rw-r--r--drivers/media/dvb/dvb-usb/nova-t-usb2.c34
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.c270
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.h70
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c24
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c20
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c40
-rw-r--r--drivers/media/dvb/frontends/Kconfig24
-rw-r--r--drivers/media/dvb/frontends/Makefile3
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c7
-rw-r--r--drivers/media/dvb/frontends/dib7000m.c1191
-rw-r--r--drivers/media/dvb/frontends/dib7000m.h51
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c1019
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h46
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h13
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c67
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h7
-rw-r--r--drivers/media/dvb/frontends/lg_h06xf.h64
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c257
-rw-r--r--drivers/media/dvb/frontends/lgdt330x_priv.h15
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.c134
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.h35
-rw-r--r--drivers/media/dvb/frontends/or51132.c176
-rw-r--r--drivers/media/dvb/frontends/or51211.c124
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c10
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h5
-rw-r--r--drivers/media/dvb/frontends/tda8083.c30
-rw-r--r--drivers/media/dvb/frontends/tda826x.c12
-rw-r--r--drivers/media/dvb/frontends/tua6100.c3
-rw-r--r--drivers/media/dvb/pluto2/pluto2.c8
-rw-r--r--drivers/media/dvb/ttpci/Kconfig1
-rw-r--r--drivers/media/dvb/ttpci/av7110.c2
-rw-r--r--drivers/media/dvb/ttpci/av7110_ir.c25
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c26
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c334
-rw-r--r--drivers/media/dvb/ttpci/budget.c2
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c11
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusbdecfe.c4
-rw-r--r--drivers/media/video/Kconfig31
-rw-r--r--drivers/media/video/Makefile6
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c6
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c101
-rw-r--r--drivers/media/video/cafe_ccic-regs.h160
-rw-r--r--drivers/media/video/cafe_ccic.c2228
-rw-r--r--drivers/media/video/cx88/Kconfig1
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c179
-rw-r--r--drivers/media/video/cx88/cx88-cards.c86
-rw-r--r--drivers/media/video/cx88/cx88-core.c2
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c333
-rw-r--r--drivers/media/video/cx88/cx88-input.c77
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c348
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c13
-rw-r--r--drivers/media/video/cx88/cx88-video.c32
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.c2
-rw-r--r--drivers/media/video/cx88/cx88.h47
-rw-r--r--drivers/media/video/dabusb.c4
-rw-r--r--drivers/media/video/ir-kbd-i2c.c46
-rw-r--r--drivers/media/video/mxb.c8
-rw-r--r--drivers/media/video/ov7670.c1333
-rw-r--r--drivers/media/video/planb.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c16
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c26
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c81
-rw-r--r--drivers/media/video/saa7115.c18
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c63
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c222
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c11
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c222
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c76
-rw-r--r--drivers/media/video/saa7134/saa7134.h8
-rw-r--r--drivers/media/video/stv680.c21
-rw-r--r--drivers/media/video/tda9887.c6
-rw-r--r--drivers/media/video/tuner-core.c4
-rw-r--r--drivers/media/video/tuner-simple.c4
-rw-r--r--drivers/media/video/tuner-types.c15
-rw-r--r--drivers/media/video/tveeprom.c9
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c9
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c2
-rw-r--r--drivers/media/video/usbvision/Kconfig12
-rw-r--r--drivers/media/video/usbvision/Makefile5
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c157
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c2554
-rw-r--r--drivers/media/video/usbvision/usbvision-i2c.c571
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c2051
-rw-r--r--drivers/media/video/usbvision/usbvision.h558
-rw-r--r--drivers/media/video/v4l1-compat.c18
-rw-r--r--drivers/media/video/v4l2-common.c85
-rw-r--r--drivers/media/video/videocodec.c2
-rw-r--r--drivers/media/video/videodev.c173
-rw-r--r--drivers/media/video/vino.c2
-rw-r--r--drivers/media/video/vivi.c16
-rw-r--r--drivers/media/video/zoran_card.c2
-rw-r--r--drivers/media/video/zr36120.c2079
-rw-r--r--drivers/media/video/zr36120.h279
-rw-r--r--drivers/media/video/zr36120_i2c.c132
-rw-r--r--drivers/media/video/zr36120_mem.c78
-rw-r--r--drivers/media/video/zr36120_mem.h3
-rw-r--r--drivers/message/i2o/core.h4
-rw-r--r--drivers/message/i2o/driver.c2
-rw-r--r--drivers/message/i2o/exec-osm.c2
-rw-r--r--drivers/message/i2o/i2o_config.c4
-rw-r--r--drivers/mmc/at91_mci.c346
-rw-r--r--drivers/mmc/mmc_queue.c4
-rw-r--r--drivers/mmc/sdhci.c4
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c2
-rw-r--r--drivers/mtd/rfd_ftl.c2
-rw-r--r--drivers/net/8139too.c5
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/appletalk/ipddp.c2
-rw-r--r--drivers/net/bsd_comp.c2
-rw-r--r--drivers/net/chelsio/cxgb2.c23
-rw-r--r--drivers/net/chelsio/sge.c115
-rw-r--r--drivers/net/chelsio/sge.h4
-rw-r--r--drivers/net/e100.c3
-rw-r--r--drivers/net/hamradio/baycom_epp.c2
-rw-r--r--drivers/net/irda/donauboe.c2
-rw-r--r--drivers/net/irda/irda-usb.c2
-rw-r--r--drivers/net/irda/irport.c2
-rw-r--r--drivers/net/lp486e.c4
-rw-r--r--drivers/net/macb.c8
-rw-r--r--drivers/net/macb.h6
-rw-r--r--drivers/net/myri10ge/myri10ge.c498
-rw-r--r--drivers/net/phy/phy_device.c2
-rw-r--r--drivers/net/ppp_deflate.c4
-rw-r--r--drivers/net/ppp_mppe.c2
-rw-r--r--drivers/net/skge.c2
-rw-r--r--drivers/net/slip.c6
-rw-r--r--drivers/net/smc91x.h90
-rw-r--r--drivers/net/ucc_geth.c12
-rw-r--r--drivers/net/wan/Kconfig5
-rw-r--r--drivers/net/wan/hostess_sv11.c2
-rw-r--r--drivers/net/wan/pc300_drv.c2
-rw-r--r--drivers/net/wan/pc300_tty.c2
-rw-r--r--drivers/net/wan/x25_asy.c8
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_download.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_hw.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_main.c2
-rw-r--r--drivers/net/wireless/ipw2100.c2
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c2
-rw-r--r--drivers/net/wireless/wavelan_cs.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c2
-rw-r--r--drivers/parisc/iosapic.c2
-rw-r--r--drivers/parport/Kconfig6
-rw-r--r--drivers/pci/hotplug/Kconfig3
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.c8
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c2
-rw-r--r--drivers/pcmcia/at91_cf.c2
-rw-r--r--drivers/pcmcia/omap_cf.c2
-rw-r--r--drivers/pnp/isapnp/core.c22
-rw-r--r--drivers/pnp/pnpacpi/core.c6
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c22
-rw-r--r--drivers/pnp/pnpbios/core.c16
-rw-r--r--drivers/pnp/pnpbios/proc.c8
-rw-r--r--drivers/pnp/pnpbios/rsparser.c16
-rw-r--r--drivers/ps3/Makefile1
-rw-r--r--drivers/ps3/vuart.c965
-rw-r--r--drivers/ps3/vuart.h94
-rw-r--r--drivers/rtc/Kconfig2
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/rtc-at91rm9200.c (renamed from drivers/rtc/rtc-at91.c)5
-rw-r--r--drivers/rtc/rtc-dev.c2
-rw-r--r--drivers/rtc/rtc-ds1672.c2
-rw-r--r--drivers/rtc/rtc-lib.c81
-rw-r--r--drivers/rtc/rtc-omap.c3
-rw-r--r--drivers/rtc/rtc-pcf8563.c6
-rw-r--r--drivers/rtc/rtc-proc.c4
-rw-r--r--drivers/rtc/rtc-rs5c372.c2
-rw-r--r--drivers/rtc/rtc-s3c.c6
-rw-r--r--drivers/rtc/rtc-sa1100.c4
-rw-r--r--drivers/rtc/rtc-sh.c245
-rw-r--r--drivers/rtc/rtc-sysfs.c2
-rw-r--r--drivers/rtc/rtc-x1205.c10
-rw-r--r--drivers/s390/char/con3215.c2
-rw-r--r--drivers/s390/char/keyboard.c2
-rw-r--r--drivers/s390/char/sclp_cpi.c2
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c4
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c4
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c2
-rw-r--r--drivers/s390/net/ctcmain.c6
-rw-r--r--drivers/s390/net/iucv.c2
-rw-r--r--drivers/s390/scsi/zfcp_aux.c2
-rw-r--r--drivers/sbus/char/vfc_dev.c2
-rw-r--r--drivers/scsi/aacraid/aachba.c2
-rw-r--r--drivers/scsi/aacraid/comminit.c2
-rw-r--r--drivers/scsi/aha1542.c2
-rw-r--r--drivers/scsi/aic7xxx_old.c2
-rw-r--r--drivers/scsi/dc395x.c2
-rw-r--r--drivers/scsi/dpt_i2o.c10
-rw-r--r--drivers/scsi/initio.c2
-rw-r--r--drivers/scsi/osst.c2
-rw-r--r--drivers/scsi/pluto.c2
-rw-r--r--drivers/scsi/sr_ioctl.c2
-rw-r--r--drivers/scsi/sr_vendor.c4
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c2
-rw-r--r--drivers/serial/Kconfig57
-rw-r--r--drivers/serial/icom.c2
-rw-r--r--drivers/serial/sh-sci.c22
-rw-r--r--drivers/serial/sh-sci.h19
-rw-r--r--drivers/spi/pxa2xx_spi.c733
-rw-r--r--drivers/usb/gadget/at91_udc.c2
-rw-r--r--drivers/usb/gadget/serial.c2
-rw-r--r--drivers/usb/host/hc_crisv10.c2
-rw-r--r--drivers/usb/misc/auerswald.c4
-rw-r--r--drivers/usb/misc/uss720.c2
-rw-r--r--drivers/usb/net/rndis_host.c2
-rw-r--r--drivers/usb/serial/cypress_m8.c2
-rw-r--r--drivers/usb/serial/digi_acceleport.c4
-rw-r--r--drivers/usb/serial/io_ti.c2
-rw-r--r--drivers/usb/serial/ipaq.c2
-rw-r--r--drivers/usb/serial/kobil_sct.c4
-rw-r--r--drivers/usb/serial/pl2303.c2
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c2
-rw-r--r--drivers/usb/serial/whiteheat.c4
-rw-r--r--drivers/usb/storage/sddr09.c2
-rw-r--r--drivers/video/Kconfig10
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/amba-clcd.c2
-rw-r--r--drivers/video/amifb.c8
-rw-r--r--drivers/video/aty/atyfb_base.c4
-rw-r--r--drivers/video/aty/radeon_i2c.c8
-rw-r--r--drivers/video/gxt4500.c741
-rw-r--r--drivers/video/i810/i810-i2c.c6
-rw-r--r--drivers/video/intelfb/intelfb_i2c.c4
-rw-r--r--drivers/video/matrox/i2c-matroxfb.c4
-rw-r--r--drivers/video/matrox/matroxfb_base.c2
-rw-r--r--drivers/video/matrox/matroxfb_crtc2.c2
-rw-r--r--drivers/video/nvidia/nv_i2c.c6
-rw-r--r--drivers/video/riva/rivafb-i2c.c6
-rw-r--r--drivers/video/savage/savagefb-i2c.c2
-rw-r--r--drivers/video/sstfb.c335
-rw-r--r--drivers/w1/slaves/Kconfig4
438 files changed, 33229 insertions, 9048 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4929e92..e7da9fa 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -80,4 +80,6 @@ source "drivers/rtc/Kconfig"
source "drivers/dma/Kconfig"
+source "drivers/kvm/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 50f76da..0dd96d1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_PCCARD) += pcmcia/
obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/
+obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_ZORRO) += zorro/
obj-$(CONFIG_MAC) += macintosh/
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c
index 0485423..674bf81 100644
--- a/drivers/acorn/block/fd1772.c
+++ b/drivers/acorn/block/fd1772.c
@@ -1549,12 +1549,12 @@ int fd1772_init(void)
#ifdef TRACKBUFFER
BufferDrive = BufferSide = BufferTrack = -1;
/* Atari uses 512 - I want to eventually cope with 1K sectors */
- DMABuffer = (char *)kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL);
+ DMABuffer = kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL);
TrackBuffer = DMABuffer + 512;
#else
/* Allocate memory for the DMAbuffer - on the Atari this takes it
out of some special memory... */
- DMABuffer = (char *) kmalloc(2048); /* Copes with pretty large sectors */
+ DMABuffer = kmalloc(2048); /* Copes with pretty large sectors */
#endif
err = -ENOMEM;
if (!DMAbuffer)
diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c
index bdb9c8b..9e584a7 100644
--- a/drivers/acorn/char/i2c.c
+++ b/drivers/acorn/char/i2c.c
@@ -360,7 +360,7 @@ static int __init i2c_ioc_init(void)
if (ret >= 0){
ret = misc_register(&rtc_dev);
if(ret < 0)
- i2c_bit_del_bus(&ioc_ops);
+ i2c_del_adapter(&ioc_ops);
}
return ret;
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 9021e34..90786d7 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -551,7 +551,7 @@ static int adma_port_start(struct ata_port *ap)
return rc;
adma_enter_reg_mode(ap);
rc = -ENOMEM;
- pp = kcalloc(1, sizeof(*pp), GFP_KERNEL);
+ pp = kzalloc(sizeof(*pp), GFP_KERNEL);
if (!pp)
goto err_out;
pp->pkt = dma_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma,
@@ -672,7 +672,7 @@ static int adma_ata_init_one(struct pci_dev *pdev,
if (rc)
goto err_out_iounmap;
- probe_ent = kcalloc(1, sizeof(*probe_ent), GFP_KERNEL);
+ probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL);
if (probe_ent == NULL) {
rc = -ENOMEM;
goto err_out_iounmap;
diff --git a/drivers/atm/.gitignore b/drivers/atm/.gitignore
index a165b71..fc0ae5e 100644
--- a/drivers/atm/.gitignore
+++ b/drivers/atm/.gitignore
@@ -2,4 +2,4 @@
fore200e_mkfirm
fore200e_pca_fw.c
pca200e.bin
-
+pca200e_ecd.bin2
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index bc1b13c..5aab7bd 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -1832,7 +1832,7 @@ static int __devinit eni_start(struct atm_dev *dev)
/* initialize memory management */
buffer_mem = eni_dev->mem - (buf - eni_dev->ram);
eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
- eni_dev->free_list = (struct eni_free *) kmalloc(
+ eni_dev->free_list = kmalloc(
sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL);
if (!eni_dev->free_list) {
printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
@@ -2232,7 +2232,7 @@ static int __devinit eni_init_one(struct pci_dev *pci_dev,
goto out0;
}
- eni_dev = (struct eni_dev *) kmalloc(sizeof(struct eni_dev),GFP_KERNEL);
+ eni_dev = kmalloc(sizeof(struct eni_dev),GFP_KERNEL);
if (!eni_dev) goto out0;
if (!cpu_zeroes) {
cpu_zeroes = pci_alloc_consistent(pci_dev,ENI_ZEROES_SIZE,
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
index 7d9b4e5..db33f6f 100644
--- a/drivers/atm/he.c
+++ b/drivers/atm/he.c
@@ -2351,7 +2351,7 @@ he_open(struct atm_vcc *vcc)
cid = he_mkcid(he_dev, vpi, vci);
- he_vcc = (struct he_vcc *) kmalloc(sizeof(struct he_vcc), GFP_ATOMIC);
+ he_vcc = kmalloc(sizeof(struct he_vcc), GFP_ATOMIC);
if (he_vcc == NULL) {
hprintk("unable to allocate he_vcc during open\n");
return -ENOMEM;
diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c
index 2678255..09f477d 100644
--- a/drivers/atm/lanai.c
+++ b/drivers/atm/lanai.c
@@ -2602,7 +2602,7 @@ static int __devinit lanai_init_one(struct pci_dev *pci,
struct atm_dev *atmdev;
int result;
- lanai = (struct lanai_dev *) kmalloc(sizeof(*lanai), GFP_KERNEL);
+ lanai = kmalloc(sizeof(*lanai), GFP_KERNEL);
if (lanai == NULL) {
printk(KERN_ERR DEV_LABEL
": couldn't allocate dev_data structure!\n");
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index bd09045..aab9b37 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -997,7 +997,7 @@ static scq_info *get_scq(int size, u32 scd)
if (size != VBR_SCQSIZE && size != CBR_SCQSIZE)
return NULL;
- scq = (scq_info *) kmalloc(sizeof(scq_info), GFP_KERNEL);
+ scq = kmalloc(sizeof(scq_info), GFP_KERNEL);
if (scq == NULL)
return NULL;
scq->org = kmalloc(2 * size, GFP_KERNEL);
@@ -1006,7 +1006,7 @@ static scq_info *get_scq(int size, u32 scd)
kfree(scq);
return NULL;
}
- scq->skb = (struct sk_buff **) kmalloc(sizeof(struct sk_buff *) *
+ scq->skb = kmalloc(sizeof(struct sk_buff *) *
(size / NS_SCQE_SIZE), GFP_KERNEL);
if (scq->skb == NULL)
{
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index 7df0f37..756d4f7 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -996,7 +996,7 @@ static int start_tx(struct atm_dev *dev)
DPRINTK("start_tx\n");
zatm_dev = ZATM_DEV(dev);
- zatm_dev->tx_map = (struct atm_vcc **) kmalloc(sizeof(struct atm_vcc *)*
+ zatm_dev->tx_map = kmalloc(sizeof(struct atm_vcc *)*
zatm_dev->chans,GFP_KERNEL);
if (!zatm_dev->tx_map) return -ENOMEM;
zatm_dev->tx_bw = ATM_OC3_PCR;
@@ -1591,7 +1591,7 @@ static int __devinit zatm_init_one(struct pci_dev *pci_dev,
struct zatm_dev *zatm_dev;
int ret = -ENOMEM;
- zatm_dev = (struct zatm_dev *) kmalloc(sizeof(*zatm_dev), GFP_KERNEL);
+ zatm_dev = kmalloc(sizeof(*zatm_dev), GFP_KERNEL);
if (!zatm_dev) {
printk(KERN_EMERG "%s: memory shortage\n", DEV_LABEL);
goto out;
diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c
index dbe0735..f95d502 100644
--- a/drivers/base/dmapool.c
+++ b/drivers/base/dmapool.c
@@ -173,7 +173,7 @@ pool_alloc_page (struct dma_pool *pool, gfp_t mem_flags)
mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
mapsize *= sizeof (long);
- page = (struct dma_page *) kmalloc (mapsize + sizeof *page, mem_flags);
+ page = kmalloc(mapsize + sizeof *page, mem_flags);
if (!page)
return NULL;
page->vaddr = dma_alloc_coherent (pool->dev,
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index ce9cfcb..58c1deb 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -28,13 +28,6 @@ config ATARI_FLOPPY
tristate "Atari floppy support"
depends on ATARI
-config BLK_DEV_SWIM_IOP
- bool "Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)"
- depends on MAC && EXPERIMENTAL && BROKEN
- help
- Say Y here to support the SWIM (Super Woz Integrated Machine) IOP
- floppy controller on the Macintosh IIfx and Quadra 900/950.
-
config MAC_FLOPPY
tristate "Support for PowerMac floppy"
depends on PPC_PMAC && !PPC_PMAC64
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 410f259..dd88e33 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_MAC_FLOPPY) += swim3.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
-obj-$(CONFIG_BLK_DEV_SWIM_IOP) += swim_iop.o
obj-$(CONFIG_ATARI_ACSI) += acsi.o
obj-$(CONFIG_ATARI_SLM) += acsi_slm.o
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index ee159ed..acb2fa9 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1039,7 +1039,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
status = -ENOMEM;
goto cleanup1;
}
- buff_size = (int *)kmalloc(MAXSGENTRIES * sizeof(int),
+ buff_size = kmalloc(MAXSGENTRIES * sizeof(int),
GFP_KERNEL);
if (!buff_size) {
status = -ENOMEM;
@@ -2837,7 +2837,7 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
if (err) {
printk(KERN_ERR "cciss: Cannot obtain PCI resources, "
"aborting\n");
- goto err_out_disable_pdev;
+ return err;
}
subsystem_vendor_id = pdev->subsystem_vendor;
@@ -2865,7 +2865,7 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
#ifdef CCISS_DEBUG
printk("address 0 = %x\n", c->paddr);
#endif /* CCISS_DEBUG */
- c->vaddr = remap_pci_mem(c->paddr, 200);
+ c->vaddr = remap_pci_mem(c->paddr, 0x250);
/* Wait for the board to become ready. (PCI hotplug needs this.)
* We poll for up to 120 secs, once per 100ms. */
@@ -3005,10 +3005,11 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
return 0;
err_out_free_res:
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
pci_release_regions(pdev);
-
- err_out_disable_pdev:
- pci_disable_device(pdev);
return err;
}
@@ -3382,8 +3383,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
if (drv->queue)
blk_cleanup_queue(drv->queue);
}
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
pci_release_regions(pdev);
- pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
free_hba(i);
return -1;
@@ -3452,8 +3456,11 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
#ifdef CONFIG_CISS_SCSI_TAPE
kfree(hba[i]->scsi_rejects.complete);
#endif
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
pci_release_regions(pdev);
- pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
free_hba(i);
}
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index d5f519e..b94cd1c 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -1625,7 +1625,7 @@ static void start_fwbk(int ctlr)
" processing\n");
/* Command does not return anything, but idasend command needs a
buffer */
- id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL);
+ id_ctlr_buf = kmalloc(sizeof(id_ctlr_t), GFP_KERNEL);
if(id_ctlr_buf==NULL)
{
printk(KERN_WARNING "cpqarray: Out of memory. "
@@ -1660,14 +1660,14 @@ static void getgeometry(int ctlr)
info_p->log_drv_map = 0;
- id_ldrive = (id_log_drv_t *)kmalloc(sizeof(id_log_drv_t), GFP_KERNEL);
+ id_ldrive = kmalloc(sizeof(id_log_drv_t), GFP_KERNEL);
if(id_ldrive == NULL)
{
printk( KERN_ERR "cpqarray: out of memory.\n");
return;
}
- id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL);
+ id_ctlr_buf = kmalloc(sizeof(id_ctlr_t), GFP_KERNEL);
if(id_ctlr_buf == NULL)
{
kfree(id_ldrive);
@@ -1675,7 +1675,7 @@ static void getgeometry(int ctlr)
return;
}
- id_lstatus_buf = (sense_log_drv_stat_t *)kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL);
+ id_lstatus_buf = kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL);
if(id_lstatus_buf == NULL)
{
kfree(id_ctlr_buf);
@@ -1684,7 +1684,7 @@ static void getgeometry(int ctlr)
return;
}
- sense_config_buf = (config_t *)kmalloc(sizeof(config_t), GFP_KERNEL);
+ sense_config_buf = kmalloc(sizeof(config_t), GFP_KERNEL);
if(sense_config_buf == NULL)
{
kfree(id_lstatus_buf);
diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c
deleted file mode 100644
index ed7b06c..0000000
--- a/drivers/block/swim_iop.c
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
- * Driver for the SWIM (Super Woz Integrated Machine) IOP
- * floppy controller on the Macintosh IIfx and Quadra 900/950
- *
- * Written by Joshua M. Thompson (funaho@jurai.org)
- * based on the SWIM3 driver (c) 1996 by Paul Mackerras.
- *
- * 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.
- *
- * 1999-06-12 (jmt) - Initial implementation.
- */
-
-/*
- * -------------------
- * Theory of Operation
- * -------------------
- *
- * Since the SWIM IOP is message-driven we implement a simple request queue
- * system. One outstanding request may be queued at any given time (this is
- * an IOP limitation); only when that request has completed can a new request
- * be sent.
- */
-
-#include <linux/stddef.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/fd.h>
-#include <linux/ioctl.h>
-#include <linux/blkdev.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/mac_iop.h>
-#include <asm/swim_iop.h>
-
-#define DRIVER_VERSION "Version 0.1 (1999-06-12)"
-
-#define MAX_FLOPPIES 4
-
-enum swim_state {
- idle,
- available,
- revalidating,
- transferring,
- ejecting
-};
-
-struct floppy_state {
- enum swim_state state;
- int drive_num; /* device number */
- int secpercyl; /* disk geometry information */
- int secpertrack;
- int total_secs;
- int write_prot; /* 1 if write-protected, 0 if not, -1 dunno */
- int ref_count;
- struct timer_list timeout;
- int ejected;
- struct wait_queue *wait;
- int wanted;
- int timeout_pending;
-};
-
-struct swim_iop_req {
- int sent;
- int complete;
- __u8 command[32];
- struct floppy_state *fs;
- void (*done)(struct swim_iop_req *);
-};
-
-static struct swim_iop_req *current_req;
-static int floppy_count;
-
-static struct floppy_state floppy_states[MAX_FLOPPIES];
-static DEFINE_SPINLOCK(swim_iop_lock);
-
-#define CURRENT elv_next_request(swim_queue)
-
-static char *drive_names[7] = {
- "not installed", /* DRV_NONE */
- "unknown (1)", /* DRV_UNKNOWN */
- "a 400K drive", /* DRV_400K */
- "an 800K drive" /* DRV_800K */
- "unknown (4)", /* ???? */
- "an FDHD", /* DRV_FDHD */
- "unknown (6)", /* ???? */
- "an Apple HD20" /* DRV_HD20 */
-};
-
-int swimiop_init(void);
-static void swimiop_init_request(struct swim_iop_req *);
-static int swimiop_send_request(struct swim_iop_req *);
-static void swimiop_receive(struct iop_msg *);
-static void swimiop_status_update(int, struct swim_drvstatus *);
-static int swimiop_eject(struct floppy_state *fs);
-
-static int floppy_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long param);
-static int floppy_open(struct inode *inode, struct file *filp);
-static int floppy_release(struct inode *inode, struct file *filp);
-static int floppy_check_change(struct gendisk *disk);
-static int floppy_revalidate(struct gendisk *disk);
-static int grab_drive(struct floppy_state *fs, enum swim_state state,
- int interruptible);
-static void release_drive(struct floppy_state *fs);
-static void set_timeout(struct floppy_state *fs, int nticks,
- void (*proc)(unsigned long));
-static void fd_request_timeout(unsigned long);
-static void do_fd_request(request_queue_t * q);
-static void start_request(struct floppy_state *fs);
-
-static struct block_device_operations floppy_fops = {
- .open = floppy_open,
- .release = floppy_release,
- .ioctl = floppy_ioctl,
- .media_changed = floppy_check_change,
- .revalidate_disk= floppy_revalidate,
-};
-
-static struct request_queue *swim_queue;
-/*
- * SWIM IOP initialization
- */
-
-int swimiop_init(void)
-{
- volatile struct swim_iop_req req;
- struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0];
- struct swim_drvstatus *ds = &cmd->status;
- struct floppy_state *fs;
- int i;
-
- current_req = NULL;
- floppy_count = 0;
-
- if (!iop_ism_present)
- return -ENODEV;
-
- if (register_blkdev(FLOPPY_MAJOR, "fd"))
- return -EBUSY;
-
- swim_queue = blk_init_queue(do_fd_request, &swim_iop_lock);
- if (!swim_queue) {
- unregister_blkdev(FLOPPY_MAJOR, "fd");
- return -ENOMEM;
- }
-
- printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n",
- DRIVER_VERSION);
-
- if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) {
- printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n");
- unregister_blkdev(FLOPPY_MAJOR, "fd");
- blk_cleanup_queue(swim_queue);
- return -EBUSY;
- }
-
- printk(KERN_ERR "SWIM_IOP: probing for installed drives.\n");
-
- for (i = 0 ; i < MAX_FLOPPIES ; i++) {
- memset(&floppy_states[i], 0, sizeof(struct floppy_state));
- fs = &floppy_states[floppy_count];
-
- swimiop_init_request(&req);
- cmd->code = CMD_STATUS;
- cmd->drive_num = i + 1;
- if (swimiop_send_request(&req) != 0) continue;
- while (!req.complete);
- if (cmd->error != 0) {
- printk(KERN_ERR "SWIM-IOP: probe on drive %d returned error %d\n", i, (uint) cmd->error);
- continue;
- }
- if (ds->installed != 0x01) continue;
- printk("SWIM-IOP: drive %d is %s (%s, %s, %s, %s)\n", i,
- drive_names[ds->info.type],
- ds->info.external? "ext" : "int",
- ds->info.scsi? "scsi" : "floppy",
- ds->info.fixed? "fixed" : "removable",
- ds->info.secondary? "secondary" : "primary");
- swimiop_status_update(floppy_count, ds);
- fs->state = idle;
-
- init_timer(&fs->timeout);
- floppy_count++;
- }
- printk("SWIM-IOP: detected %d installed drives.\n", floppy_count);
-
- for (i = 0; i < floppy_count; i++) {
- struct gendisk *disk = alloc_disk(1);
- if (!disk)
- continue;
- disk->major = FLOPPY_MAJOR;
- disk->first_minor = i;
- disk->fops = &floppy_fops;
- sprintf(disk->disk_name, "fd%d", i);
- disk->private_data = &floppy_states[i];
- disk->queue = swim_queue;
- set_capacity(disk, 2880 * 2);
- add_disk(disk);
- }
-
- return 0;
-}
-
-static void swimiop_init_request(struct swim_iop_req *req)
-{
- req->sent = 0;
- req->complete = 0;
- req->done = NULL;
-}
-
-static int swimiop_send_request(struct swim_iop_req *req)
-{
- unsigned long flags;
- int err;
-
- /* It's doubtful an interrupt routine would try to send */
- /* a SWIM request, but I'd rather play it safe here. */
-
- local_irq_save(flags);
-
- if (current_req != NULL) {
- local_irq_restore(flags);
- return -ENOMEM;
- }
-
- current_req = req;
-
- /* Interrupts should be back on for iop_send_message() */
-
- local_irq_restore(flags);
-
- err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req,
- sizeof(req->command), (__u8 *) &req->command[0],
- swimiop_receive);
-
- /* No race condition here; we own current_req at this point */
-
- if (err) {
- current_req = NULL;
- } else {
- req->sent = 1;
- }
- return err;
-}
-
-/*
- * Receive a SWIM message from the IOP.
- *
- * This will be called in two cases:
- *
- * 1. A message has been successfully sent to the IOP.
- * 2. An unsolicited message was received from the IOP.
- */
-
-void swimiop_receive(struct iop_msg *msg)
-{
- struct swim_iop_req *req;
- struct swimmsg_status *sm;
- struct swim_drvstatus *ds;
-
- req = current_req;
-
- switch(msg->status) {
- case IOP_MSGSTATUS_COMPLETE:
- memcpy(&req->command[0], &msg->reply[0], sizeof(req->command));
- req->complete = 1;
- if (req->done) (*req->done)(req);
- current_req = NULL;
- break;
- case IOP_MSGSTATUS_UNSOL:
- sm = (struct swimmsg_status *) &msg->message[0];
- ds = &sm->status;
- swimiop_status_update(sm->drive_num, ds);
- iop_complete_message(msg);
- break;
- }
-}
-
-static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds)
-{
- struct floppy_state *fs = &floppy_states[drive_num];
-
- fs->write_prot = (ds->write_prot == 0x80);
- if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) {
- fs->ejected = 1;
- } else {
- fs->ejected = 0;
- }
- switch(ds->info.type) {
- case DRV_400K:
- fs->secpercyl = 10;
- fs->secpertrack = 10;
- fs->total_secs = 800;
- break;
- case DRV_800K:
- fs->secpercyl = 20;
- fs->secpertrack = 10;
- fs->total_secs = 1600;
- break;
- case DRV_FDHD:
- fs->secpercyl = 36;
- fs->secpertrack = 18;
- fs->total_secs = 2880;
- break;
- default:
- fs->secpercyl = 0;
- fs->secpertrack = 0;
- fs->total_secs = 0;
- break;
- }
-}
-
-static int swimiop_eject(struct floppy_state *fs)
-{
- int err, n;
- struct swim_iop_req req;
- struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0];
-
- err = grab_drive(fs, ejecting, 1);
- if (err) return err;
-
- swimiop_init_request(&req);
- cmd->code = CMD_EJECT;
- cmd->drive_num = fs->drive_num;
- err = swimiop_send_request(&req);
- if (err) {
- release_drive(fs);
- return err;
- }
- for (n = 2*HZ; n > 0; --n) {
- if (req.complete) break;
- if (signal_pending(current)) {
- err = -EINTR;
- break;
- }
- schedule_timeout_interruptible(1);
- }
- release_drive(fs);
- return cmd->error;
-}
-
-static struct floppy_struct floppy_type =
- { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
-
-static int floppy_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long param)
-{
- struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
- int err;
-
- if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- switch (cmd) {
- case FDEJECT:
- if (fs->ref_count != 1)
- return -EBUSY;
- err = swimiop_eject(fs);
- return err;
- case FDGETPRM:
- if (copy_to_user((void *) param, (void *) &floppy_type,
- sizeof(struct floppy_struct)))
- return -EFAULT;
- return 0;
- }
- return -ENOTTY;
-}
-
-static int floppy_open(struct inode *inode, struct file *filp)
-{
- struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
-
- if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
- return -EBUSY;
-
- if ((filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) {
- check_disk_change(inode->i_bdev);
- if (fs->ejected)
- return -ENXIO;
- }
-
- if ((filp->f_mode & 2) && fs->write_prot)
- return -EROFS;
-
- if (filp->f_flags & O_EXCL)
- fs->ref_count = -1;
- else
- ++fs->ref_count;
-
- return 0;
-}
-
-static int floppy_release(struct inode *inode, struct file *filp)
-{
- struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
- if (fs->ref_count > 0)
- fs->ref_count--;
- return 0;
-}
-
-static int floppy_check_change(struct gendisk *disk)
-{
- struct floppy_state *fs = disk->private_data;
- return fs->ejected;
-}
-
-static int floppy_revalidate(struct gendisk *disk)
-{
- struct floppy_state *fs = disk->private_data;
- grab_drive(fs, revalidating, 0);
- /* yadda, yadda */
- release_drive(fs);
- return 0;
-}
-
-static void floppy_off(unsigned int nr)
-{
-}
-
-static int grab_drive(struct floppy_state *fs, enum swim_state state,
- int interruptible)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- if (fs->state != idle) {
- ++fs->wanted;
- while (fs->state != available) {
- if (interruptible && signal_pending(current)) {
- --fs->wanted;
- local_irq_restore(flags);
- return -EINTR;
- }
- interruptible_sleep_on(&fs->wait);
- }
- --fs->wanted;
- }
- fs->state = state;
- local_irq_restore(flags);
- return 0;
-}
-
-static void release_drive(struct floppy_state *fs)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- fs->state = idle;
- start_request(fs);
- local_irq_restore(flags);
-}
-
-static void set_timeout(struct floppy_state *fs, int nticks,
- void (*proc)(unsigned long))
-{
- unsigned long flags;
-
- local_irq_save(flags);
- if (fs->timeout_pending)
- del_timer(&fs->timeout);
- init_timer(&fs->timeout);
- fs->timeout.expires = jiffies + nticks;
- fs->timeout.function = proc;
- fs->timeout.data = (unsigned long) fs;
- add_timer(&fs->timeout);
- fs->timeout_pending = 1;
- local_irq_restore(flags);
-}
-
-static void do_fd_request(request_queue_t * q)
-{
- int i;
-
- for (i = 0 ; i < floppy_count ; i++) {
- start_request(&floppy_states[i]);
- }
-}
-
-static void fd_request_complete(struct swim_iop_req *req)
-{
- struct floppy_state *fs = req->fs;
- struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];
-
- del_timer(&fs->timeout);
- fs->timeout_pending = 0;
- fs->state = idle;
- if (cmd->error) {
- printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error);
- end_request(CURRENT, 0);
- } else {
- CURRENT->sector += cmd->num_blocks;
- CURRENT->current_nr_sectors -= cmd->num_blocks;
- if (CURRENT->current_nr_sectors <= 0) {
- end_request(CURRENT, 1);
- return;
- }
- }
- start_request(fs);
-}
-
-static void fd_request_timeout(unsigned long data)
-{
- struct floppy_state *fs = (struct floppy_state *) data;
-
- fs->timeout_pending = 0;
- end_request(CURRENT, 0);
- fs->state = idle;
-}
-
-static void start_request(struct floppy_state *fs)
-{
- volatile struct swim_iop_req req;
- struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];
-
- if (fs->state == idle && fs->wanted) {
- fs->state = available;
- wake_up(&fs->wait);
- return;
- }
- while (CURRENT && fs->state == idle) {
- if (CURRENT->bh && !buffer_locked(CURRENT->bh))
- panic("floppy: block not locked");
-#if 0
- printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
- CURRENT->rq_disk->disk_name, CURRENT->cmd,
- CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
- printk(" errors=%d current_nr_sectors=%ld\n",
- CURRENT->errors, CURRENT->current_nr_sectors);
-#endif
-
- if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
- end_request(CURRENT, 0);
- continue;
- }
- if (CURRENT->current_nr_sectors == 0) {
- end_request(CURRENT, 1);
- continue;
- }
- if (fs->ejected) {
- end_request(CURRENT, 0);
- continue;
- }
-
- swimiop_init_request(&req);
- req.fs = fs;
- req.done = fd_request_complete;
-
- if (CURRENT->cmd == WRITE) {
- if (fs->write_prot) {
- end_request(CURRENT, 0);
- continue;
- }
- cmd->code = CMD_WRITE;
- } else {
- cmd->code = CMD_READ;
-
- }
- cmd->drive_num = fs->drive_num;
- cmd->buffer = CURRENT->buffer;
- cmd->first_block = CURRENT->sector;
- cmd->num_blocks = CURRENT->current_nr_sectors;
-
- if (swimiop_send_request(&req)) {
- end_request(CURRENT, 0);
- continue;
- }
-
- set_timeout(fs, HZ*CURRENT->current_nr_sectors,
- fd_request_timeout);
-
- fs->state = transferring;
- }
-}
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 2df5cf4..e4a2f8f 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1810,7 +1810,7 @@ static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s)
size = sizeof(s->disckey.value) + 4;
- if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
+ if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
@@ -1861,7 +1861,7 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
size = sizeof(s->manufact.value) + 4;
- if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
+ if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
@@ -2849,7 +2849,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
/* FIXME: we need upper bound checking, too!! */
if (lba < 0)
return -EINVAL;
- cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL);
+ cgc.buffer = kmalloc(blocksize, GFP_KERNEL);
if (cgc.buffer == NULL)
return -ENOMEM;
memset(&sense, 0, sizeof(sense));
@@ -3031,7 +3031,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
int size = sizeof(dvd_struct);
if (!CDROM_CAN(CDC_DVD))
return -ENOSYS;
- if ((s = (dvd_struct *) kmalloc(size, GFP_KERNEL)) == NULL)
+ if ((s = kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
if (copy_from_user(s, (dvd_struct __user *)arg, size)) {
diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c
index e6d8e9e..b6c61bb 100644
--- a/drivers/cdrom/cm206.c
+++ b/drivers/cdrom/cm206.c
@@ -1420,7 +1420,7 @@ int __init cm206_init(void)
return -EIO;
}
printk(" adapter at 0x%x", cm206_base);
- cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL);
+ cd = kmalloc(size, GFP_KERNEL);
if (!cd)
goto out_base;
/* Now we have found the adaptor card, try to reset it. As we have
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index b10f4d8..9e43e39 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -97,7 +97,7 @@ config SERIAL_NONSTANDARD
config COMPUTONE
tristate "Computone IntelliPort Plus serial support"
- depends on SERIAL_NONSTANDARD
+ depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
---help---
This driver supports the entire family of Intelliport II/Plus
controllers with the exception of the MicroChannel controllers and
@@ -203,7 +203,7 @@ config MOXA_SMARTIO
config MOXA_SMARTIO_NEW
tristate "Moxa SmartIO support v. 2.0 (EXPERIMENTAL)"
- depends on SERIAL_NONSTANDARD
+ depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA)
help
Say Y here if you have a Moxa SmartIO multiport serial card and/or
want to help develop a new version of this driver.
@@ -218,7 +218,7 @@ config MOXA_SMARTIO_NEW
config ISI
tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
- depends on SERIAL_NONSTANDARD
+ depends on SERIAL_NONSTANDARD && PCI
select FW_LOADER
help
This is a driver for the Multi-Tech cards which provide several
@@ -312,7 +312,7 @@ config SPECIALIX_RTSCTS
config SX
tristate "Specialix SX (and SI) card support"
- depends on SERIAL_NONSTANDARD
+ depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA)
help
This is a driver for the SX and SI multiport serial cards.
Please read the file <file:Documentation/sx.txt> for details.
@@ -867,7 +867,7 @@ config SONYPI
config TANBAC_TB0219
tristate "TANBAC TB0219 base board support"
- depends TANBAC_TB022X
+ depends on TANBAC_TB022X
select GPIO_VR41XX
source "drivers/char/agp/Kconfig"
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index c603bf2..a9f9c48 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -86,7 +86,7 @@ config AGP_NVIDIA
config AGP_SIS
tristate "SiS chipset support"
- depends on AGP
+ depends on AGP && X86
help
This option gives you AGP support for the GLX component of
X on Silicon Integrated Systems [SiS] chipsets.
@@ -103,7 +103,7 @@ config AGP_SWORKS
config AGP_VIA
tristate "VIA chipset support"
- depends on AGP
+ depends on AGP && X86
help
This option gives you AGP support for the GLX component of
X on VIA MVP3/Apollo Pro chipsets.
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 5ff457b..883a36a 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -419,6 +419,31 @@ static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_
*requested_mode &= ~AGP2_RESERVED_MASK;
}
+ /*
+ * Some dumb bridges are programmed to disobey the AGP2 spec.
+ * This is likely a BIOS misprogramming rather than poweron default, or
+ * it would be a lot more common.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=8816
+ * AGPv2 spec 6.1.9 states:
+ * The RATE field indicates the data transfer rates supported by this
+ * device. A.G.P. devices must report all that apply.
+ * Fix them up as best we can.
+ */
+ switch (*bridge_agpstat & 7) {
+ case 4:
+ *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X);
+ printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate"
+ "Fixing up support for x2 & x1\n");
+ break;
+ case 2:
+ *bridge_agpstat |= AGPSTAT2_1X;
+ printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate"
+ "Fixing up support for x1\n");
+ break;
+ default:
+ break;
+ }
+
/* Check the speed bits make sense. Only one should be set. */
tmp = *requested_mode & 7;
switch (tmp) {
diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c
index 04a1202..b99b756 100644
--- a/drivers/char/consolemap.c
+++ b/drivers/char/consolemap.c
@@ -443,7 +443,7 @@ int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
if (p && p->readonly) return -EIO;
if (!p || --p->refcount) {
- q = (struct uni_pagedir *)kmalloc(sizeof(*p), GFP_KERNEL);
+ q = kmalloc(sizeof(*p), GFP_KERNEL);
if (!q) {
if (p) p->refcount++;
return -ENOMEM;
diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c
index 6c59baa..e736119 100644
--- a/drivers/char/ipmi/ipmi_bt_sm.c
+++ b/drivers/char/ipmi/ipmi_bt_sm.c
@@ -37,8 +37,10 @@
#define BT_DEBUG_ENABLE 1 /* Generic messages */
#define BT_DEBUG_MSG 2 /* Prints all request/response buffers */
#define BT_DEBUG_STATES 4 /* Verbose look at state changes */
+/* BT_DEBUG_OFF must be zero to correspond to the default uninitialized
+ value */
-static int bt_debug = BT_DEBUG_OFF;
+static int bt_debug; /* 0 == BT_DEBUG_OFF */
module_param(bt_debug, int, 0644);
MODULE_PARM_DESC(bt_debug, "debug bitmask, 1=enable, 2=messages, 4=states");
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index e257835..ff2d052 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -834,7 +834,7 @@ static const struct file_operations ipmi_fops = {
#define DEVICE_NAME "ipmidev"
-static int ipmi_major = 0;
+static int ipmi_major;
module_param(ipmi_major, int, 0);
MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By"
" default, or if you set it to zero, it will choose the next"
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 5703ee2..4e4691a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -53,10 +53,10 @@
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
-static int initialized = 0;
+static int initialized;
#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry *proc_ipmi_root = NULL;
+static struct proc_dir_entry *proc_ipmi_root;
#endif /* CONFIG_PROC_FS */
/* Remain in auto-maintenance mode for this amount of time (in ms). */
@@ -2142,8 +2142,7 @@ cleanup_bmc_device(struct kref *ref)
bmc = container_of(ref, struct bmc_device, refcount);
remove_files(bmc);
- if (bmc->dev)
- platform_device_unregister(bmc->dev);
+ platform_device_unregister(bmc->dev);
kfree(bmc);
}
@@ -2341,8 +2340,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum,
while (ipmi_find_bmc_prod_dev_id(&ipmidriver,
bmc->id.product_id,
- bmc->id.device_id))
- {
+ bmc->id.device_id)) {
if (!warn_printed) {
printk(KERN_WARNING PFX
"This machine has two different BMCs"
@@ -4043,7 +4041,7 @@ static void send_panic_events(char *str)
}
#endif /* CONFIG_IPMI_PANIC_EVENT */
-static int has_panicked = 0;
+static int has_panicked;
static int panic_event(struct notifier_block *this,
unsigned long event,
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 597eb4f..9d23136e 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -58,10 +58,10 @@ static int poweroff_powercycle;
static int ifnum_to_use = -1;
/* Our local state. */
-static int ready = 0;
+static int ready;
static ipmi_user_t ipmi_user;
static int ipmi_ifnum;
-static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
+static void (*specific_poweroff_func)(ipmi_user_t user);
/* Holds the old poweroff function so we can restore it on removal. */
static void (*old_poweroff_func)(void);
@@ -182,7 +182,7 @@ static int ipmi_request_in_rc_mode(ipmi_user_t user,
#define IPMI_MOTOROLA_MANUFACTURER_ID 0x0000A1
#define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID 0x0051
-static void (*atca_oem_poweroff_hook)(ipmi_user_t user) = NULL;
+static void (*atca_oem_poweroff_hook)(ipmi_user_t user);
static void pps_poweroff_atca (ipmi_user_t user)
{
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 81a0c89..f1afd26 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -845,7 +845,7 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1);
}
-static int initialized = 0;
+static int initialized;
static void smi_timeout(unsigned long data)
{
@@ -1018,17 +1018,17 @@ static int num_ports;
static int irqs[SI_MAX_PARMS];
static int num_irqs;
static int regspacings[SI_MAX_PARMS];
-static int num_regspacings = 0;
+static int num_regspacings;
static int regsizes[SI_MAX_PARMS];
-static int num_regsizes = 0;
+static int num_regsizes;
static int regshifts[SI_MAX_PARMS];
-static int num_regshifts = 0;
+static int num_regshifts;
static int slave_addrs[SI_MAX_PARMS];
-static int num_slave_addrs = 0;
+static int num_slave_addrs;
#define IPMI_IO_ADDR_SPACE 0
#define IPMI_MEM_ADDR_SPACE 1
-static char *addr_space_to_str[] = { "I/O", "mem" };
+static char *addr_space_to_str[] = { "i/o", "mem" };
static int hotmod_handler(const char *val, struct kernel_param *kp);
@@ -1397,20 +1397,7 @@ static struct hotmod_vals hotmod_as[] = {
{ "i/o", IPMI_IO_ADDR_SPACE },
{ NULL }
};
-static int ipmi_strcasecmp(const char *s1, const char *s2)
-{
- while (*s1 || *s2) {
- if (!*s1)
- return -1;
- if (!*s2)
- return 1;
- if (*s1 != *s2)
- return *s1 - *s2;
- s1++;
- s2++;
- }
- return 0;
-}
+
static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr)
{
char *s;
@@ -1424,7 +1411,7 @@ static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr)
*s = '\0';
s++;
for (i = 0; hotmod_ops[i].name; i++) {
- if (ipmi_strcasecmp(*curr, v[i].name) == 0) {
+ if (strcmp(*curr, v[i].name) == 0) {
*val = v[i].val;
*curr = s;
return 0;
@@ -1435,10 +1422,34 @@ static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr)
return -EINVAL;
}
+static int check_hotmod_int_op(const char *curr, const char *option,
+ const char *name, int *val)
+{
+ char *n;
+
+ if (strcmp(curr, name) == 0) {
+ if (!option) {
+ printk(KERN_WARNING PFX
+ "No option given for '%s'\n",
+ curr);
+ return -EINVAL;
+ }
+ *val = simple_strtoul(option, &n, 0);
+ if ((*n != '\0') || (*option == '\0')) {
+ printk(KERN_WARNING PFX
+ "Bad option given for '%s'\n",
+ curr);
+ return -EINVAL;
+ }
+ return 1;
+ }
+ return 0;
+}
+
static int hotmod_handler(const char *val, struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
- int rv = -EINVAL;
+ int rv;
char *next, *curr, *s, *n, *o;
enum hotmod_op op;
enum si_type si_type;
@@ -1450,13 +1461,15 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
int irq;
int ipmb;
int ival;
+ int len;
struct smi_info *info;
if (!str)
return -ENOMEM;
/* Kill any trailing spaces, as we can get a "\n" from echo. */
- ival = strlen(str) - 1;
+ len = strlen(str);
+ ival = len - 1;
while ((ival >= 0) && isspace(str[ival])) {
str[ival] = '\0';
ival--;
@@ -1513,35 +1526,37 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
*o = '\0';
o++;
}
-#define HOTMOD_INT_OPT(name, val) \
- if (ipmi_strcasecmp(curr, name) == 0) { \
- if (!o) { \
- printk(KERN_WARNING PFX \
- "No option given for '%s'\n", \
- curr); \
- goto out; \
- } \
- val = simple_strtoul(o, &n, 0); \
- if ((*n != '\0') || (*o == '\0')) { \
- printk(KERN_WARNING PFX \
- "Bad option given for '%s'\n", \
- curr); \
- goto out; \
- } \
- }
-
- HOTMOD_INT_OPT("rsp", regspacing)
- else HOTMOD_INT_OPT("rsi", regsize)
- else HOTMOD_INT_OPT("rsh", regshift)
- else HOTMOD_INT_OPT("irq", irq)
- else HOTMOD_INT_OPT("ipmb", ipmb)
- else {
- printk(KERN_WARNING PFX
- "Invalid hotmod option '%s'\n",
- curr);
+ rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
+ if (rv < 0)
goto out;
- }
-#undef HOTMOD_INT_OPT
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
+ if (rv < 0)
+ goto out;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
+ if (rv < 0)
+ goto out;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "irq", &irq);
+ if (rv < 0)
+ goto out;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
+ if (rv < 0)
+ goto out;
+ else if (rv)
+ continue;
+
+ rv = -EINVAL;
+ printk(KERN_WARNING PFX
+ "Invalid hotmod option '%s'\n",
+ curr);
+ goto out;
}
if (op == HM_ADD) {
@@ -1590,6 +1605,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
mutex_unlock(&smi_infos_lock);
}
}
+ rv = len;
out:
kfree(str);
return rv;
@@ -1610,11 +1626,11 @@ static __devinit void hardcode_find_bmc(void)
info->addr_source = "hardcoded";
- if (!si_type[i] || ipmi_strcasecmp(si_type[i], "kcs") == 0) {
+ if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
info->si_type = SI_KCS;
- } else if (ipmi_strcasecmp(si_type[i], "smic") == 0) {
+ } else if (strcmp(si_type[i], "smic") == 0) {
info->si_type = SI_SMIC;
- } else if (ipmi_strcasecmp(si_type[i], "bt") == 0) {
+ } else if (strcmp(si_type[i], "bt") == 0) {
info->si_type = SI_BT;
} else {
printk(KERN_WARNING
@@ -1668,7 +1684,7 @@ static __devinit void hardcode_find_bmc(void)
/* Once we get an ACPI failure, we don't try any more, because we go
through the tables sequentially. Once we don't find a table, there
are no more. */
-static int acpi_failure = 0;
+static int acpi_failure;
/* For GPE-type interrupts. */
static u32 ipmi_acpi_gpe(void *context)
@@ -1779,7 +1795,6 @@ struct SPMITable {
static __devinit int try_init_acpi(struct SPMITable *spmi)
{
struct smi_info *info;
- char *io_type;
u8 addr_space;
if (spmi->IPMIlegacy != 1) {
@@ -1843,11 +1858,9 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
info->io.regshift = spmi->addr.register_bit_offset;
if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
- io_type = "memory";
info->io_setup = mem_setup;
info->io.addr_type = IPMI_IO_ADDR_SPACE;
} else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
- io_type = "I/O";
info->io_setup = port_setup;
info->io.addr_type = IPMI_MEM_ADDR_SPACE;
} else {
@@ -2773,8 +2786,7 @@ static __devinit int init_ipmi_si(void)
#endif
#ifdef CONFIG_ACPI
- if (si_trydefaults)
- acpi_find_bmc();
+ acpi_find_bmc();
#endif
#ifdef CONFIG_PCI
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 90fb2a5..7828038 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -134,14 +134,14 @@
static int nowayout = WATCHDOG_NOWAYOUT;
-static ipmi_user_t watchdog_user = NULL;
+static ipmi_user_t watchdog_user;
static int watchdog_ifnum;
/* Default the timeout to 10 seconds. */
static int timeout = 10;
/* The pre-timeout is disabled by default. */
-static int pretimeout = 0;
+static int pretimeout;
/* Default action is to reset the board on a timeout. */
static unsigned char action_val = WDOG_TIMEOUT_RESET;
@@ -156,10 +156,10 @@ static unsigned char preop_val = WDOG_PREOP_NONE;
static char preop[16] = "preop_none";
static DEFINE_SPINLOCK(ipmi_read_lock);
-static char data_to_read = 0;
+static char data_to_read;
static DECLARE_WAIT_QUEUE_HEAD(read_q);
-static struct fasync_struct *fasync_q = NULL;
-static char pretimeout_since_last_heartbeat = 0;
+static struct fasync_struct *fasync_q;
+static char pretimeout_since_last_heartbeat;
static char expect_close;
static int ifnum_to_use = -1;
@@ -177,7 +177,7 @@ static void ipmi_unregister_watchdog(int ipmi_intf);
/* If true, the driver will start running as soon as it is configured
and ready. */
-static int start_now = 0;
+static int start_now;
static int set_param_int(const char *val, struct kernel_param *kp)
{
@@ -300,16 +300,16 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
/* If shutting down via IPMI, we ignore the heartbeat. */
-static int ipmi_ignore_heartbeat = 0;
+static int ipmi_ignore_heartbeat;
/* Is someone using the watchdog? Only one user is allowed. */
-static unsigned long ipmi_wdog_open = 0;
+static unsigned long ipmi_wdog_open;
/* If set to 1, the heartbeat command will set the state to reset and
start the timer. The timer doesn't normally run when the driver is
first opened until the heartbeat is set the first time, this
variable is used to accomplish this. */
-static int ipmi_start_timer_on_heartbeat = 0;
+static int ipmi_start_timer_on_heartbeat;
/* IPMI version of the BMC. */
static unsigned char ipmi_version_major;
diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c
index da601fd..d649abb 100644
--- a/drivers/char/lcd.c
+++ b/drivers/char/lcd.c
@@ -459,7 +459,7 @@ static int lcd_ioctl(struct inode *inode, struct file *file,
(&display, (struct lcd_display *) arg,
sizeof(struct lcd_display)))
return -EFAULT;
- rom = (unsigned char *) kmalloc((128), GFP_ATOMIC);
+ rom = kmalloc((128), GFP_ATOMIC);
if (rom == NULL) {
printk(KERN_ERR LCD "kmalloc() failed in %s\n",
__FUNCTION__);
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index b70b538..b51d08b 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -525,7 +525,7 @@ static int lp_open(struct inode * inode, struct file * file)
return -EIO;
}
}
- lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
+ lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp_table[minor].lp_buffer) {
LP_F(minor) &= ~LP_BUSY;
return -ENOMEM;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 089020e..4f1813e 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -646,7 +646,8 @@ static inline size_t read_zero_pagealigned(char __user * buf, size_t size)
count = size;
zap_page_range(vma, addr, count, NULL);
- zeromap_page_range(vma, addr, count, PAGE_COPY);
+ if (zeromap_page_range(vma, addr, count, PAGE_COPY))
+ break;
size -= count;
buf += count;
@@ -713,11 +714,14 @@ out:
static int mmap_zero(struct file * file, struct vm_area_struct * vma)
{
+ int err;
+
if (vma->vm_flags & VM_SHARED)
return shmem_zero_setup(vma);
- if (zeromap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
- return -EAGAIN;
- return 0;
+ err = zeromap_page_range(vma, vma->vm_start,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+ BUG_ON(err == -EEXIST);
+ return err;
}
#else /* CONFIG_MMU */
static ssize_t read_zero(struct file * file, char * buf,
diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c
index efa8076..cd989dc 100644
--- a/drivers/char/mxser_new.c
+++ b/drivers/char/mxser_new.c
@@ -315,6 +315,7 @@ static struct mxser_mon_ext mon_data_ext;
static int mxser_set_baud_method[MXSER_PORTS + 1];
static spinlock_t gm_lock;
+#ifdef CONFIG_PCI
static int CheckIsMoxaMust(int io)
{
u8 oldmcr, hwid;
@@ -337,6 +338,7 @@ static int CheckIsMoxaMust(int io)
}
return MOXA_OTHER_UART;
}
+#endif
static void process_txrx_fifo(struct mxser_port *info)
{
@@ -2380,9 +2382,11 @@ static void mxser_release_res(struct mxser_board *brd, struct pci_dev *pdev,
if (irq)
free_irq(brd->irq, brd);
if (pdev != NULL) { /* PCI */
+#ifdef CONFIG_PCI
pci_release_region(pdev, 2);
pci_release_region(pdev, 3);
pci_dev_put(pdev);
+#endif
} else {
release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
release_region(brd->vector, 1);
@@ -2546,6 +2550,7 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
static int __devinit mxser_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
+#ifdef CONFIG_PCI
struct mxser_board *brd;
unsigned int i, j;
unsigned long ioaddress;
@@ -2644,6 +2649,9 @@ err_relio:
brd->info = NULL;
err:
return retval;
+#else
+ return -ENODEV;
+#endif
}
static void __devexit mxser_remove(struct pci_dev *pdev)
diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c
index 103d338..dc6d418 100644
--- a/drivers/char/n_r3964.c
+++ b/drivers/char/n_r3964.c
@@ -125,8 +125,8 @@ static void transmit_block(struct r3964_info *pInfo);
static void receive_char(struct r3964_info *pInfo, const unsigned char c);
static void receive_error(struct r3964_info *pInfo, const char flag);
static void on_timeout(unsigned long priv);
-static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg);
-static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char __user *buf);
+static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg);
+static int read_telegram(struct r3964_info *pInfo, struct pid *pid, unsigned char __user *buf);
static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
int error_code, struct r3964_block_header *pBlock);
static struct r3964_message* remove_msg(struct r3964_info *pInfo,
@@ -829,7 +829,7 @@ static void on_timeout(unsigned long priv)
}
static struct r3964_client_info *findClient(
- struct r3964_info *pInfo, pid_t pid)
+ struct r3964_info *pInfo, struct pid *pid)
{
struct r3964_client_info *pClient;
@@ -843,7 +843,7 @@ static struct r3964_client_info *findClient(
return NULL;
}
-static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg)
+static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
{
struct r3964_client_info *pClient;
struct r3964_client_info **ppClient;
@@ -858,7 +858,7 @@ static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg)
if(pClient->pid == pid)
{
- TRACE_PS("removing client %d from client list", pid);
+ TRACE_PS("removing client %d from client list", pid_nr(pid));
*ppClient = pClient->next;
while(pClient->msg_count)
{
@@ -869,6 +869,7 @@ static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg)
TRACE_M("enable_signals - msg kfree %p",pMsg);
}
}
+ put_pid(pClient->pid);
kfree(pClient);
TRACE_M("enable_signals - kfree %p",pClient);
return 0;
@@ -892,10 +893,10 @@ static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg)
if(pClient==NULL)
return -ENOMEM;
- TRACE_PS("add client %d to client list", pid);
+ TRACE_PS("add client %d to client list", pid_nr(pid));
spin_lock_init(&pClient->lock);
pClient->sig_flags=arg;
- pClient->pid = pid;
+ pClient->pid = get_pid(pid);
pClient->next=pInfo->firstClient;
pClient->first_msg = NULL;
pClient->last_msg = NULL;
@@ -908,7 +909,7 @@ static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg)
return 0;
}
-static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char __user *buf)
+static int read_telegram(struct r3964_info *pInfo, struct pid *pid, unsigned char __user *buf)
{
struct r3964_client_info *pClient;
struct r3964_block_header *block;
@@ -1005,7 +1006,7 @@ queue_the_message:
/* Send SIGIO signal to client process: */
if(pClient->sig_flags & R3964_USE_SIGIO)
{
- kill_proc(pClient->pid, SIGIO, 1);
+ kill_pid(pClient->pid, SIGIO, 1);
}
}
@@ -1042,7 +1043,7 @@ static void remove_client_block(struct r3964_info *pInfo,
{
struct r3964_block_header *block;
- TRACE_PS("remove_client_block PID %d", pClient->pid);
+ TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid));
block=pClient->next_block_to_read;
if(block)
@@ -1157,6 +1158,7 @@ static void r3964_close(struct tty_struct *tty)
TRACE_M("r3964_close - msg kfree %p",pMsg);
}
}
+ put_pid(pClient->pid);
kfree(pClient);
TRACE_M("r3964_close - client kfree %p",pClient);
pClient=pNext;
@@ -1193,12 +1195,11 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
struct r3964_client_message theMsg;
DECLARE_WAITQUEUE (wait, current);
- int pid = current->pid;
int count;
TRACE_L("read()");
- pClient=findClient(pInfo, pid);
+ pClient=findClient(pInfo, task_pid(current));
if(pClient)
{
pMsg = remove_msg(pInfo, pClient);
@@ -1252,7 +1253,6 @@ static ssize_t r3964_write(struct tty_struct * tty, struct file * file,
struct r3964_block_header *pHeader;
struct r3964_client_info *pClient;
unsigned char *new_data;
- int pid;
TRACE_L("write request, %d characters", count);
/*
@@ -1295,9 +1295,7 @@ static ssize_t r3964_write(struct tty_struct * tty, struct file * file,
pHeader->locks = 0;
pHeader->owner = NULL;
- pid=current->pid;
-
- pClient=findClient(pInfo, pid);
+ pClient=findClient(pInfo, task_pid(current));
if(pClient)
{
pHeader->owner = pClient;
@@ -1328,7 +1326,7 @@ static int r3964_ioctl(struct tty_struct * tty, struct file * file,
switch(cmd)
{
case R3964_ENABLE_SIGNALS:
- return enable_signals(pInfo, current->pid, arg);
+ return enable_signals(pInfo, task_pid(current), arg);
case R3964_SETPRIORITY:
if(arg<R3964_MASTER || arg>R3964_SLAVE)
return -EINVAL;
@@ -1341,7 +1339,7 @@ static int r3964_ioctl(struct tty_struct * tty, struct file * file,
pInfo->flags &= ~R3964_BCC;
return 0;
case R3964_READ_TELEGRAM:
- return read_telegram(pInfo, current->pid, (unsigned char __user *)arg);
+ return read_telegram(pInfo, task_pid(current), (unsigned char __user *)arg);
default:
return -ENOIOCTLCMD;
}
@@ -1357,7 +1355,6 @@ static unsigned int r3964_poll(struct tty_struct * tty, struct file * file,
struct poll_table_struct *wait)
{
struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data;
- int pid=current->pid;
struct r3964_client_info *pClient;
struct r3964_message *pMsg=NULL;
unsigned long flags;
@@ -1365,7 +1362,7 @@ static unsigned int r3964_poll(struct tty_struct * tty, struct file * file,
TRACE_L("POLL");
- pClient=findClient(pInfo,pid);
+ pClient=findClient(pInfo, task_pid(current));
if(pClient)
{
poll_wait(file, &pInfo->read_wait, wait);
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index e96a00f..2bdb014 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -1151,7 +1151,6 @@ static int copy_from_read_buf(struct tty_struct *tty,
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
- mb();
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
spin_lock_irqsave(&tty->read_lock, flags);
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 5152ced..f108c13 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -541,7 +541,7 @@ static int mgslpc_probe(struct pcmcia_device *link)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("mgslpc_attach\n");
- info = (MGSLPC_INFO *)kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
+ info = kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
if (!info) {
printk("Error can't allocate device instance data\n");
return -ENOMEM;
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 092a01c..13d0b13 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1203,7 +1203,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
static int uuid_strategy(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
- void __user *newval, size_t newlen, void **context)
+ void __user *newval, size_t newlen)
{
unsigned char tmp_uuid[16], *uuid;
unsigned int len;
diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c
index 167ebc8..245f031 100644
--- a/drivers/char/rio/riocmd.c
+++ b/drivers/char/rio/riocmd.c
@@ -556,7 +556,7 @@ struct CmdBlk *RIOGetCmdBlk(void)
{
struct CmdBlk *CmdBlkP;
- CmdBlkP = (struct CmdBlk *)kmalloc(sizeof(struct CmdBlk), GFP_ATOMIC);
+ CmdBlkP = kmalloc(sizeof(struct CmdBlk), GFP_ATOMIC);
if (CmdBlkP)
memset(CmdBlkP, 0, sizeof(struct CmdBlk));
return CmdBlkP;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 66a7385..e1d70e8 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -113,7 +113,7 @@ static int rtc_has_irq = 1;
#define hpet_set_rtc_irq_bit(arg) 0
#define hpet_rtc_timer_init() do { } while (0)
#define hpet_rtc_dropped_irq() 0
-static inline irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) {return 0;}
+static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) {return 0;}
#else
extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
#endif
@@ -165,7 +165,9 @@ static void mask_rtc_irq_bit(unsigned char bit)
}
#endif
+#ifdef CONFIG_PROC_FS
static int rtc_proc_open(struct inode *inode, struct file *file);
+#endif
/*
* Bits in rtc_status. (6 bits of room for future expansion)
@@ -906,6 +908,7 @@ static struct miscdevice rtc_dev = {
.fops = &rtc_fops,
};
+#ifdef CONFIG_PROC_FS
static const struct file_operations rtc_proc_fops = {
.owner = THIS_MODULE,
.open = rtc_proc_open,
@@ -913,14 +916,13 @@ static const struct file_operations rtc_proc_fops = {
.llseek = seq_lseek,
.release = single_release,
};
-
-#if defined(RTC_IRQ) && !defined(__sparc__)
-static irq_handler_t rtc_int_handler_ptr;
#endif
static int __init rtc_init(void)
{
+#ifdef CONFIG_PROC_FS
struct proc_dir_entry *ent;
+#endif
#if defined(__alpha__) || defined(__mips__)
unsigned int year, ctrl;
char *guess = NULL;
@@ -932,9 +934,11 @@ static int __init rtc_init(void)
struct sparc_isa_bridge *isa_br;
struct sparc_isa_device *isa_dev;
#endif
-#endif
-#ifndef __sparc__
+#else
void *r;
+#ifdef RTC_IRQ
+ irq_handler_t rtc_int_handler_ptr;
+#endif
#endif
#ifdef __sparc__
@@ -958,6 +962,7 @@ static int __init rtc_init(void)
}
}
#endif
+ rtc_has_irq = 0;
printk(KERN_ERR "rtc_init: no PC rtc found\n");
return -EIO;
@@ -972,6 +977,7 @@ found:
* PCI Slot 2 INTA# (and some INTx# in Slot 1).
*/
if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc", (void *)&rtc_port)) {
+ rtc_has_irq = 0;
printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
return -EIO;
}
@@ -982,6 +988,9 @@ no_irq:
else
r = request_mem_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
if (!r) {
+#ifdef RTC_IRQ
+ rtc_has_irq = 0;
+#endif
printk(KERN_ERR "rtc: I/O resource %lx is not free.\n",
(long)(RTC_PORT(0)));
return -EIO;
@@ -996,6 +1005,7 @@ no_irq:
if(request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED, "rtc", NULL)) {
/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
+ rtc_has_irq = 0;
printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
if (RTC_IOMAPPED)
release_region(RTC_PORT(0), RTC_IO_EXTENT);
@@ -1012,21 +1022,19 @@ no_irq:
if (misc_register(&rtc_dev)) {
#ifdef RTC_IRQ
free_irq(RTC_IRQ, NULL);
+ rtc_has_irq = 0;
#endif
release_region(RTC_PORT(0), RTC_IO_EXTENT);
return -ENODEV;
}
+#ifdef CONFIG_PROC_FS
ent = create_proc_entry("driver/rtc", 0, NULL);
- if (!ent) {
-#ifdef RTC_IRQ
- free_irq(RTC_IRQ, NULL);
+ if (ent)
+ ent->proc_fops = &rtc_proc_fops;
+ else
+ printk(KERN_WARNING "rtc: Failed to register with procfs.\n");
#endif
- release_region(RTC_PORT(0), RTC_IO_EXTENT);
- misc_deregister(&rtc_dev);
- return -ENOMEM;
- }
- ent->proc_fops = &rtc_proc_fops;
#if defined(__alpha__) || defined(__mips__)
rtc_freq = HZ;
@@ -1159,6 +1167,7 @@ static void rtc_dropped_irq(unsigned long data)
}
#endif
+#ifdef CONFIG_PROC_FS
/*
* Info exported via "/proc/driver/rtc".
*/
@@ -1243,6 +1252,7 @@ static int rtc_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, rtc_proc_show, NULL);
}
+#endif
void rtc_get_rtc_time(struct rtc_time *rtc_tm)
{
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index a3008ce..1da92a6 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -2498,8 +2498,10 @@ static void __devexit sx_remove_card(struct sx_board *board,
/* It is safe/allowed to del_timer a non-active timer */
del_timer(&board->timer);
if (pdev) {
+#ifdef CONFIG_PCI
pci_iounmap(pdev, board->base);
pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2);
+#endif
} else {
iounmap(board->base);
release_region(board->hw_base, board->hw_len);
@@ -2601,6 +2603,7 @@ static struct eisa_driver sx_eisadriver = {
#endif
+#ifdef CONFIG_PCI
/********************************************************
* Setting bit 17 in the CNTRL register of the PLX 9050 *
* chip forces a retry on writes while a read is pending.*
@@ -2632,10 +2635,12 @@ static void __devinit fix_sx_pci(struct pci_dev *pdev, struct sx_board *board)
}
iounmap(rebase);
}
+#endif
static int __devinit sx_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
+#ifdef CONFIG_PCI
struct sx_board *board;
unsigned int i, reg;
int retval = -EIO;
@@ -2700,6 +2705,9 @@ err_flag:
board->flags &= ~SX_BOARD_PRESENT;
err:
return retval;
+#else
+ return -ENODEV;
+#endif
}
static void __devexit sx_pci_remove(struct pci_dev *pdev)
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index acc6fab..3fa625d 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -4332,7 +4332,7 @@ static struct mgsl_struct* mgsl_allocate_device(void)
{
struct mgsl_struct *info;
- info = (struct mgsl_struct *)kmalloc(sizeof(struct mgsl_struct),
+ info = kmalloc(sizeof(struct mgsl_struct),
GFP_KERNEL);
if (!info) {
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
index 53e8ccf..8f4d67a 100644
--- a/drivers/char/synclinkmp.c
+++ b/drivers/char/synclinkmp.c
@@ -2730,7 +2730,7 @@ static int startup(SLMP_INFO * info)
return 0;
if (!info->tx_buf) {
- info->tx_buf = (unsigned char *)kmalloc(info->max_frame_size, GFP_KERNEL);
+ info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
if (!info->tx_buf) {
printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
__FILE__,__LINE__,info->device_name);
@@ -3798,7 +3798,7 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
{
SLMP_INFO *info;
- info = (SLMP_INFO *)kmalloc(sizeof(SLMP_INFO),
+ info = kmalloc(sizeof(SLMP_INFO),
GFP_KERNEL);
if (!info) {
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index 05810c8..1393523 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -41,7 +41,34 @@
#include <asm/irq_regs.h>
/* Whether we react on sysrq keys or just ignore them */
-int sysrq_enabled = 1;
+int __read_mostly __sysrq_enabled = 1;
+
+static int __read_mostly sysrq_always_enabled;
+
+int sysrq_on(void)
+{
+ return __sysrq_enabled || sysrq_always_enabled;
+}
+
+/*
+ * A value of 1 means 'all', other nonzero values are an op mask:
+ */
+static inline int sysrq_on_mask(int mask)
+{
+ return sysrq_always_enabled || __sysrq_enabled == 1 ||
+ (__sysrq_enabled & mask);
+}
+
+static int __init sysrq_always_enabled_setup(char *str)
+{
+ sysrq_always_enabled = 1;
+ printk(KERN_INFO "debug: sysrq always enabled.\n");
+
+ return 1;
+}
+
+__setup("sysrq_always_enabled", sysrq_always_enabled_setup);
+
static void sysrq_handle_loglevel(int key, struct tty_struct *tty)
{
@@ -379,8 +406,7 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
* Should we check for enabled operations (/proc/sysrq-trigger
* should not) and is the invoked operation enabled?
*/
- if (!check_mask || sysrq_enabled == 1 ||
- (sysrq_enabled & op_p->enable_mask)) {
+ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
printk("%s\n", op_p->action_msg);
console_loglevel = orig_log_level;
op_p->handler(key, tty);
@@ -414,9 +440,8 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
*/
void handle_sysrq(int key, struct tty_struct *tty)
{
- if (!sysrq_enabled)
- return;
- __handle_sysrq(key, tty, 1);
+ if (sysrq_on())
+ __handle_sysrq(key, tty, 1);
}
EXPORT_SYMBOL(handle_sysrq);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 4044c86..47a6eac 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -3335,18 +3335,13 @@ static void __do_SAK(struct work_struct *work)
int session;
int i;
struct file *filp;
- struct tty_ldisc *disc;
struct fdtable *fdt;
if (!tty)
return;
session = tty->session;
- /* We don't want an ldisc switch during this */
- disc = tty_ldisc_ref(tty);
- if (disc && disc->flush_buffer)
- disc->flush_buffer(tty);
- tty_ldisc_deref(disc);
+ tty_ldisc_flush(tty);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
@@ -3821,6 +3816,7 @@ struct tty_struct *get_current_tty(void)
barrier();
return tty;
}
+EXPORT_SYMBOL_GPL(get_current_tty);
/*
* Initialize the console device. This is called *early*, so
diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c
index 6d2e314..0e0da44 100644
--- a/drivers/char/viocons.c
+++ b/drivers/char/viocons.c
@@ -61,10 +61,7 @@
static DEFINE_SPINLOCK(consolelock);
static DEFINE_SPINLOCK(consoleloglock);
-#ifdef CONFIG_MAGIC_SYSRQ
static int vio_sysrq_pressed;
-extern int sysrq_enabled;
-#endif
#define VIOCHAR_NUM_BUF 16
@@ -936,8 +933,10 @@ static void vioHandleData(struct HvLpEvent *event)
*/
num_pushed = 0;
for (index = 0; index < cevent->len; index++) {
-#ifdef CONFIG_MAGIC_SYSRQ
- if (sysrq_enabled) {
+ /*
+ * Will be optimized away if !CONFIG_MAGIC_SYSRQ:
+ */
+ if (sysrq_on()) {
/* 0x0f is the ascii character for ^O */
if (cevent->data[index] == '\x0f') {
vio_sysrq_pressed = 1;
@@ -956,7 +955,6 @@ static void vioHandleData(struct HvLpEvent *event)
continue;
}
}
-#endif
/*
* The sysrq sequence isn't included in this check if
* sysrq is enabled and compiled into the kernel because
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index a8239da..06c32a3 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -784,7 +784,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
return 0;
- newscreen = (unsigned short *) kmalloc(new_screen_size, GFP_USER);
+ newscreen = kmalloc(new_screen_size, GFP_USER);
if (!newscreen)
return -ENOMEM;
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index ac5d60e..dc8368e 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -129,7 +129,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str
!capable(CAP_SYS_RESOURCE))
return -EPERM;
- key_map = (ushort *) kmalloc(sizeof(plain_map),
+ key_map = kmalloc(sizeof(plain_map),
GFP_KERNEL);
if (!key_map)
return -ENOMEM;
@@ -259,7 +259,7 @@ do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
sz = 256;
while (sz < funcbufsize - funcbufleft + delta)
sz <<= 1;
- fnw = (char *) kmalloc(sz, GFP_KERNEL);
+ fnw = kmalloc(sz, GFP_KERNEL);
if(!fnw) {
ret = -ENOMEM;
goto reterr;
@@ -1087,7 +1087,7 @@ static void complete_change_console(struct vc_data *vc)
switch_screen(vc);
/*
- * This can't appear below a successful kill_proc(). If it did,
+ * This can't appear below a successful kill_pid(). If it did,
* then the *blank_screen operation could occur while X, having
* received acqsig, is waking up on another processor. This
* condition can lead to overlapping accesses to the VGA range
@@ -1110,7 +1110,7 @@ static void complete_change_console(struct vc_data *vc)
*/
if (vc->vt_mode.mode == VT_PROCESS) {
/*
- * Send the signal as privileged - kill_proc() will
+ * Send the signal as privileged - kill_pid() will
* tell us if the process has gone or something else
* is awry
*/
@@ -1170,7 +1170,7 @@ void change_console(struct vc_data *new_vc)
vc = vc_cons[fg_console].d;
if (vc->vt_mode.mode == VT_PROCESS) {
/*
- * Send the signal as privileged - kill_proc() will
+ * Send the signal as privileged - kill_pid() will
* tell us if the process has gone or something else
* is awry
*/
diff --git a/drivers/char/watchdog/at91rm9200_wdt.c b/drivers/char/watchdog/at91rm9200_wdt.c
index cb86967..38bd373 100644
--- a/drivers/char/watchdog/at91rm9200_wdt.c
+++ b/drivers/char/watchdog/at91rm9200_wdt.c
@@ -203,9 +203,9 @@ static int __init at91wdt_probe(struct platform_device *pdev)
{
int res;
- if (at91wdt_miscdev.dev)
+ if (at91wdt_miscdev.parent)
return -EBUSY;
- at91wdt_miscdev.dev = &pdev->dev;
+ at91wdt_miscdev.parent = &pdev->dev;
res = misc_register(&at91wdt_miscdev);
if (res)
@@ -221,7 +221,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
res = misc_deregister(&at91wdt_miscdev);
if (!res)
- at91wdt_miscdev.dev = NULL;
+ at91wdt_miscdev.parent = NULL;
return res;
}
diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c
index 3404a9c..e88947f 100644
--- a/drivers/char/watchdog/mpcore_wdt.c
+++ b/drivers/char/watchdog/mpcore_wdt.c
@@ -347,7 +347,7 @@ static int __devinit mpcore_wdt_probe(struct platform_device *dev)
goto err_free;
}
- mpcore_wdt_miscdev.dev = &dev->dev;
+ mpcore_wdt_miscdev.parent = &dev->dev;
ret = misc_register(&mpcore_wdt_miscdev);
if (ret) {
dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n",
diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c
index 5dbd7dc..6c6f973 100644
--- a/drivers/char/watchdog/omap_wdt.c
+++ b/drivers/char/watchdog/omap_wdt.c
@@ -290,7 +290,7 @@ static int __init omap_wdt_probe(struct platform_device *pdev)
omap_wdt_disable();
omap_wdt_adjust_timeout(timer_margin);
- omap_wdt_miscdev.dev = &pdev->dev;
+ omap_wdt_miscdev.parent = &pdev->dev;
ret = misc_register(&omap_wdt_miscdev);
if (ret)
goto fail;
diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c
index 6113872..2da5ac9 100644
--- a/drivers/char/watchdog/pcwd_usb.c
+++ b/drivers/char/watchdog/pcwd_usb.c
@@ -42,6 +42,7 @@
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
+#include <linux/hid.h> /* For HID_REQ_SET_REPORT & HID_DT_REPORT */
#ifdef CONFIG_USB_DEBUG
@@ -109,10 +110,6 @@ MODULE_DEVICE_TABLE (usb, usb_pcwd_table);
#define CMD_ENABLE_WATCHDOG 0x30 /* Enable / Disable Watchdog */
#define CMD_DISABLE_WATCHDOG CMD_ENABLE_WATCHDOG
-/* Some defines that I like to be somewhere else like include/linux/usb_hid.h */
-#define HID_REQ_SET_REPORT 0x09
-#define HID_DT_REPORT (USB_TYPE_CLASS | 0x02)
-
/* We can only use 1 card due to the /dev/watchdog restriction */
static int cards_found;
diff --git a/drivers/char/watchdog/rm9k_wdt.c b/drivers/char/watchdog/rm9k_wdt.c
index ec39093..7576a13 100644
--- a/drivers/char/watchdog/rm9k_wdt.c
+++ b/drivers/char/watchdog/rm9k_wdt.c
@@ -47,7 +47,7 @@
/* Function prototypes */
-static irqreturn_t wdt_gpi_irqhdl(int, void *, struct pt_regs *);
+static irqreturn_t wdt_gpi_irqhdl(int, void *);
static void wdt_gpi_start(void);
static void wdt_gpi_stop(void);
static void wdt_gpi_set_timeout(unsigned int);
@@ -94,8 +94,28 @@ module_param(nowayout, bool, 0444);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started");
+/* Kernel interfaces */
+static struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = wdt_gpi_open,
+ .release = wdt_gpi_release,
+ .write = wdt_gpi_write,
+ .unlocked_ioctl = wdt_gpi_ioctl,
+};
+
+static struct miscdevice miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = wdt_gpi_name,
+ .fops = &fops,
+};
+
+static struct notifier_block wdt_gpi_shutdown = {
+ .notifier_call = wdt_gpi_notify,
+};
+
+
/* Interrupt handler */
-static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt, struct pt_regs *regs)
+static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt)
{
if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1))
return IRQ_NONE;
@@ -312,26 +332,6 @@ wdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused)
}
-/* Kernel interfaces */
-static struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = wdt_gpi_open,
- .release = wdt_gpi_release,
- .write = wdt_gpi_write,
- .unlocked_ioctl = wdt_gpi_ioctl,
-};
-
-static struct miscdevice miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = wdt_gpi_name,
- .fops = &fops,
-};
-
-static struct notifier_block wdt_gpi_shutdown = {
- .notifier_call = wdt_gpi_notify,
-};
-
-
/* Init & exit procedures */
static const struct resource *
wdt_gpi_get_resource(struct platform_device *pdv, const char *name,
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index 8ab61ef..b6bcdbb 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -77,11 +77,11 @@ static struct clocksource clocksource_acpi_pm = {
#ifdef CONFIG_PCI
-static int acpi_pm_good;
+static int __devinitdata acpi_pm_good;
static int __init acpi_pm_good_setup(char *__str)
{
- acpi_pm_good = 1;
- return 1;
+ acpi_pm_good = 1;
+ return 1;
}
__setup("acpi_pm_good", acpi_pm_good_setup);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 47ab42d..9fb2edf 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -29,7 +29,8 @@
#include <linux/completion.h>
#include <linux/mutex.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "cpufreq-core", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
+ "cpufreq-core", msg)
/**
* The "cpufreq driver" - the arch- or hardware-dependent low
@@ -151,7 +152,8 @@ static void cpufreq_debug_disable_ratelimit(void)
spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
}
-void cpufreq_debug_printk(unsigned int type, const char *prefix, const char *fmt, ...)
+void cpufreq_debug_printk(unsigned int type, const char *prefix,
+ const char *fmt, ...)
{
char s[256];
va_list args;
@@ -161,7 +163,8 @@ void cpufreq_debug_printk(unsigned int type, const char *prefix, const char *fmt
WARN_ON(!prefix);
if (type & debug) {
spin_lock_irqsave(&disable_ratelimit_lock, flags);
- if (!disable_ratelimit && debug_ratelimit && !printk_ratelimit()) {
+ if (!disable_ratelimit && debug_ratelimit
+ && !printk_ratelimit()) {
spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
return;
}
@@ -182,10 +185,12 @@ EXPORT_SYMBOL(cpufreq_debug_printk);
module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core, 2 to debug drivers, and 4 to debug governors.");
+MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core,"
+ " 2 to debug drivers, and 4 to debug governors.");
module_param(debug_ratelimit, uint, 0644);
-MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging: set to 0 to disable ratelimiting.");
+MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging:"
+ " set to 0 to disable ratelimiting.");
#else /* !CONFIG_CPU_FREQ_DEBUG */
@@ -219,17 +224,23 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
if (!l_p_j_ref_freq) {
l_p_j_ref = loops_per_jiffy;
l_p_j_ref_freq = ci->old;
- dprintk("saving %lu as reference value for loops_per_jiffy; freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
+ dprintk("saving %lu as reference value for loops_per_jiffy;"
+ "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
}
if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) ||
(val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
(val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
- loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
- dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
+ loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq,
+ ci->new);
+ dprintk("scaling loops_per_jiffy to %lu"
+ "for frequency %u kHz\n", loops_per_jiffy, ci->new);
}
}
#else
-static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; }
+static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
+{
+ return;
+}
#endif
@@ -316,7 +327,8 @@ static int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_PERFORMANCE;
err = 0;
- } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {
+ } else if (!strnicmp(str_governor, "powersave",
+ CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_POWERSAVE;
err = 0;
}
@@ -328,7 +340,8 @@ static int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
t = __find_governor(str_governor);
if (t == NULL) {
- char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", str_governor);
+ char *name = kasprintf(GFP_KERNEL, "cpufreq_%s",
+ str_governor);
if (name) {
int ret;
@@ -361,7 +374,8 @@ extern struct sysdev_class cpu_sysdev_class;
/**
- * cpufreq_per_cpu_attr_read() / show_##file_name() - print out cpufreq information
+ * cpufreq_per_cpu_attr_read() / show_##file_name() -
+ * print out cpufreq information
*
* Write out information from cpufreq_driver->policy[cpu]; object must be
* "unsigned int".
@@ -380,7 +394,8 @@ show_one(scaling_min_freq, min);
show_one(scaling_max_freq, max);
show_one(scaling_cur_freq, cur);
-static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy);
+static int __cpufreq_set_policy(struct cpufreq_policy *data,
+ struct cpufreq_policy *policy);
/**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
@@ -416,7 +431,8 @@ store_one(scaling_max_freq,max);
/**
* show_cpuinfo_cur_freq - current CPU frequency as detected by hardware
*/
-static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, char *buf)
+static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy,
+ char *buf)
{
unsigned int cur_freq = cpufreq_get(policy->cpu);
if (!cur_freq)
@@ -428,7 +444,8 @@ static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, char *buf)
/**
* show_scaling_governor - show the current policy for the specified CPU
*/
-static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf)
+static ssize_t show_scaling_governor (struct cpufreq_policy * policy,
+ char *buf)
{
if(policy->policy == CPUFREQ_POLICY_POWERSAVE)
return sprintf(buf, "powersave\n");
@@ -458,7 +475,8 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy,
if (ret != 1)
return -EINVAL;
- if (cpufreq_parse_governor(str_governor, &new_policy.policy, &new_policy.governor))
+ if (cpufreq_parse_governor(str_governor, &new_policy.policy,
+ &new_policy.governor))
return -EINVAL;
lock_cpu_hotplug();
@@ -474,7 +492,10 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy,
unlock_cpu_hotplug();
- return ret ? ret : count;
+ if (ret)
+ return ret;
+ else
+ return count;
}
/**
@@ -488,7 +509,7 @@ static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf)
/**
* show_scaling_available_governors - show the available CPUfreq governors
*/
-static ssize_t show_scaling_available_governors (struct cpufreq_policy * policy,
+static ssize_t show_scaling_available_governors (struct cpufreq_policy *policy,
char *buf)
{
ssize_t i = 0;
@@ -574,7 +595,11 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf)
policy = cpufreq_cpu_get(policy->cpu);
if (!policy)
return -EINVAL;
- ret = fattr->show ? fattr->show(policy,buf) : -EIO;
+ if (fattr->show)
+ ret = fattr->show(policy, buf);
+ else
+ ret = -EIO;
+
cpufreq_cpu_put(policy);
return ret;
}
@@ -588,7 +613,11 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr,
policy = cpufreq_cpu_get(policy->cpu);
if (!policy)
return -EINVAL;
- ret = fattr->store ? fattr->store(policy,buf,count) : -EIO;
+ if (fattr->store)
+ ret = fattr->store(policy, buf, count);
+ else
+ ret = -EIO;
+
cpufreq_cpu_put(policy);
return ret;
}
@@ -913,7 +942,8 @@ static void handle_update(struct work_struct *work)
* We adjust to current frequency first, and need to clean up later. So either call
* to cpufreq_update_policy() or schedule handle_update()).
*/
-static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, unsigned int new_freq)
+static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
+ unsigned int new_freq)
{
struct cpufreq_freqs freqs;
@@ -938,16 +968,16 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, unsigne
unsigned int cpufreq_quick_get(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
- unsigned int ret = 0;
+ unsigned int ret_freq = 0;
if (policy) {
mutex_lock(&policy->lock);
- ret = policy->cur;
+ ret_freq = policy->cur;
mutex_unlock(&policy->lock);
cpufreq_cpu_put(policy);
}
- return (ret);
+ return (ret_freq);
}
EXPORT_SYMBOL(cpufreq_quick_get);
@@ -961,7 +991,7 @@ EXPORT_SYMBOL(cpufreq_quick_get);
unsigned int cpufreq_get(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
- unsigned int ret = 0;
+ unsigned int ret_freq = 0;
if (!policy)
return 0;
@@ -971,12 +1001,14 @@ unsigned int cpufreq_get(unsigned int cpu)
mutex_lock(&policy->lock);
- ret = cpufreq_driver->get(cpu);
+ ret_freq = cpufreq_driver->get(cpu);
- if (ret && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
- /* verify no discrepancy between actual and saved value exists */
- if (unlikely(ret != policy->cur)) {
- cpufreq_out_of_sync(cpu, policy->cur, ret);
+ if (ret_freq && policy->cur &&
+ !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
+ /* verify no discrepancy between actual and
+ saved value exists */
+ if (unlikely(ret_freq != policy->cur)) {
+ cpufreq_out_of_sync(cpu, policy->cur, ret_freq);
schedule_work(&policy->update);
}
}
@@ -986,7 +1018,7 @@ unsigned int cpufreq_get(unsigned int cpu)
out:
cpufreq_cpu_put(policy);
- return (ret);
+ return (ret_freq);
}
EXPORT_SYMBOL(cpufreq_get);
@@ -998,7 +1030,7 @@ EXPORT_SYMBOL(cpufreq_get);
static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg)
{
int cpu = sysdev->id;
- unsigned int ret = 0;
+ int ret = 0;
unsigned int cur_freq = 0;
struct cpufreq_policy *cpu_policy;
@@ -1080,7 +1112,7 @@ out:
static int cpufreq_resume(struct sys_device * sysdev)
{
int cpu = sysdev->id;
- unsigned int ret = 0;
+ int ret = 0;
struct cpufreq_policy *cpu_policy;
dprintk("resuming cpu %u\n", cpu);
@@ -1276,22 +1308,45 @@ int cpufreq_driver_target(struct cpufreq_policy *policy,
}
EXPORT_SYMBOL_GPL(cpufreq_driver_target);
+int cpufreq_driver_getavg(struct cpufreq_policy *policy)
+{
+ int ret = 0;
+
+ policy = cpufreq_cpu_get(policy->cpu);
+ if (!policy)
+ return -EINVAL;
+
+ mutex_lock(&policy->lock);
+
+ if (cpu_online(policy->cpu) && cpufreq_driver->getavg)
+ ret = cpufreq_driver->getavg(policy->cpu);
+
+ mutex_unlock(&policy->lock);
+
+ cpufreq_cpu_put(policy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_driver_getavg);
+
/*
* Locking: Must be called with the lock_cpu_hotplug() lock held
* when "event" is CPUFREQ_GOV_LIMITS
*/
-static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event)
+static int __cpufreq_governor(struct cpufreq_policy *policy,
+ unsigned int event)
{
int ret;
if (!try_module_get(policy->governor->owner))
return -EINVAL;
- dprintk("__cpufreq_governor for CPU %u, event %u\n", policy->cpu, event);
+ dprintk("__cpufreq_governor for CPU %u, event %u\n",
+ policy->cpu, event);
ret = policy->governor->governor(policy, event);
- /* we keep one module reference alive for each CPU governed by this CPU */
+ /* we keep one module reference alive for
+ each CPU governed by this CPU */
if ((event != CPUFREQ_GOV_START) || ret)
module_put(policy->governor->owner);
if ((event == CPUFREQ_GOV_STOP) && !ret)
@@ -1367,9 +1422,12 @@ EXPORT_SYMBOL(cpufreq_get_policy);
/*
+ * data : current policy.
+ * policy : policy to be set.
* Locking: Must be called with the lock_cpu_hotplug() lock held
*/
-static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy)
+static int __cpufreq_set_policy(struct cpufreq_policy *data,
+ struct cpufreq_policy *policy)
{
int ret = 0;
@@ -1377,7 +1435,8 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli
dprintk("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
policy->min, policy->max);
- memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo));
+ memcpy(&policy->cpuinfo, &data->cpuinfo,
+ sizeof(struct cpufreq_cpuinfo));
if (policy->min > data->min && policy->min > policy->max) {
ret = -EINVAL;
@@ -1410,7 +1469,8 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli
data->min = policy->min;
data->max = policy->max;
- dprintk("new min and max freqs are %u - %u kHz\n", data->min, data->max);
+ dprintk("new min and max freqs are %u - %u kHz\n",
+ data->min, data->max);
if (cpufreq_driver->setpolicy) {
data->policy = policy->policy;
@@ -1431,10 +1491,12 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli
data->governor = policy->governor;
if (__cpufreq_governor(data, CPUFREQ_GOV_START)) {
/* new governor failed, so re-start old one */
- dprintk("starting governor %s failed\n", data->governor->name);
+ dprintk("starting governor %s failed\n",
+ data->governor->name);
if (old_gov) {
data->governor = old_gov;
- __cpufreq_governor(data, CPUFREQ_GOV_START);
+ __cpufreq_governor(data,
+ CPUFREQ_GOV_START);
}
ret = -EINVAL;
goto error_out;
@@ -1524,7 +1586,8 @@ int cpufreq_update_policy(unsigned int cpu)
data->cur = policy.cur;
} else {
if (data->cur != policy.cur)
- cpufreq_out_of_sync(cpu, data->cur, policy.cur);
+ cpufreq_out_of_sync(cpu, data->cur,
+ policy.cur);
}
}
@@ -1626,8 +1689,10 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
/* if all ->init() calls failed, unregister */
if (ret) {
- dprintk("no CPU initialized for driver %s\n", driver_data->name);
- sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver);
+ dprintk("no CPU initialized for driver %s\n",
+ driver_data->name);
+ sysdev_driver_unregister(&cpu_sysdev_class,
+ &cpufreq_sysdev_driver);
spin_lock_irqsave(&cpufreq_driver_lock, flags);
cpufreq_driver = NULL;
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 5ef5ede..eef0270 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -44,15 +44,17 @@
* latency of the processor. The governor will work on any processor with
* transition latency <= 10mS, using appropriate sampling
* rate.
- * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
- * this governor will not work.
+ * For CPUs with transition latency > 10mS (mostly drivers
+ * with CPUFREQ_ETERNAL), this governor will not work.
* All times here are in uS.
*/
static unsigned int def_sampling_rate;
#define MIN_SAMPLING_RATE_RATIO (2)
/* for correct statistics, we need at least 10 ticks between each measure */
-#define MIN_STAT_SAMPLING_RATE (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
-#define MIN_SAMPLING_RATE (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+#define MIN_STAT_SAMPLING_RATE \
+ (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
+#define MIN_SAMPLING_RATE \
+ (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
#define MAX_SAMPLING_RATE (500 * def_sampling_rate)
#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000)
#define DEF_SAMPLING_DOWN_FACTOR (1)
@@ -103,11 +105,16 @@ static struct dbs_tuners dbs_tuners_ins = {
static inline unsigned int get_cpu_idle_time(unsigned int cpu)
{
- return kstat_cpu(cpu).cpustat.idle +
+ unsigned int add_nice = 0, ret;
+
+ if (dbs_tuners_ins.ignore_nice)
+ add_nice = kstat_cpu(cpu).cpustat.nice;
+
+ ret = kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait +
- ( dbs_tuners_ins.ignore_nice ?
- kstat_cpu(cpu).cpustat.nice :
- 0);
+ add_nice;
+
+ return ret;
}
/************************** sysfs interface ************************/
@@ -452,6 +459,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int cpu = policy->cpu;
struct cpu_dbs_info_s *this_dbs_info;
unsigned int j;
+ int rc;
this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
@@ -468,6 +476,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
break;
mutex_lock(&dbs_mutex);
+
+ rc = sysfs_create_group(&policy->kobj, &dbs_attr_group);
+ if (rc) {
+ mutex_unlock(&dbs_mutex);
+ return rc;
+ }
+
for_each_cpu_mask(j, policy->cpus) {
struct cpu_dbs_info_s *j_dbs_info;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
@@ -480,7 +495,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
this_dbs_info->enable = 1;
this_dbs_info->down_skip = 0;
this_dbs_info->requested_freq = policy->cur;
- sysfs_create_group(&policy->kobj, &dbs_attr_group);
+
dbs_enable++;
/*
* Start the timerschedule work, when this governor
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index e1cc511..f697449 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -41,8 +41,10 @@
static unsigned int def_sampling_rate;
#define MIN_SAMPLING_RATE_RATIO (2)
/* for correct statistics, we need at least 10 ticks between each measure */
-#define MIN_STAT_SAMPLING_RATE (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
-#define MIN_SAMPLING_RATE (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+#define MIN_STAT_SAMPLING_RATE \
+ (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
+#define MIN_SAMPLING_RATE \
+ (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
#define MAX_SAMPLING_RATE (500 * def_sampling_rate)
#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000)
#define TRANSITION_LATENCY_LIMIT (10 * 1000)
@@ -206,7 +208,8 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused,
ret = sscanf(buf, "%u", &input);
mutex_lock(&dbs_mutex);
- if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) {
+ if (ret != 1 || input > MAX_SAMPLING_RATE
+ || input < MIN_SAMPLING_RATE) {
mutex_unlock(&dbs_mutex);
return -EINVAL;
}
@@ -397,8 +400,15 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
* policy. To be safe, we focus 10 points under the threshold.
*/
if (load < (dbs_tuners_ins.up_threshold - 10)) {
- unsigned int freq_next = (policy->cur * load) /
+ unsigned int freq_next, freq_cur;
+
+ freq_cur = cpufreq_driver_getavg(policy);
+ if (!freq_cur)
+ freq_cur = policy->cur;
+
+ freq_next = (freq_cur * load) /
(dbs_tuners_ins.up_threshold - 10);
+
if (!dbs_tuners_ins.powersave_bias) {
__cpufreq_driver_target(policy, freq_next,
CPUFREQ_RELATION_L);
@@ -472,6 +482,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int cpu = policy->cpu;
struct cpu_dbs_info_s *this_dbs_info;
unsigned int j;
+ int rc;
this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
@@ -494,12 +505,23 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
if (dbs_enable == 1) {
kondemand_wq = create_workqueue("kondemand");
if (!kondemand_wq) {
- printk(KERN_ERR "Creation of kondemand failed\n");
+ printk(KERN_ERR
+ "Creation of kondemand failed\n");
dbs_enable--;
mutex_unlock(&dbs_mutex);
return -ENOSPC;
}
}
+
+ rc = sysfs_create_group(&policy->kobj, &dbs_attr_group);
+ if (rc) {
+ if (dbs_enable == 1)
+ destroy_workqueue(kondemand_wq);
+ dbs_enable--;
+ mutex_unlock(&dbs_mutex);
+ return rc;
+ }
+
for_each_cpu_mask(j, policy->cpus) {
struct cpu_dbs_info_s *j_dbs_info;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
@@ -509,7 +531,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
j_dbs_info->prev_cpu_wall = get_jiffies_64();
}
this_dbs_info->enable = 1;
- sysfs_create_group(&policy->kobj, &dbs_attr_group);
/*
* Start the timerschedule work, when this governor
* is used for first time
diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c
index de91e33..e8e1451 100644
--- a/drivers/cpufreq/cpufreq_performance.c
+++ b/drivers/cpufreq/cpufreq_performance.c
@@ -15,7 +15,8 @@
#include <linux/cpufreq.h>
#include <linux/init.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg)
+#define dprintk(msg...) \
+ cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg)
static int cpufreq_governor_performance(struct cpufreq_policy *policy,
@@ -24,8 +25,10 @@ static int cpufreq_governor_performance(struct cpufreq_policy *policy,
switch (event) {
case CPUFREQ_GOV_START:
case CPUFREQ_GOV_LIMITS:
- dprintk("setting to %u kHz because of event %u\n", policy->max, event);
- __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H);
+ dprintk("setting to %u kHz because of event %u\n",
+ policy->max, event);
+ __cpufreq_driver_target(policy, policy->max,
+ CPUFREQ_RELATION_H);
break;
default:
break;
diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c
index 0a25960..13fe06b 100644
--- a/drivers/cpufreq/cpufreq_powersave.c
+++ b/drivers/cpufreq/cpufreq_powersave.c
@@ -15,7 +15,8 @@
#include <linux/cpufreq.h>
#include <linux/init.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg)
+#define dprintk(msg...) \
+ cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg)
static int cpufreq_governor_powersave(struct cpufreq_policy *policy,
unsigned int event)
@@ -23,8 +24,10 @@ static int cpufreq_governor_powersave(struct cpufreq_policy *policy,
switch (event) {
case CPUFREQ_GOV_START:
case CPUFREQ_GOV_LIMITS:
- dprintk("setting to %u kHz because of event %u\n", policy->min, event);
- __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L);
+ dprintk("setting to %u kHz because of event %u\n",
+ policy->min, event);
+ __cpufreq_driver_target(policy, policy->min,
+ CPUFREQ_RELATION_L);
break;
default:
break;
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index c2ecc59..6742b1a 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -351,8 +351,8 @@ __init cpufreq_stats_init(void)
register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
for_each_online_cpu(cpu) {
- cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_ONLINE,
- (void *)(long)cpu);
+ cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier,
+ CPU_ONLINE, (void *)(long)cpu);
}
return 0;
}
@@ -368,14 +368,15 @@ __exit cpufreq_stats_exit(void)
unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
lock_cpu_hotplug();
for_each_online_cpu(cpu) {
- cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_DEAD,
- (void *)(long)cpu);
+ cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier,
+ CPU_DEAD, (void *)(long)cpu);
}
unlock_cpu_hotplug();
}
MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
-MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats through sysfs filesystem");
+MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats"
+ "through sysfs filesystem");
MODULE_LICENSE ("GPL");
module_init(cpufreq_stats_init);
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index a06c204..2a4eb0b 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -131,19 +131,26 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
unsigned int event)
{
unsigned int cpu = policy->cpu;
+ int rc = 0;
+
switch (event) {
case CPUFREQ_GOV_START:
if (!cpu_online(cpu))
return -EINVAL;
BUG_ON(!policy->cur);
mutex_lock(&userspace_mutex);
+ rc = sysfs_create_file (&policy->kobj,
+ &freq_attr_scaling_setspeed.attr);
+ if (rc)
+ goto start_out;
+
cpu_is_managed[cpu] = 1;
cpu_min_freq[cpu] = policy->min;
cpu_max_freq[cpu] = policy->max;
cpu_cur_freq[cpu] = policy->cur;
cpu_set_freq[cpu] = policy->cur;
- sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]);
+start_out:
mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_STOP:
@@ -180,7 +187,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
mutex_unlock(&userspace_mutex);
break;
}
- return 0;
+ return rc;
}
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 551f4cc..e749092 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -9,7 +9,8 @@
#include <linux/init.h>
#include <linux/cpufreq.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg)
+#define dprintk(msg...) \
+ cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg)
/*********************************************************************
* FREQUENCY TABLE HELPERS *
@@ -29,7 +30,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
continue;
}
- dprintk("table entry %u: %u kHz, %u index\n", i, freq, table[i].index);
+ dprintk("table entry %u: %u kHz, %u index\n",
+ i, freq, table[i].index);
if (freq < min_freq)
min_freq = freq;
if (freq > max_freq)
@@ -54,13 +56,14 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
unsigned int i;
unsigned int count = 0;
- dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu);
+ dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n",
+ policy->min, policy->max, policy->cpu);
if (!cpu_online(policy->cpu))
return -EINVAL;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
@@ -75,10 +78,11 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
if (!count)
policy->max = next_larger;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
- dprintk("verification lead to (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu);
+ dprintk("verification lead to (%u - %u kHz) for cpu %u\n",
+ policy->min, policy->max, policy->cpu);
return 0;
}
@@ -101,7 +105,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
};
unsigned int i;
- dprintk("request for target %u kHz (relation: %u) for cpu %u\n", target_freq, relation, policy->cpu);
+ dprintk("request for target %u kHz (relation: %u) for cpu %u\n",
+ target_freq, relation, policy->cpu);
switch (relation) {
case CPUFREQ_RELATION_H:
@@ -192,7 +197,10 @@ static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
}
struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
- .attr = { .name = "scaling_available_frequencies", .mode = 0444, .owner=THIS_MODULE },
+ .attr = { .name = "scaling_available_frequencies",
+ .mode = 0444,
+ .owner=THIS_MODULE
+ },
.show = show_available_freqs,
};
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e816535..879250d 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -53,7 +53,7 @@ config CRYPTO_DEV_PADLOCK_SHA
config CRYPTO_DEV_GEODE
tristate "Support for the Geode LX AES engine"
- depends on CRYPTO && X86_32
+ depends on CRYPTO && X86_32 && PCI
select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
default m
diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c
index ca4e67a..22b62b3 100644
--- a/drivers/fc4/fc.c
+++ b/drivers/fc4/fc.c
@@ -266,7 +266,7 @@ static void fcp_report_map_done(fc_channel *fc, int i, int status)
printk ("FC: Bad magic from REPORT_AL_MAP on %s - %08x\n", fc->name, p->magic);
fc->state = FC_STATE_OFFLINE;
} else {
- fc->posmap = (fcp_posmap *)kzalloc(sizeof(fcp_posmap)+p->len, GFP_KERNEL);
+ fc->posmap = kzalloc(sizeof(fcp_posmap)+p->len, GFP_KERNEL);
if (!fc->posmap) {
printk("FC: Not enough memory, offlining channel\n");
fc->state = FC_STATE_OFFLINE;
@@ -355,7 +355,7 @@ void fcp_register(fc_channel *fc, u8 type, int unregister)
for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
set_bit (i, fc->scsi_bitmap);
fc->scsi_free = fc->can_queue;
- fc->cmd_slots = (fcp_cmnd **)kzalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
+ fc->cmd_slots = kzalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
fc->abort_count = 0;
} else {
fc->scsi_name[0] = 0;
@@ -933,7 +933,7 @@ int fcp_scsi_dev_reset(struct scsi_cmnd *SCpnt)
DECLARE_MUTEX_LOCKED(sem);
if (!fc->rst_pkt) {
- fc->rst_pkt = (struct scsi_cmnd *) kmalloc(sizeof(SCpnt), GFP_KERNEL);
+ fc->rst_pkt = kmalloc(sizeof(SCpnt), GFP_KERNEL);
if (!fc->rst_pkt) return FAILED;
fcmd = FCP_CMND(fc->rst_pkt);
@@ -1107,7 +1107,7 @@ int fc_do_plogi(fc_channel *fc, unsigned char alpa, fc_wwn *node, fc_wwn *nport)
logi *l;
int status;
- l = (logi *)kzalloc(2 * sizeof(logi), GFP_KERNEL);
+ l = kzalloc(2 * sizeof(logi), GFP_KERNEL);
if (!l) return -ENOMEM;
l->code = LS_PLOGI;
memcpy (&l->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
@@ -1141,7 +1141,7 @@ int fc_do_prli(fc_channel *fc, unsigned char alpa)
prli *p;
int status;
- p = (prli *)kzalloc(2 * sizeof(prli), GFP_KERNEL);
+ p = kzalloc(2 * sizeof(prli), GFP_KERNEL);
if (!p) return -ENOMEM;
p->code = LS_PRLI;
p->params[0] = 0x08002000;
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index c034820..af02034 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -38,17 +38,6 @@ config I2C_ALGOPCA
This support is also available as a module. If so, the module
will be called i2c-algo-pca.
-config I2C_ALGOITE
- tristate "ITE I2C Algorithm"
- depends on MIPS_ITE8172 && I2C
- help
- This supports the use of the ITE8172 I2C interface found on some MIPS
- systems. Say Y if you have one of these. You should also say Y for
- the ITE I2C peripheral driver support below.
-
- This support is also available as a module. If so, the module
- will be called i2c-algo-ite.
-
config I2C_ALGO8XX
tristate "MPC8xx CPM I2C interface"
depends on 8xx && I2C
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index 208be04..cac1051 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -5,7 +5,6 @@
obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o
-obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o
obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o
ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 21c36bf..95aa539 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -540,15 +540,7 @@ int i2c_bit_add_bus(struct i2c_adapter *adap)
return i2c_add_adapter(adap);
}
-
-
-int i2c_bit_del_bus(struct i2c_adapter *adap)
-{
- return i2c_del_adapter(adap);
-}
-
EXPORT_SYMBOL(i2c_bit_add_bus);
-EXPORT_SYMBOL(i2c_bit_del_bus);
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm");
diff --git a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c
deleted file mode 100644
index 70d8eef..0000000
--- a/drivers/i2c/algos/i2c-algo-ite.c
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- -------------------------------------------------------------------------
- i2c-algo-ite.c i2c driver algorithms for ITE adapters
-
- Hai-Pao Fan, MontaVista Software, Inc.
- hpfan@mvista.com or source@mvista.com
-
- Copyright 2000 MontaVista Software Inc.
-
- ---------------------------------------------------------------------------
- This file was highly leveraged from i2c-algo-pcf.c, which was created
- by Simon G. Vogl and Hans Berglund:
-
-
- Copyright (C) 1995-1997 Simon G. Vogl
- 1998-2000 Hans Berglund
-
- 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. */
-/* ------------------------------------------------------------------------- */
-
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
- Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
- <mbailey@littlefeet-inc.com> */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <asm/uaccess.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-
-#include <linux/i2c.h>
-#include <linux/i2c-algo-ite.h>
-#include "i2c-algo-ite.h"
-
-#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR
-#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04
-#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR
-
-#define DEB2(x) if (i2c_debug>=2) x
-#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/
-#define DEF_TIMEOUT 16
-
-
-/* module parameters:
- */
-static int i2c_debug;
-static int iic_test; /* see if the line-setting functions work */
-
-/* --- setting states on the bus with the right timing: --------------- */
-
-#define get_clock(adap) adap->getclock(adap->data)
-#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val)
-#define iic_inw(adap, reg) adap->getiic(adap->data, reg)
-
-
-/* --- other auxiliary functions -------------------------------------- */
-
-static void iic_start(struct i2c_algo_iic_data *adap)
-{
- iic_outw(adap,ITE_I2CHCR,ITE_CMD);
-}
-
-static void iic_stop(struct i2c_algo_iic_data *adap)
-{
- iic_outw(adap,ITE_I2CHCR,0);
- iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI);
-}
-
-static void iic_reset(struct i2c_algo_iic_data *adap)
-{
- iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80);
-}
-
-
-static int wait_for_bb(struct i2c_algo_iic_data *adap)
-{
- int timeout = DEF_TIMEOUT;
- short status;
-
- status = iic_inw(adap, ITE_I2CHSR);
-#ifndef STUB_I2C
- while (timeout-- && (status & ITE_I2CHSR_HB)) {
- udelay(1000); /* How much is this? */
- status = iic_inw(adap, ITE_I2CHSR);
- }
-#endif
- if (timeout<=0) {
- printk(KERN_ERR "Timeout, host is busy\n");
- iic_reset(adap);
- }
- return(timeout<=0);
-}
-
-/* After we issue a transaction on the IIC bus, this function
- * is called. It puts this process to sleep until we get an interrupt from
- * from the controller telling us that the transaction we requested in complete.
- */
-static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) {
-
- int timeout = DEF_TIMEOUT;
-
- timeout = wait_for_bb(adap);
- if (timeout) {
- DEB2(printk("Timeout waiting for host not busy\n");)
- return -EIO;
- }
- timeout = DEF_TIMEOUT;
-
- *status = iic_inw(adap, ITE_I2CHSR);
-#ifndef STUB_I2C
- while (timeout-- && !(*status & ITE_I2CHSR_TDI)) {
- adap->waitforpin();
- *status = iic_inw(adap, ITE_I2CHSR);
- }
-#endif
- if (timeout <= 0)
- return(-1);
- else
- return(0);
-}
-
-static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status)
-{
- int timeout = DEF_TIMEOUT;
-
- *status = iic_inw(adap, ITE_I2CFSR);
-#ifndef STUB_I2C
- while (timeout-- && (*status & ITE_I2CFSR_FE)) {
- udelay(1000);
- iic_inw(adap, ITE_I2CFSR);
- }
-#endif
- if (timeout <= 0)
- return(-1);
- else
- return(0);
-}
-
-static int iic_init (struct i2c_algo_iic_data *adap)
-{
- short i;
-
- /* Clear bit 7 to set I2C to normal operation mode */
- i=iic_inw(adap, PM_DSR)& 0xff7f;
- iic_outw(adap, PM_DSR, i);
-
- /* set IT_GPCCR port C bit 2&3 as function 2 */
- i = iic_inw(adap, GPIO_CCR) & 0xfc0f;
- iic_outw(adap,GPIO_CCR,i);
-
- /* Clear slave address/sub-address */
- iic_outw(adap,ITE_I2CSAR, 0);
- iic_outw(adap,ITE_I2CSSAR, 0);
-
- /* Set clock counter register */
- iic_outw(adap,ITE_I2CCKCNT, get_clock(adap));
-
- /* Set START/reSTART/STOP time registers */
- iic_outw(adap,ITE_I2CSHDR, 0x0a);
- iic_outw(adap,ITE_I2CRSUR, 0x0a);
- iic_outw(adap,ITE_I2CPSUR, 0x0a);
-
- /* Enable interrupts on completing the current transaction */
- iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE);
-
- /* Clear transfer count */
- iic_outw(adap,ITE_I2CFBCR, 0x0);
-
- DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n",
- iic_inw(adap, ITE_I2CHSR)));
- return 0;
-}
-
-
-/*
- * Sanity check for the adapter hardware - check the reaction of
- * the bus lines only if it seems to be idle.
- */
-static int test_bus(struct i2c_algo_iic_data *adap, char *name) {
-#if 0
- int scl,sda;
- sda=getsda(adap);
- if (adap->getscl==NULL) {
- printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n");
- return 0;
- }
- scl=getscl(adap);
- printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n",
- name,getscl(adap),getsda(adap));
- if (!scl || !sda ) {
- printk("test_bus: %s seems to be busy.\n",adap->name);
- goto bailout;
- }
- sdalo(adap);
- printk("test_bus:1 scl: %d sda: %d\n", getscl(adap),
- getsda(adap));
- if ( 0 != getsda(adap) ) {
- printk("test_bus: %s SDA stuck high!\n",name);
- sdahi(adap);
- goto bailout;
- }
- if ( 0 == getscl(adap) ) {
- printk("test_bus: %s SCL unexpected low while pulling SDA low!\n",
- name);
- goto bailout;
- }
- sdahi(adap);
- printk("test_bus:2 scl: %d sda: %d\n", getscl(adap),
- getsda(adap));
- if ( 0 == getsda(adap) ) {
- printk("test_bus: %s SDA stuck low!\n",name);
- sdahi(adap);
- goto bailout;
- }
- if ( 0 == getscl(adap) ) {
- printk("test_bus: %s SCL unexpected low while SDA high!\n",
- adap->name);
- goto bailout;
- }
- scllo(adap);
- printk("test_bus:3 scl: %d sda: %d\n", getscl(adap),
- getsda(adap));
- if ( 0 != getscl(adap) ) {
-
- sclhi(adap);
- goto bailout;
- }
- if ( 0 == getsda(adap) ) {
- printk("test_bus: %s SDA unexpected low while pulling SCL low!\n",
- name);
- goto bailout;
- }
- sclhi(adap);
- printk("test_bus:4 scl: %d sda: %d\n", getscl(adap),
- getsda(adap));
- if ( 0 == getscl(adap) ) {
- printk("test_bus: %s SCL stuck low!\n",name);
- sclhi(adap);
- goto bailout;
- }
- if ( 0 == getsda(adap) ) {
- printk("test_bus: %s SDA unexpected low while SCL high!\n",
- name);
- goto bailout;
- }
- printk("test_bus: %s passed test.\n",name);
- return 0;
-bailout:
- sdahi(adap);
- sclhi(adap);
- return -ENODEV;
-#endif
- return (0);
-}
-
-/* ----- Utility functions
- */
-
-
-/* Verify the device we want to talk to on the IIC bus really exists. */
-static inline int try_address(struct i2c_algo_iic_data *adap,
- unsigned int addr, int retries)
-{
- int i, ret = -1;
- short status;
-
- for (i=0;i<retries;i++) {
- iic_outw(adap, ITE_I2CSAR, addr);
- iic_start(adap);
- if (wait_for_pin(adap, &status) == 0) {
- if ((status & ITE_I2CHSR_DNE) == 0) {
- iic_stop(adap);
- iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
- ret=1;
- break; /* success! */
- }
- }
- iic_stop(adap);
- udelay(adap->udelay);
- }
- DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i,
- addr));
- return ret;
-}
-
-
-static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf,
- int count)
-{
- struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
- int wrcount=0, timeout;
- short status;
- int loops, remainder, i, j;
- union {
- char byte[2];
- unsigned short word;
- } tmp;
-
- iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]);
- count--;
- if (count == 0)
- return -EIO;
-
- loops = count / 32; /* 32-byte FIFO */
- remainder = count % 32;
-
- if(loops) {
- for(i=0; i<loops; i++) {
-
- iic_outw(adap, ITE_I2CFBCR, 32);
- for(j=0; j<32/2; j++) {
- tmp.byte[1] = buf[wrcount++];
- tmp.byte[0] = buf[wrcount++];
- iic_outw(adap, ITE_I2CFDR, tmp.word);
- }
-
- /* status FIFO overrun */
- iic_inw(adap, ITE_I2CFSR);
- iic_inw(adap, ITE_I2CFBCR);
-
- iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
-
- /* Wait for transmission to complete */
- timeout = wait_for_pin(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
- return -EREMOTEIO; /* got a better one ?? */
- }
- if (status & ITE_I2CHSR_DB) {
- iic_stop(adap);
- printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
- return -EREMOTEIO; /* got a better one ?? */
- }
- }
- }
- if(remainder) {
- iic_outw(adap, ITE_I2CFBCR, remainder);
- for(i=0; i<remainder/2; i++) {
- tmp.byte[1] = buf[wrcount++];
- tmp.byte[0] = buf[wrcount++];
- iic_outw(adap, ITE_I2CFDR, tmp.word);
- }
-
- /* status FIFO overrun */
- iic_inw(adap, ITE_I2CFSR);
- iic_inw(adap, ITE_I2CFBCR);
-
- iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
-
- timeout = wait_for_pin(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
- return -EREMOTEIO; /* got a better one ?? */
- }
-#ifndef STUB_I2C
- if (status & ITE_I2CHSR_DB) {
- iic_stop(adap);
- printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
- return -EREMOTEIO; /* got a better one ?? */
- }
-#endif
- }
- iic_stop(adap);
- return wrcount;
-}
-
-
-static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count,
- int sread)
-{
- int rdcount=0, i, timeout;
- short status;
- struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
- int loops, remainder, j;
- union {
- char byte[2];
- unsigned short word;
- } tmp;
-
- loops = count / 32; /* 32-byte FIFO */
- remainder = count % 32;
-
- if(loops) {
- for(i=0; i<loops; i++) {
- iic_outw(adap, ITE_I2CFBCR, 32);
- if (sread)
- iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
- else
- iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
-
- timeout = wait_for_pin(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
- return (-1);
- }
-#ifndef STUB_I2C
- if (status & ITE_I2CHSR_DB) {
- iic_stop(adap);
- printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
- return (-1);
- }
-#endif
-
- timeout = wait_for_fe(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
- return (-1);
- }
-
- for(j=0; j<32/2; j++) {
- tmp.word = iic_inw(adap, ITE_I2CFDR);
- buf[rdcount++] = tmp.byte[1];
- buf[rdcount++] = tmp.byte[0];
- }
-
- /* status FIFO underrun */
- iic_inw(adap, ITE_I2CFSR);
-
- }
- }
-
-
- if(remainder) {
- remainder=(remainder+1)/2 * 2;
- iic_outw(adap, ITE_I2CFBCR, remainder);
- if (sread)
- iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
- else
- iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
-
- timeout = wait_for_pin(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
- return (-1);
- }
-#ifndef STUB_I2C
- if (status & ITE_I2CHSR_DB) {
- iic_stop(adap);
- printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
- return (-1);
- }
-#endif
- timeout = wait_for_fe(adap, &status);
- if(timeout) {
- iic_stop(adap);
- printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
- return (-1);
- }
-
- for(i=0; i<(remainder+1)/2; i++) {
- tmp.word = iic_inw(adap, ITE_I2CFDR);
- buf[rdcount++] = tmp.byte[1];
- buf[rdcount++] = tmp.byte[0];
- }
-
- /* status FIFO underrun */
- iic_inw(adap, ITE_I2CFSR);
-
- }
-
- iic_stop(adap);
- return rdcount;
-}
-
-
-/* This function implements combined transactions. Combined
- * transactions consist of combinations of reading and writing blocks of data.
- * Each transfer (i.e. a read or a write) is separated by a repeated start
- * condition.
- */
-#if 0
-static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
-{
- int i;
- struct i2c_msg *pmsg;
- int ret;
-
- DEB2(printk("Beginning combined transaction\n"));
-
- for(i=0; i<(num-1); i++) {
- pmsg = &msgs[i];
- if(pmsg->flags & I2C_M_RD) {
- DEB2(printk(" This one is a read\n"));
- ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
- }
- else if(!(pmsg->flags & I2C_M_RD)) {
- DEB2(printk("This one is a write\n"));
- ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
- }
- }
- /* Last read or write segment needs to be terminated with a stop */
- pmsg = &msgs[i];
-
- if(pmsg->flags & I2C_M_RD) {
- DEB2(printk("Doing the last read\n"));
- ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
- }
- else if(!(pmsg->flags & I2C_M_RD)) {
- DEB2(printk("Doing the last write\n"));
- ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
- }
-
- return ret;
-}
-#endif
-
-
-/* Whenever we initiate a transaction, the first byte clocked
- * onto the bus after the start condition is the address (7 bit) of the
- * device we want to talk to. This function manipulates the address specified
- * so that it makes sense to the hardware when written to the IIC peripheral.
- *
- * Note: 10 bit addresses are not supported in this driver, although they are
- * supported by the hardware. This functionality needs to be implemented.
- */
-static inline int iic_doAddress(struct i2c_algo_iic_data *adap,
- struct i2c_msg *msg, int retries)
-{
- unsigned short flags = msg->flags;
- unsigned int addr;
- int ret;
-
-/* Ten bit addresses not supported right now */
- if ( (flags & I2C_M_TEN) ) {
-#if 0
- addr = 0xf0 | (( msg->addr >> 7) & 0x03);
- DEB2(printk("addr0: %d\n",addr));
- ret = try_address(adap, addr, retries);
- if (ret!=1) {
- printk("iic_doAddress: died at extended address code.\n");
- return -EREMOTEIO;
- }
- iic_outw(adap,msg->addr & 0x7f);
- if (ret != 1) {
- printk("iic_doAddress: died at 2nd address code.\n");
- return -EREMOTEIO;
- }
- if ( flags & I2C_M_RD ) {
- i2c_repstart(adap);
- addr |= 0x01;
- ret = try_address(adap, addr, retries);
- if (ret!=1) {
- printk("iic_doAddress: died at extended address code.\n");
- return -EREMOTEIO;
- }
- }
-#endif
- } else {
-
- addr = ( msg->addr << 1 );
-
-#if 0
- if (flags & I2C_M_RD )
- addr |= 1;
- if (flags & I2C_M_REV_DIR_ADDR )
- addr ^= 1;
-#endif
-
- if (iic_inw(adap, ITE_I2CSAR) != addr) {
- iic_outw(adap, ITE_I2CSAR, addr);
- ret = try_address(adap, addr, retries);
- if (ret!=1) {
- printk("iic_doAddress: died at address code.\n");
- return -EREMOTEIO;
- }
- }
-
- }
-
- return 0;
-}
-
-
-/* Description: Prepares the controller for a transaction (clearing status
- * registers, data buffers, etc), and then calls either iic_readbytes or
- * iic_sendbytes to do the actual transaction.
- *
- * still to be done: Before we issue a transaction, we should
- * verify that the bus is not busy or in some unknown state.
- */
-static int iic_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs,
- int num)
-{
- struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
- struct i2c_msg *pmsg;
- int i = 0;
- int ret, timeout;
-
- pmsg = &msgs[i];
-
- if(!pmsg->len) {
- DEB2(printk("iic_xfer: read/write length is 0\n");)
- return -EIO;
- }
- if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) {
- DEB2(printk("iic_xfer: write buffer length is not odd\n");)
- return -EIO;
- }
-
- /* Wait for any pending transfers to complete */
- timeout = wait_for_bb(adap);
- if (timeout) {
- DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");)
- return -EIO;
- }
-
- /* Flush FIFO */
- iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
-
- /* Load address */
- ret = iic_doAddress(adap, pmsg, i2c_adap->retries);
- if (ret)
- return -EIO;
-
-#if 0
- /* Combined transaction (read and write) */
- if(num > 1) {
- DEB2(printk("iic_xfer: Call combined transaction\n"));
- ret = iic_combined_transaction(i2c_adap, msgs, num);
- }
-#endif
-
- DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
- i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
-
- if(pmsg->flags & I2C_M_RD) /* Read */
- ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0);
- else { /* Write */
- udelay(1000);
- ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len);
- }
-
- if (ret != pmsg->len)
- DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret));
- else
- DEB3(printk("iic_xfer: read/write %d bytes.\n",ret));
-
- return ret;
-}
-
-
-/* Implements device specific ioctls. Higher level ioctls can
- * be found in i2c-core.c and are typical of any i2c controller (specifying
- * slave address, timeouts, etc). These ioctls take advantage of any hardware
- * features built into the controller for which this algorithm-adapter set
- * was written. These ioctls allow you to take control of the data and clock
- * lines and set the either high or low,
- * similar to a GPIO pin.
- */
-static int algo_control(struct i2c_adapter *adapter,
- unsigned int cmd, unsigned long arg)
-{
-
- struct i2c_algo_iic_data *adap = adapter->algo_data;
- struct i2c_iic_msg s_msg;
- char *buf;
- int ret;
-
- if (cmd == I2C_SREAD) {
- if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg,
- sizeof(struct i2c_iic_msg)))
- return -EFAULT;
- buf = kmalloc(s_msg.len, GFP_KERNEL);
- if (buf== NULL)
- return -ENOMEM;
-
- /* Flush FIFO */
- iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
-
- /* Load address */
- iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1);
- iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff);
-
- ret = iic_readbytes(adapter, buf, s_msg.len, 1);
- if (ret>=0) {
- if(copy_to_user( s_msg.buf, buf, s_msg.len) )
- ret = -EFAULT;
- }
- kfree(buf);
- }
- return 0;
-}
-
-
-static u32 iic_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_PROTOCOL_MANGLING;
-}
-
-/* -----exported algorithm data: ------------------------------------- */
-
-static struct i2c_algorithm iic_algo = {
- .master_xfer = iic_xfer,
- .algo_control = algo_control, /* ioctl */
- .functionality = iic_func,
-};
-
-
-/*
- * registering functions to load algorithms at runtime
- */
-int i2c_iic_add_bus(struct i2c_adapter *adap)
-{
- struct i2c_algo_iic_data *iic_adap = adap->algo_data;
-
- if (iic_test) {
- int ret = test_bus(iic_adap, adap->name);
- if (ret<0)
- return -ENODEV;
- }
-
- DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n",
- adap->name));
-
- /* register new adapter to i2c module... */
- adap->algo = &iic_algo;
-
- adap->timeout = 100; /* default values, should */
- adap->retries = 3; /* be replaced by defines */
- adap->flags = 0;
-
- iic_init(iic_adap);
- return i2c_add_adapter(adap);
-}
-
-
-int i2c_iic_del_bus(struct i2c_adapter *adap)
-{
- int res;
- if ((res = i2c_del_adapter(adap)) < 0)
- return res;
- DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name));
-
- return 0;
-}
-
-
-int __init i2c_algo_iic_init (void)
-{
- printk(KERN_INFO "ITE iic (i2c) algorithm module\n");
- return 0;
-}
-
-
-void i2c_algo_iic_exit(void)
-{
- return;
-}
-
-
-EXPORT_SYMBOL(i2c_iic_add_bus);
-EXPORT_SYMBOL(i2c_iic_del_bus);
-
-/* The MODULE_* macros resolve to nothing if MODULES is not defined
- * when this file is compiled.
- */
-MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
-MODULE_DESCRIPTION("ITE iic algorithm");
-MODULE_LICENSE("GPL");
-
-module_param(iic_test, bool, 0);
-module_param(i2c_debug, int, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available");
-MODULE_PARM_DESC(i2c_debug,
- "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol");
-
-
-/* This function resolves to init_module (the function invoked when a module
- * is loaded via insmod) when this file is compiled with MODULES defined.
- * Otherwise (i.e. if you want this driver statically linked to the kernel),
- * a pointer to this function is stored in a table and called
- * during the initialization of the kernel (in do_basic_setup in /init/main.c)
- *
- * All this functionality is complements of the macros defined in linux/init.h
- */
-module_init(i2c_algo_iic_init);
-
-
-/* If MODULES is defined when this file is compiled, then this function will
- * resolved to cleanup_module.
- */
-module_exit(i2c_algo_iic_exit);
diff --git a/drivers/i2c/algos/i2c-algo-ite.h b/drivers/i2c/algos/i2c-algo-ite.h
deleted file mode 100644
index a8ca3c9..0000000
--- a/drivers/i2c/algos/i2c-algo-ite.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- --------------------------------------------------------------------
- i2c-ite.h: Global defines for the I2C controller on board the
- ITE MIPS processor.
- --------------------------------------------------------------------
- Hai-Pao Fan, MontaVista Software, Inc.
- hpfan@mvista.com or source@mvista.com
-
- Copyright 2001 MontaVista Software 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * 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 I2C_ITE_H
-#define I2C_ITE_H 1
-
-#include <asm/it8172/it8172.h>
-
-/* I2C Registers */
-#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30
-#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34
-#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38
-#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c
-#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48
-#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c
-#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50
-#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54
-
-#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70
-#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74
-#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78
-#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c
-
-
-/* Host Control Register ITE_I2CHCR */
-#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */
-#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing
- the current transaction */
-#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */
-#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */
-#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */
-#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute
- the command and send the data programmed in
- all required registers to I2C bus */
-#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST
-#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W
-#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R
-#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S
-
-/* Host Status Register ITE_I2CHSR */
-#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except
- in the first and last bytes */
-#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */
-#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */
-#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */
-#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */
-
-/* Slave Address Register ITE_I2CSAR */
-#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */
-#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the
- next slave address is addressed */
-
-/* Slave Sub-address Register ITE_I2CSSAR */
-#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */
-
-/* Clock Counter Register ITE_I2CCKCNT */
-#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */
-#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */
-#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */
-
-/* START Hold Time Register ITE_I2CSHDR */
-/* value is counted based on 16 MHz internal clock */
-#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */
-#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */
-
-/* (Repeated) START Setup Time Register ITE_I2CRSUR */
-/* value is counted based on 16 MHz internal clock */
-#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */
-#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */
-
-/* STOP setup Time Register ITE_I2CPSUR */
-
-/* FIFO Data Register ITE_I2CFDR */
-#define ITE_I2CFDR_MASK 0xff
-
-/* FIFO Byte Count Register ITE_I2CFBCR */
-#define ITE_I2CFBCR_MASK 0x3f
-
-/* FIFO Control Register ITE_I2CFCR */
-#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point
- and I2CFSR */
-/* FIFO Status Register ITE_I2CFSR */
-#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */
-#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */
-#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */
-#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */
-
-#endif /* I2C_ITE_H */
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 9081c9f..36fdf97 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -381,14 +381,7 @@ int i2c_pca_add_bus(struct i2c_adapter *adap)
return rval;
}
-
-int i2c_pca_del_bus(struct i2c_adapter *adap)
-{
- return i2c_del_adapter(adap);
-}
-
EXPORT_SYMBOL(i2c_pca_add_bus);
-EXPORT_SYMBOL(i2c_pca_del_bus);
MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm");
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 3b20033..ecb2c2d 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -486,15 +486,7 @@ int i2c_pcf_add_bus(struct i2c_adapter *adap)
return rval;
}
-
-
-int i2c_pcf_del_bus(struct i2c_adapter *adap)
-{
- return i2c_del_adapter(adap);
-}
-
EXPORT_SYMBOL(i2c_pcf_add_bus);
-EXPORT_SYMBOL(i2c_pcf_del_bus);
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm");
diff --git a/drivers/i2c/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c
index 490d999..ac2d505 100644
--- a/drivers/i2c/algos/i2c-algo-sgi.c
+++ b/drivers/i2c/algos/i2c-algo-sgi.c
@@ -171,15 +171,7 @@ int i2c_sgi_add_bus(struct i2c_adapter *adap)
return i2c_add_adapter(adap);
}
-
-
-int i2c_sgi_del_bus(struct i2c_adapter *adap)
-{
- return i2c_del_adapter(adap);
-}
-
EXPORT_SYMBOL(i2c_sgi_add_bus);
-EXPORT_SYMBOL(i2c_sgi_del_bus);
MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
MODULE_DESCRIPTION("I2C-Bus SGI algorithm");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 90f91d03..e1989f3 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -74,6 +74,13 @@ config I2C_AMD8111
This driver can also be built as a module. If so, the module
will be called i2c-amd8111.
+config I2C_AT91
+ tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
+ depends on I2C && ARCH_AT91 && EXPERIMENTAL
+ help
+ This supports the use of the I2C interface on Atmel AT91
+ processors.
+
config I2C_AU1550
tristate "Au1550/Au1200 SMBus interface"
depends on I2C && (SOC_AU1550 || SOC_AU1200)
@@ -209,18 +216,6 @@ config I2C_ISA
tristate
depends on I2C
-config I2C_ITE
- tristate "ITE I2C Adapter"
- depends on I2C && MIPS_ITE8172
- select I2C_ALGOITE
- help
- This supports the ITE8172 I2C peripheral found on some MIPS
- systems. Say Y if you have one of these. You should also say Y for
- the ITE I2C driver algorithm support above.
-
- This support is also available as a module. If so, the module
- will be called i2c-ite.
-
config I2C_IXP4XX
tristate "IXP4xx GPIO-Based I2C Interface"
depends on I2C && ARCH_IXP4XX
@@ -481,6 +476,17 @@ config I2C_STUB
If you don't know what to do here, definitely say N.
+config I2C_VERSATILE
+ tristate "ARM Versatile/Realview I2C bus support"
+ depends on I2C && (ARCH_VERSATILE || ARCH_REALVIEW)
+ select I2C_ALGOBIT
+ help
+ Say yes if you want to support the I2C serial bus on ARMs Versatile
+ range of platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-versatile.
+
config I2C_VIA
tristate "VIA 82C586B"
depends on I2C && PCI && EXPERIMENTAL
@@ -548,4 +554,23 @@ config I2C_MV64XXX
This driver can also be built as a module. If so, the module
will be called i2c-mv64xxx.
+config I2C_PNX
+ tristate "I2C bus support for Philips PNX targets"
+ depends on ARCH_PNX4008 && I2C
+ help
+ This driver supports the Philips IP3204 I2C IP block master and/or
+ slave controller
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-pnx.
+
+config I2C_PNX_EARLY
+ bool "Early initialization for I2C on PNXxxxx"
+ depends on I2C_PNX=y
+ help
+ Under certain circumstances one may need to make sure I2C on PNXxxxx
+ is initialized earlier than some other driver that depends on it
+ (for instance, that might be USB in case of PNX4008). With this
+ option turned on you can guarantee that.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 493c872..37196c1 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o
obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
+obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
@@ -16,7 +17,6 @@ obj-$(CONFIG_I2C_I810) += i2c-i810.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_ISA) += i2c-isa.o
-obj-$(CONFIG_I2C_ITE) += i2c-ite.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
@@ -29,6 +29,7 @@ obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
+obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
+obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_VIA) += i2c-via.o
obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
new file mode 100644
index 0000000..67f91bd
--- /dev/null
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -0,0 +1,325 @@
+/*
+ i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+
+ Copyright (C) 2004 Rick Bronson
+ Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+
+ Borrowed heavily from original work by:
+ Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/at91_twi.h>
+#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
+
+#define TWI_CLOCK 100000 /* Hz. max 400 Kbits/sec */
+
+
+static struct clk *twi_clk;
+static void __iomem *twi_base;
+
+#define at91_twi_read(reg) __raw_readl(twi_base + (reg))
+#define at91_twi_write(reg, val) __raw_writel((val), twi_base + (reg))
+
+
+/*
+ * Initialize the TWI hardware registers.
+ */
+static void __devinit at91_twi_hwinit(void)
+{
+ unsigned long cdiv, ckdiv;
+
+ at91_twi_write(AT91_TWI_IDR, 0xffffffff); /* Disable all interrupts */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_SWRST); /* Reset peripheral */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); /* Set Master mode */
+
+ /* Calcuate clock dividers */
+ cdiv = (clk_get_rate(twi_clk) / (2 * TWI_CLOCK)) - 3;
+ cdiv = cdiv + 1; /* round up */
+ ckdiv = 0;
+ while (cdiv > 255) {
+ ckdiv++;
+ cdiv = cdiv >> 1;
+ }
+
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 Errata #22 */
+ if (ckdiv > 5) {
+ printk(KERN_ERR "AT91 I2C: Invalid TWI_CLOCK value!\n");
+ ckdiv = 5;
+ }
+ }
+
+ at91_twi_write(AT91_TWI_CWGR, (ckdiv << 16) | (cdiv << 8) | cdiv);
+}
+
+/*
+ * Poll the i2c status register until the specified bit is set.
+ * Returns 0 if timed out (100 msec).
+ */
+static short at91_poll_status(unsigned long bit)
+{
+ int loop_cntr = 10000;
+
+ do {
+ udelay(10);
+ } while (!(at91_twi_read(AT91_TWI_SR) & bit) && (--loop_cntr > 0));
+
+ return (loop_cntr > 0);
+}
+
+static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
+{
+ /* Send Start */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_START);
+
+ /* Read data */
+ while (length--) {
+ if (!length) /* need to send Stop before reading last byte */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP);
+ if (!at91_poll_status(AT91_TWI_RXRDY)) {
+ dev_dbg(&adap->dev, "RXRDY timeout\n");
+ return -ETIMEDOUT;
+ }
+ *buf++ = (at91_twi_read(AT91_TWI_RHR) & 0xff);
+ }
+
+ return 0;
+}
+
+static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
+{
+ /* Load first byte into transmitter */
+ at91_twi_write(AT91_TWI_THR, *buf++);
+
+ /* Send Start */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_START);
+
+ do {
+ if (!at91_poll_status(AT91_TWI_TXRDY)) {
+ dev_dbg(&adap->dev, "TXRDY timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ length--; /* byte was transmitted */
+
+ if (length > 0) /* more data to send? */
+ at91_twi_write(AT91_TWI_THR, *buf++);
+ } while (length);
+
+ /* Send Stop */
+ at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP);
+
+ return 0;
+}
+
+/*
+ * Generic i2c master transfer entrypoint.
+ *
+ * Note: We do not use Atmel's feature of storing the "internal device address".
+ * Instead the "internal device address" has to be written using a seperate
+ * i2c message.
+ * http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
+ */
+static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num)
+{
+ int i, ret;
+
+ dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
+
+ for (i = 0; i < num; i++) {
+ dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
+ pmsg->flags & I2C_M_RD ? "read" : "writ",
+ pmsg->len, pmsg->len > 1 ? "s" : "",
+ pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr);
+
+ at91_twi_write(AT91_TWI_MMR, (pmsg->addr << 16)
+ | ((pmsg->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0));
+
+ if (pmsg->len && pmsg->buf) { /* sanity check */
+ if (pmsg->flags & I2C_M_RD)
+ ret = xfer_read(adap, pmsg->buf, pmsg->len);
+ else
+ ret = xfer_write(adap, pmsg->buf, pmsg->len);
+
+ if (ret)
+ return ret;
+
+ /* Wait until transfer is finished */
+ if (!at91_poll_status(AT91_TWI_TXCOMP)) {
+ dev_dbg(&adap->dev, "TXCOMP timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+ dev_dbg(&adap->dev, "transfer complete\n");
+ pmsg++; /* next message */
+ }
+ return i;
+}
+
+/*
+ * Return list of supported functionality.
+ */
+static u32 at91_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm at91_algorithm = {
+ .master_xfer = at91_xfer,
+ .functionality = at91_func,
+};
+
+/*
+ * Main initialization routine.
+ */
+static int __devinit at91_i2c_probe(struct platform_device *pdev)
+{
+ struct i2c_adapter *adapter;
+ struct resource *res;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c"))
+ return -EBUSY;
+
+ twi_base = ioremap(res->start, res->end - res->start + 1);
+ if (!twi_base) {
+ rc = -ENOMEM;
+ goto fail0;
+ }
+
+ twi_clk = clk_get(NULL, "twi_clk");
+ if (IS_ERR(twi_clk)) {
+ dev_err(&pdev->dev, "no clock defined\n");
+ rc = -ENODEV;
+ goto fail1;
+ }
+
+ adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (adapter == NULL) {
+ dev_err(&pdev->dev, "can't allocate inteface!\n");
+ rc = -ENOMEM;
+ goto fail2;
+ }
+ sprintf(adapter->name, "AT91");
+ adapter->algo = &at91_algorithm;
+ adapter->class = I2C_CLASS_HWMON;
+ adapter->dev.parent = &pdev->dev;
+
+ platform_set_drvdata(pdev, adapter);
+
+ clk_enable(twi_clk); /* enable peripheral clock */
+ at91_twi_hwinit(); /* initialize TWI controller */
+
+ rc = i2c_add_adapter(adapter);
+ if (rc) {
+ dev_err(&pdev->dev, "Adapter %s registration failed\n",
+ adapter->name);
+ goto fail3;
+ }
+
+ dev_info(&pdev->dev, "AT91 i2c bus driver.\n");
+ return 0;
+
+fail3:
+ platform_set_drvdata(pdev, NULL);
+ kfree(adapter);
+ clk_disable(twi_clk);
+fail2:
+ clk_put(twi_clk);
+fail1:
+ iounmap(twi_base);
+fail0:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ return rc;
+}
+
+static int __devexit at91_i2c_remove(struct platform_device *pdev)
+{
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct resource *res;
+ int rc;
+
+ rc = i2c_del_adapter(adapter);
+ platform_set_drvdata(pdev, NULL);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap(twi_base);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ clk_disable(twi_clk); /* disable peripheral clock */
+ clk_put(twi_clk);
+
+ return rc;
+}
+
+#ifdef CONFIG_PM
+
+/* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */
+
+static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ clk_disable(twi_clk);
+ return 0;
+}
+
+static int at91_i2c_resume(struct platform_device *pdev)
+{
+ return clk_enable(twi_clk);
+}
+
+#else
+#define at91_i2c_suspend NULL
+#define at91_i2c_resume NULL
+#endif
+
+static struct platform_driver at91_i2c_driver = {
+ .probe = at91_i2c_probe,
+ .remove = __devexit_p(at91_i2c_remove),
+ .suspend = at91_i2c_suspend,
+ .resume = at91_i2c_resume,
+ .driver = {
+ .name = "at91_i2c",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_i2c_init(void)
+{
+ return platform_driver_register(&at91_i2c_driver);
+}
+
+static void __exit at91_i2c_exit(void)
+{
+ platform_driver_unregister(&at91_i2c_driver);
+}
+
+module_init(at91_i2c_init);
+module_exit(at91_i2c_exit);
+
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index a591fe6..8349674 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -293,7 +293,7 @@ static int __init i2c_pcfisa_init(void)
static void i2c_pcfisa_exit(void)
{
- i2c_pcf_del_bus(&pcf_isa_ops);
+ i2c_del_adapter(&pcf_isa_ops);
if (irq > 0) {
disable_irq(irq);
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index 457d48a..9832f77 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -146,7 +146,7 @@ static int __devinit hydra_probe(struct pci_dev *dev,
static void __devexit hydra_remove(struct pci_dev *dev)
{
pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */
- i2c_bit_del_bus(&hydra_adap);
+ i2c_del_adapter(&hydra_adap);
iounmap(hydra_bit_data.data);
release_mem_region(pci_resource_start(dev, 0)+
offsetof(struct Hydra, CachePD), 4);
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index c7be2fd..ae625b8 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -470,12 +470,20 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
int err;
I801_dev = dev;
- if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) ||
- (dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) ||
- (dev->device == PCI_DEVICE_ID_INTEL_ESB_4))
+ switch (dev->device) {
+ case PCI_DEVICE_ID_INTEL_82801DB_3:
+ case PCI_DEVICE_ID_INTEL_82801EB_3:
+ case PCI_DEVICE_ID_INTEL_ESB_4:
+ case PCI_DEVICE_ID_INTEL_ICH6_16:
+ case PCI_DEVICE_ID_INTEL_ICH7_17:
+ case PCI_DEVICE_ID_INTEL_ESB2_17:
+ case PCI_DEVICE_ID_INTEL_ICH8_5:
+ case PCI_DEVICE_ID_INTEL_ICH9_6:
isich4 = 1;
- else
+ break;
+ default:
isich4 = 0;
+ }
err = pci_enable_device(dev);
if (err) {
diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c
index b66fb6b..10c98bc 100644
--- a/drivers/i2c/busses/i2c-i810.c
+++ b/drivers/i2c/busses/i2c-i810.c
@@ -219,14 +219,14 @@ static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id
return retval;
retval = i2c_bit_add_bus(&i810_ddc_adapter);
if (retval)
- i2c_bit_del_bus(&i810_i2c_adapter);
+ i2c_del_adapter(&i810_i2c_adapter);
return retval;
}
static void __devexit i810_remove(struct pci_dev *dev)
{
- i2c_bit_del_bus(&i810_ddc_adapter);
- i2c_bit_del_bus(&i810_i2c_adapter);
+ i2c_del_adapter(&i810_ddc_adapter);
+ i2c_del_adapter(&i810_i2c_adapter);
iounmap(ioaddr);
}
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 781a99c..1898e99 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -680,6 +680,12 @@ static int __devinit iic_probe(struct ocp_device *ocp){
dev->idx = ocp->def->index;
ocp_set_drvdata(ocp, dev);
+ if (!request_mem_region(ocp->def->paddr, sizeof(struct iic_regs),
+ "ibm_iic")) {
+ ret = -EBUSY;
+ goto fail1;
+ }
+
if (!(dev->vaddr = ioremap(ocp->def->paddr, sizeof(struct iic_regs)))){
printk(KERN_CRIT "ibm-iic%d: failed to ioremap device registers\n",
dev->idx);
@@ -750,6 +756,8 @@ fail:
iounmap(dev->vaddr);
fail2:
+ release_mem_region(ocp->def->paddr, sizeof(struct iic_regs));
+fail1:
ocp_set_drvdata(ocp, NULL);
kfree(dev);
return ret;
@@ -777,6 +785,7 @@ static void __devexit iic_remove(struct ocp_device *ocp)
free_irq(dev->irq, dev);
}
iounmap(dev->vaddr);
+ release_mem_region(ocp->def->paddr, sizeof(struct iic_regs));
kfree(dev);
}
}
diff --git a/drivers/i2c/busses/i2c-ite.c b/drivers/i2c/busses/i2c-ite.c
deleted file mode 100644
index f7d7186..0000000
--- a/drivers/i2c/busses/i2c-ite.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- -------------------------------------------------------------------------
- i2c-adap-ite.c i2c-hw access for the IIC peripheral on the ITE MIPS system
- -------------------------------------------------------------------------
- Hai-Pao Fan, MontaVista Software, Inc.
- hpfan@mvista.com or source@mvista.com
-
- Copyright 2001 MontaVista Software Inc.
-
- ----------------------------------------------------------------------------
- This file was highly leveraged from i2c-elektor.c, which was created
- by Simon G. Vogl and Hans Berglund:
-
-
- Copyright (C) 1995-97 Simon G. Vogl
- 1998-99 Hans Berglund
-
- 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. */
-/* ------------------------------------------------------------------------- */
-
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
- Frodo Looijaard <frodol@dds.nl> */
-
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-
-#include <linux/i2c.h>
-#include <linux/i2c-algo-ite.h>
-#include <linux/i2c-adap-ite.h>
-#include "../i2c-ite.h"
-
-#define DEFAULT_BASE 0x14014030
-#define ITE_IIC_IO_SIZE 0x40
-#define DEFAULT_IRQ 0
-#define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */
-#define DEFAULT_OWN 0x55
-
-static int base;
-static int irq;
-static int clock;
-static int own;
-
-static struct iic_ite gpi;
-static wait_queue_head_t iic_wait;
-static int iic_pending;
-static spinlock_t lock;
-
-/* ----- local functions ---------------------------------------------- */
-
-static void iic_ite_setiic(void *data, int ctl, short val)
-{
- unsigned long j = jiffies + 10;
-
- pr_debug(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff);
-#ifdef DEBUG
- while (time_before(jiffies, j))
- schedule();
-#endif
- outw(val,ctl);
-}
-
-static short iic_ite_getiic(void *data, int ctl)
-{
- short val;
-
- val = inw(ctl);
- pr_debug("Read 0x%02x from 0x%x\n",(unsigned short)val, ctl&0xff);
- return (val);
-}
-
-/* Return our slave address. This is the address
- * put on the I2C bus when another master on the bus wants to address us
- * as a slave
- */
-static int iic_ite_getown(void *data)
-{
- return (gpi.iic_own);
-}
-
-
-static int iic_ite_getclock(void *data)
-{
- return (gpi.iic_clock);
-}
-
-
-/* Put this process to sleep. We will wake up when the
- * IIC controller interrupts.
- */
-static void iic_ite_waitforpin(void) {
- DEFINE_WAIT(wait);
- int timeout = 2;
- unsigned long flags;
-
- /* If interrupts are enabled (which they are), then put the process to
- * sleep. This process will be awakened by two events -- either the
- * the IIC peripheral interrupts or the timeout expires.
- * If interrupts are not enabled then delay for a reasonable amount
- * of time and return.
- */
- if (gpi.iic_irq > 0) {
- spin_lock_irqsave(&lock, flags);
- if (iic_pending == 0) {
- spin_unlock_irqrestore(&lock, flags);
- prepare_to_wait(&iic_wait, &wait, TASK_INTERRUPTIBLE);
- if (schedule_timeout(timeout*HZ)) {
- spin_lock_irqsave(&lock, flags);
- if (iic_pending == 1) {
- iic_pending = 0;
- }
- spin_unlock_irqrestore(&lock, flags);
- }
- finish_wait(&iic_wait, &wait);
- } else {
- iic_pending = 0;
- spin_unlock_irqrestore(&lock, flags);
- }
- } else {
- udelay(100);
- }
-}
-
-
-static irqreturn_t iic_ite_handler(int this_irq, void *dev_id)
-{
- spin_lock(&lock);
- iic_pending = 1;
- spin_unlock(&lock);
-
- wake_up_interruptible(&iic_wait);
-
- return IRQ_HANDLED;
-}
-
-
-/* Lock the region of memory where I/O registers exist. Request our
- * interrupt line and register its associated handler.
- */
-static int iic_hw_resrc_init(void)
-{
- if (!request_region(gpi.iic_base, ITE_IIC_IO_SIZE, "i2c"))
- return -ENODEV;
-
- if (gpi.iic_irq <= 0)
- return 0;
-
- if (request_irq(gpi.iic_irq, iic_ite_handler, 0, "ITE IIC", 0) < 0)
- gpi.iic_irq = 0;
- else
- enable_irq(gpi.iic_irq);
-
- return 0;
-}
-
-
-static void iic_ite_release(void)
-{
- if (gpi.iic_irq > 0) {
- disable_irq(gpi.iic_irq);
- free_irq(gpi.iic_irq, 0);
- }
- release_region(gpi.iic_base , 2);
-}
-
-/* ------------------------------------------------------------------------
- * Encapsulate the above functions in the correct operations structure.
- * This is only done when more than one hardware adapter is supported.
- */
-static struct i2c_algo_iic_data iic_ite_data = {
- NULL,
- iic_ite_setiic,
- iic_ite_getiic,
- iic_ite_getown,
- iic_ite_getclock,
- iic_ite_waitforpin,
- 80, 80, 100, /* waits, timeout */
-};
-
-static struct i2c_adapter iic_ite_ops = {
- .owner = THIS_MODULE,
- .id = I2C_HW_I_IIC,
- .algo_data = &iic_ite_data,
- .name = "ITE IIC adapter",
-};
-
-/* Called when the module is loaded. This function starts the
- * cascade of calls up through the hierarchy of i2c modules (i.e. up to the
- * algorithm layer and into to the core layer)
- */
-static int __init iic_ite_init(void)
-{
-
- struct iic_ite *piic = &gpi;
-
- printk(KERN_INFO "Initialize ITE IIC adapter module\n");
- if (base == 0)
- piic->iic_base = DEFAULT_BASE;
- else
- piic->iic_base = base;
-
- if (irq == 0)
- piic->iic_irq = DEFAULT_IRQ;
- else
- piic->iic_irq = irq;
-
- if (clock == 0)
- piic->iic_clock = DEFAULT_CLOCK;
- else
- piic->iic_clock = clock;
-
- if (own == 0)
- piic->iic_own = DEFAULT_OWN;
- else
- piic->iic_own = own;
-
- iic_ite_data.data = (void *)piic;
- init_waitqueue_head(&iic_wait);
- spin_lock_init(&lock);
- if (iic_hw_resrc_init() == 0) {
- if (i2c_iic_add_bus(&iic_ite_ops) < 0)
- return -ENODEV;
- } else {
- return -ENODEV;
- }
- printk(KERN_INFO " found device at %#x irq %d.\n",
- piic->iic_base, piic->iic_irq);
- return 0;
-}
-
-
-static void iic_ite_exit(void)
-{
- i2c_iic_del_bus(&iic_ite_ops);
- iic_ite_release();
-}
-
-/* If modules is NOT defined when this file is compiled, then the MODULE_*
- * macros will resolve to nothing
- */
-MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter");
-MODULE_LICENSE("GPL");
-
-module_param(base, int, 0);
-module_param(irq, int, 0);
-module_param(clock, int, 0);
-module_param(own, int, 0);
-
-
-/* Called when module is loaded or when kernel is initialized.
- * If MODULES is defined when this file is compiled, then this function will
- * resolve to init_module (the function called when insmod is invoked for a
- * module). Otherwise, this function is called early in the boot, when the
- * kernel is intialized. Check out /include/init.h to see how this works.
- */
-module_init(iic_ite_init);
-
-/* Resolves to module_cleanup when MODULES is defined. */
-module_exit(iic_ite_exit);
diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c
index dd3f4cd..efa3ecc 100644
--- a/drivers/i2c/busses/i2c-ixp2000.c
+++ b/drivers/i2c/busses/i2c-ixp2000.c
@@ -90,7 +90,7 @@ static int ixp2000_i2c_remove(struct platform_device *plat_dev)
platform_set_drvdata(plat_dev, NULL);
- i2c_bit_del_bus(&drv_data->adapter);
+ i2c_del_adapter(&drv_data->adapter);
kfree(drv_data);
diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c
index 68fe863..08e89b8 100644
--- a/drivers/i2c/busses/i2c-ixp4xx.c
+++ b/drivers/i2c/busses/i2c-ixp4xx.c
@@ -91,7 +91,7 @@ static int ixp4xx_i2c_remove(struct platform_device *plat_dev)
platform_set_drvdata(plat_dev, NULL);
- i2c_bit_del_bus(&drv_data->adapter);
+ i2c_del_adapter(&drv_data->adapter);
kfree(drv_data);
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index e0292e4..ad37c10 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -35,7 +35,7 @@
nForce4 MCP55 0368
This driver supports the 2 SMBuses that are included in the MCP of the
- nForce2/3/4 chipsets.
+ nForce2/3/4/5xx chipsets.
*/
/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
@@ -52,8 +52,8 @@
#include <asm/io.h>
MODULE_LICENSE("GPL");
-MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>");
-MODULE_DESCRIPTION("nForce2 SMBus driver");
+MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@gmx.net>");
+MODULE_DESCRIPTION("nForce2/3/4/5xx SMBus driver");
struct nforce2_smbus {
@@ -80,9 +80,6 @@ struct nforce2_smbus {
#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */
#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */
#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */
-#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data bytes */
-#define NVIDIA_SMB_ALRM_A (smbus->base + 0x25) /* alarm address */
-#define NVIDIA_SMB_ALRM_D (smbus->base + 0x26) /* 2 bytes alarm data */
#define NVIDIA_SMB_STS_DONE 0x80
#define NVIDIA_SMB_STS_ALRM 0x40
@@ -95,40 +92,17 @@ struct nforce2_smbus {
#define NVIDIA_SMB_PRTCL_BYTE 0x04
#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06
#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08
-#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a
-#define NVIDIA_SMB_PRTCL_PROC_CALL 0x0c
-#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
-#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define NVIDIA_SMB_PRTCL_PEC 0x80
static struct pci_driver nforce2_driver;
-static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data);
-static u32 nforce2_func(struct i2c_adapter *adapter);
-
-
-static const struct i2c_algorithm smbus_algorithm = {
- .smbus_xfer = nforce2_access,
- .functionality = nforce2_func,
-};
-
-static struct i2c_adapter nforce2_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON,
- .algo = &smbus_algorithm,
-};
-
-/* Return -1 on error. See smbus.h for more information */
+/* Return -1 on error */
static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct nforce2_smbus *smbus = adap->algo_data;
unsigned char protocol, pec, temp;
- unsigned char len = 0; /* to keep the compiler quiet */
- int i;
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
NVIDIA_SMB_PRTCL_WRITE;
@@ -163,35 +137,6 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
break;
- case I2C_SMBUS_BLOCK_DATA:
- outb_p(command, NVIDIA_SMB_CMD);
- if (read_write == I2C_SMBUS_WRITE) {
- len = min_t(u8, data->block[0], 32);
- outb_p(len, NVIDIA_SMB_BCNT);
- for (i = 0; i < len; i++)
- outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
- }
- protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
- break;
-
- case I2C_SMBUS_I2C_BLOCK_DATA:
- len = min_t(u8, data->block[0], 32);
- outb_p(command, NVIDIA_SMB_CMD);
- outb_p(len, NVIDIA_SMB_BCNT);
- if (read_write == I2C_SMBUS_WRITE)
- for (i = 0; i < len; i++)
- outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
- protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA;
- break;
-
- case I2C_SMBUS_PROC_CALL:
- dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- return -1;
-
- case I2C_SMBUS_BLOCK_PROC_CALL:
- dev_err(&adap->dev, "I2C_SMBUS_BLOCK_PROC_CALL not supported!\n");
- return -1;
-
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
return -1;
@@ -227,19 +172,8 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
break;
case I2C_SMBUS_WORD_DATA:
- /* case I2C_SMBUS_PROC_CALL: not supported */
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
break;
-
- case I2C_SMBUS_BLOCK_DATA:
- /* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */
- len = inb_p(NVIDIA_SMB_BCNT);
- len = min_t(u8, len, 32);
- case I2C_SMBUS_I2C_BLOCK_DATA:
- for (i = 0; i < len; i++)
- data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
- data->block[0] = len;
- break;
}
return 0;
@@ -250,10 +184,14 @@ static u32 nforce2_func(struct i2c_adapter *adapter)
{
/* other functionality might be possible, but is not tested */
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* |
- I2C_FUNC_SMBUS_BLOCK_DATA */;
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
}
+static struct i2c_algorithm smbus_algorithm = {
+ .smbus_xfer = nforce2_access,
+ .functionality = nforce2_func,
+};
+
static struct pci_device_id nforce2_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) },
@@ -267,7 +205,6 @@ static struct pci_device_id nforce2_ids[] = {
{ 0 }
};
-
MODULE_DEVICE_TABLE (pci, nforce2_ids);
@@ -291,7 +228,7 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
}
smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
- smbus->size = 8;
+ smbus->size = 64;
}
smbus->dev = dev;
@@ -300,7 +237,9 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
smbus->base, smbus->base+smbus->size-1, name);
return -1;
}
- smbus->adapter = nforce2_adapter;
+ smbus->adapter.owner = THIS_MODULE;
+ smbus->adapter.class = I2C_CLASS_HWMON;
+ smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.dev.parent = &dev->dev;
snprintf(smbus->adapter.name, I2C_NAME_SIZE,
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index dec04da..bcd8367 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -231,8 +231,8 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
* 13 2 1
* 19.2 2 1
*/
- if (fclk_rate > 16000000)
- psc = (fclk_rate + 8000000) / 12000000;
+ if (fclk_rate > 12000000)
+ psc = fclk_rate / 12000000;
}
/* Setup clock prescaler to obtain approx 12MHz I2C module clock: */
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index 5eb2bd2..4bc4281 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -163,7 +163,7 @@ static void __exit i2c_parport_exit(void)
if (adapter_parm[type].init.val)
line_set(0, &adapter_parm[type].init);
- i2c_bit_del_bus(&parport_adapter);
+ i2c_del_adapter(&parport_adapter);
release_region(base, 3);
}
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 48a8294..66696a4 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -218,7 +218,7 @@ static void i2c_parport_detach (struct parport *port)
if (adapter_parm[type].init.val)
line_set(port, 0, &adapter_parm[type].init);
- i2c_bit_del_bus(&adapter->adapter);
+ i2c_del_adapter(&adapter->adapter);
parport_unregister_device(adapter->pdev);
if (prev)
prev->next = adapter->next;
diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c
index 407840b..cc6536a 100644
--- a/drivers/i2c/busses/i2c-pca-isa.c
+++ b/drivers/i2c/busses/i2c-pca-isa.c
@@ -156,7 +156,7 @@ static int __init pca_isa_init(void)
static void pca_isa_exit(void)
{
- i2c_pca_del_bus(&pca_isa_ops);
+ i2c_del_adapter(&pca_isa_ops);
if (irq > 0) {
disable_irq(irq);
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
new file mode 100644
index 0000000..de0bca7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -0,0 +1,708 @@
+/*
+ * Provides I2C support for Philips PNX010x/PNX4008 boards.
+ *
+ * Authors: Dennis Kovalev <dkovalev@ru.mvista.com>
+ * Vitaly Wool <vwool@ru.mvista.com>
+ *
+ * 2004-2006 (c) MontaVista Software, Inc. 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/i2c-pnx.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define I2C_PNX_TIMEOUT 10 /* msec */
+#define I2C_PNX_SPEED_KHZ 100
+#define I2C_PNX_REGION_SIZE 0x100
+#define PNX_DEFAULT_FREQ 13 /* MHz */
+
+static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data)
+{
+ while (timeout > 0 &&
+ (ioread32(I2C_REG_STS(data)) & mstatus_active)) {
+ mdelay(1);
+ timeout--;
+ }
+ return (timeout <= 0);
+}
+
+static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data)
+{
+ while (timeout > 0 &&
+ (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {
+ mdelay(1);
+ timeout--;
+ }
+ return (timeout <= 0);
+}
+
+static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *data = adap->algo_data;
+ struct timer_list *timer = &data->mif.timer;
+ int expires = I2C_PNX_TIMEOUT / (1000 / HZ);
+
+ del_timer_sync(timer);
+
+ dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n",
+ jiffies, expires);
+
+ timer->expires = jiffies + expires;
+ timer->data = (unsigned long)adap;
+
+ add_timer(timer);
+}
+
+/**
+ * i2c_pnx_start - start a device
+ * @slave_addr: slave address
+ * @adap: pointer to adapter structure
+ *
+ * Generate a START signal in the desired mode.
+ */
+static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+
+ dev_dbg(&adap->dev, "%s(): addr 0x%x mode %d\n", __FUNCTION__,
+ slave_addr, alg_data->mif.mode);
+
+ /* Check for 7 bit slave addresses only */
+ if (slave_addr & ~0x7f) {
+ dev_err(&adap->dev, "%s: Invalid slave address %x. "
+ "Only 7-bit addresses are supported\n",
+ adap->name, slave_addr);
+ return -EINVAL;
+ }
+
+ /* First, make sure bus is idle */
+ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) {
+ /* Somebody else is monopolizing the bus */
+ dev_err(&adap->dev, "%s: Bus busy. Slave addr = %02x, "
+ "cntrl = %x, stat = %x\n",
+ adap->name, slave_addr,
+ ioread32(I2C_REG_CTL(alg_data)),
+ ioread32(I2C_REG_STS(alg_data)));
+ return -EBUSY;
+ } else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) {
+ /* Sorry, we lost the bus */
+ dev_err(&adap->dev, "%s: Arbitration failure. "
+ "Slave addr = %02x\n", adap->name, slave_addr);
+ return -EIO;
+ }
+
+ /*
+ * OK, I2C is enabled and we have the bus.
+ * Clear the current TDI and AFI status flags.
+ */
+ iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi,
+ I2C_REG_STS(alg_data));
+
+ dev_dbg(&adap->dev, "%s(): sending %#x\n", __FUNCTION__,
+ (slave_addr << 1) | start_bit | alg_data->mif.mode);
+
+ /* Write the slave address, START bit and R/W bit */
+ iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode,
+ I2C_REG_TX(alg_data));
+
+ dev_dbg(&adap->dev, "%s(): exit\n", __FUNCTION__);
+
+ return 0;
+}
+
+/**
+ * i2c_pnx_stop - stop a device
+ * @adap: pointer to I2C adapter structure
+ *
+ * Generate a STOP signal to terminate the master transaction.
+ */
+static void i2c_pnx_stop(struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ /* Only 1 msec max timeout due to interrupt context */
+ long timeout = 1000;
+
+ dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ /* Write a STOP bit to TX FIFO */
+ iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data));
+
+ /* Wait until the STOP is seen. */
+ while (timeout > 0 &&
+ (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) {
+ /* may be called from interrupt context */
+ udelay(1);
+ timeout--;
+ }
+
+ dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+}
+
+/**
+ * i2c_pnx_master_xmit - transmit data to slave
+ * @adap: pointer to I2C adapter structure
+ *
+ * Sends one byte of data to the slave
+ */
+static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ u32 val;
+
+ dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ if (alg_data->mif.len > 0) {
+ /* We still have something to talk about... */
+ val = *alg_data->mif.buf++;
+
+ if (alg_data->mif.len == 1) {
+ val |= stop_bit;
+ if (!alg_data->last)
+ val |= start_bit;
+ }
+
+ alg_data->mif.len--;
+ iowrite32(val, I2C_REG_TX(alg_data));
+
+ dev_dbg(&adap->dev, "%s(): xmit %#x [%d]\n", __FUNCTION__,
+ val, alg_data->mif.len + 1);
+
+ if (alg_data->mif.len == 0) {
+ if (alg_data->last) {
+ /* Wait until the STOP is seen. */
+ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
+ dev_err(&adap->dev, "The bus is still "
+ "active after timeout\n");
+ }
+ /* Disable master interrupts */
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
+ ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
+ I2C_REG_CTL(alg_data));
+
+ del_timer_sync(&alg_data->mif.timer);
+
+ dev_dbg(&adap->dev, "%s(): Waking up xfer routine.\n",
+ __FUNCTION__);
+
+ complete(&alg_data->mif.complete);
+ }
+ } else if (alg_data->mif.len == 0) {
+ /* zero-sized transfer */
+ i2c_pnx_stop(adap);
+
+ /* Disable master interrupts. */
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
+ ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
+ I2C_REG_CTL(alg_data));
+
+ /* Stop timer. */
+ del_timer_sync(&alg_data->mif.timer);
+ dev_dbg(&adap->dev, "%s(): Waking up xfer routine after "
+ "zero-xfer.\n", __FUNCTION__);
+
+ complete(&alg_data->mif.complete);
+ }
+
+ dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ return 0;
+}
+
+/**
+ * i2c_pnx_master_rcv - receive data from slave
+ * @adap: pointer to I2C adapter structure
+ *
+ * Reads one byte data from the slave
+ */
+static int i2c_pnx_master_rcv(struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ unsigned int val = 0;
+ u32 ctl = 0;
+
+ dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ /* Check, whether there is already data,
+ * or we didn't 'ask' for it yet.
+ */
+ if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
+ dev_dbg(&adap->dev, "%s(): Write dummy data to fill "
+ "Rx-fifo...\n", __FUNCTION__);
+
+ if (alg_data->mif.len == 1) {
+ /* Last byte, do not acknowledge next rcv. */
+ val |= stop_bit;
+ if (!alg_data->last)
+ val |= start_bit;
+
+ /*
+ * Enable interrupt RFDAIE (data in Rx fifo),
+ * and disable DRMIE (need data for Tx)
+ */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl |= mcntrl_rffie | mcntrl_daie;
+ ctl &= ~mcntrl_drmie;
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+ }
+
+ /*
+ * Now we'll 'ask' for data:
+ * For each byte we want to receive, we must
+ * write a (dummy) byte to the Tx-FIFO.
+ */
+ iowrite32(val, I2C_REG_TX(alg_data));
+
+ return 0;
+ }
+
+ /* Handle data. */
+ if (alg_data->mif.len > 0) {
+ val = ioread32(I2C_REG_RX(alg_data));
+ *alg_data->mif.buf++ = (u8) (val & 0xff);
+ dev_dbg(&adap->dev, "%s(): rcv 0x%x [%d]\n", __FUNCTION__, val,
+ alg_data->mif.len);
+
+ alg_data->mif.len--;
+ if (alg_data->mif.len == 0) {
+ if (alg_data->last)
+ /* Wait until the STOP is seen. */
+ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
+ dev_err(&adap->dev, "The bus is still "
+ "active after timeout\n");
+
+ /* Disable master interrupts */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
+ mcntrl_drmie | mcntrl_daie);
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+
+ /* Kill timer. */
+ del_timer_sync(&alg_data->mif.timer);
+ complete(&alg_data->mif.complete);
+ }
+ }
+
+ dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ return 0;
+}
+
+static irqreturn_t
+i2c_pnx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 stat, ctl;
+ struct i2c_adapter *adap = dev_id;
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+
+ dev_dbg(&adap->dev, "%s(): mstat = %x mctrl = %x, mode = %d\n",
+ __FUNCTION__,
+ ioread32(I2C_REG_STS(alg_data)),
+ ioread32(I2C_REG_CTL(alg_data)),
+ alg_data->mif.mode);
+ stat = ioread32(I2C_REG_STS(alg_data));
+
+ /* let's see what kind of event this is */
+ if (stat & mstatus_afi) {
+ /* We lost arbitration in the midst of a transfer */
+ alg_data->mif.ret = -EIO;
+
+ /* Disable master interrupts. */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
+ mcntrl_drmie);
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+
+ /* Stop timer, to prevent timeout. */
+ del_timer_sync(&alg_data->mif.timer);
+ complete(&alg_data->mif.complete);
+ } else if (stat & mstatus_nai) {
+ /* Slave did not acknowledge, generate a STOP */
+ dev_dbg(&adap->dev, "%s(): "
+ "Slave did not acknowledge, generating a STOP.\n",
+ __FUNCTION__);
+ i2c_pnx_stop(adap);
+
+ /* Disable master interrupts. */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
+ mcntrl_drmie);
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+
+ /* Our return value. */
+ alg_data->mif.ret = -EIO;
+
+ /* Stop timer, to prevent timeout. */
+ del_timer_sync(&alg_data->mif.timer);
+ complete(&alg_data->mif.complete);
+ } else {
+ /*
+ * Two options:
+ * - Master Tx needs data.
+ * - There is data in the Rx-fifo
+ * The latter is only the case if we have requested for data,
+ * via a dummy write. (See 'i2c_pnx_master_rcv'.)
+ * We therefore check, as a sanity check, whether that interrupt
+ * has been enabled.
+ */
+ if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) {
+ if (alg_data->mif.mode == I2C_SMBUS_WRITE) {
+ i2c_pnx_master_xmit(adap);
+ } else if (alg_data->mif.mode == I2C_SMBUS_READ) {
+ i2c_pnx_master_rcv(adap);
+ }
+ }
+ }
+
+ /* Clear TDI and AFI bits */
+ stat = ioread32(I2C_REG_STS(alg_data));
+ iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data));
+
+ dev_dbg(&adap->dev, "%s(): exiting, stat = %x ctrl = %x.\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)),
+ ioread32(I2C_REG_CTL(alg_data)));
+
+ return IRQ_HANDLED;
+}
+
+static void i2c_pnx_timeout(unsigned long data)
+{
+ struct i2c_adapter *adap = (struct i2c_adapter *)data;
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ u32 ctl;
+
+ dev_err(&adap->dev, "Master timed out. stat = %04x, cntrl = %04x. "
+ "Resetting master...\n",
+ ioread32(I2C_REG_STS(alg_data)),
+ ioread32(I2C_REG_CTL(alg_data)));
+
+ /* Reset master and disable interrupts */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie);
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+
+ ctl |= mcntrl_reset;
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+ wait_reset(I2C_PNX_TIMEOUT, alg_data);
+ alg_data->mif.ret = -EIO;
+ complete(&alg_data->mif.complete);
+}
+
+static inline void bus_reset_if_active(struct i2c_adapter *adap)
+{
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ u32 stat;
+
+ if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) {
+ dev_err(&adap->dev,
+ "%s: Bus is still active after xfer. Reset it...\n",
+ adap->name);
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
+ I2C_REG_CTL(alg_data));
+ wait_reset(I2C_PNX_TIMEOUT, alg_data);
+ } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) {
+ /* If there is data in the fifo's after transfer,
+ * flush fifo's by reset.
+ */
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
+ I2C_REG_CTL(alg_data));
+ wait_reset(I2C_PNX_TIMEOUT, alg_data);
+ } else if (stat & mstatus_nai) {
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
+ I2C_REG_CTL(alg_data));
+ wait_reset(I2C_PNX_TIMEOUT, alg_data);
+ }
+}
+
+/**
+ * i2c_pnx_xfer - generic transfer entry point
+ * @adap: pointer to I2C adapter structure
+ * @msgs: array of messages
+ * @num: number of messages
+ *
+ * Initiates the transfer
+ */
+static int
+i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct i2c_msg *pmsg;
+ int rc = 0, completed = 0, i;
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ u32 stat = ioread32(I2C_REG_STS(alg_data));
+
+ dev_dbg(&adap->dev, "%s(): entering: %d messages, stat = %04x.\n",
+ __FUNCTION__, num, ioread32(I2C_REG_STS(alg_data)));
+
+ bus_reset_if_active(adap);
+
+ /* Process transactions in a loop. */
+ for (i = 0; rc >= 0 && i < num; i++) {
+ u8 addr;
+
+ pmsg = &msgs[i];
+ addr = pmsg->addr;
+
+ if (pmsg->flags & I2C_M_TEN) {
+ dev_err(&adap->dev,
+ "%s: 10 bits addr not supported!\n",
+ adap->name);
+ rc = -EINVAL;
+ break;
+ }
+
+ alg_data->mif.buf = pmsg->buf;
+ alg_data->mif.len = pmsg->len;
+ alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?
+ I2C_SMBUS_READ : I2C_SMBUS_WRITE;
+ alg_data->mif.ret = 0;
+ alg_data->last = (i == num - 1);
+
+ dev_dbg(&adap->dev, "%s(): mode %d, %d bytes\n", __FUNCTION__,
+ alg_data->mif.mode,
+ alg_data->mif.len);
+
+ i2c_pnx_arm_timer(adap);
+
+ /* initialize the completion var */
+ init_completion(&alg_data->mif.complete);
+
+ /* Enable master interrupt */
+ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie |
+ mcntrl_naie | mcntrl_drmie,
+ I2C_REG_CTL(alg_data));
+
+ /* Put start-code and slave-address on the bus. */
+ rc = i2c_pnx_start(addr, adap);
+ if (rc < 0)
+ break;
+
+ /* Wait for completion */
+ wait_for_completion(&alg_data->mif.complete);
+
+ if (!(rc = alg_data->mif.ret))
+ completed++;
+ dev_dbg(&adap->dev, "%s(): Complete, return code = %d.\n",
+ __FUNCTION__, rc);
+
+ /* Clear TDI and AFI bits in case they are set. */
+ if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) {
+ dev_dbg(&adap->dev,
+ "%s: TDI still set... clearing now.\n",
+ adap->name);
+ iowrite32(stat, I2C_REG_STS(alg_data));
+ }
+ if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) {
+ dev_dbg(&adap->dev,
+ "%s: AFI still set... clearing now.\n",
+ adap->name);
+ iowrite32(stat, I2C_REG_STS(alg_data));
+ }
+ }
+
+ bus_reset_if_active(adap);
+
+ /* Cleanup to be sure... */
+ alg_data->mif.buf = NULL;
+ alg_data->mif.len = 0;
+
+ dev_dbg(&adap->dev, "%s(): exiting, stat = %x\n",
+ __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));
+
+ if (completed != num)
+ return ((rc < 0) ? rc : -EREMOTEIO);
+
+ return num;
+}
+
+static u32 i2c_pnx_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm pnx_algorithm = {
+ .master_xfer = i2c_pnx_xfer,
+ .functionality = i2c_pnx_func,
+};
+
+static int i2c_pnx_controller_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
+ return i2c_pnx->suspend(pdev, state);
+}
+
+static int i2c_pnx_controller_resume(struct platform_device *pdev)
+{
+ struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
+ return i2c_pnx->resume(pdev);
+}
+
+static int __devinit i2c_pnx_probe(struct platform_device *pdev)
+{
+ unsigned long tmp;
+ int ret = 0;
+ struct i2c_pnx_algo_data *alg_data;
+ int freq_mhz;
+ struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;
+
+ if (!i2c_pnx || !i2c_pnx->adapter) {
+ dev_err(&pdev->dev, "%s: no platform data supplied\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ platform_set_drvdata(pdev, i2c_pnx);
+
+ if (i2c_pnx->calculate_input_freq)
+ freq_mhz = i2c_pnx->calculate_input_freq(pdev);
+ else {
+ freq_mhz = PNX_DEFAULT_FREQ;
+ dev_info(&pdev->dev, "Setting bus frequency to default value: "
+ "%d MHz", freq_mhz);
+ }
+
+ i2c_pnx->adapter->algo = &pnx_algorithm;
+
+ alg_data = i2c_pnx->adapter->algo_data;
+ init_timer(&alg_data->mif.timer);
+ alg_data->mif.timer.function = i2c_pnx_timeout;
+ alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;
+
+ /* Register I/O resource */
+ if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) {
+ dev_err(&pdev->dev,
+ "I/O region 0x%08x for I2C already in use.\n",
+ alg_data->base);
+ ret = -ENODEV;
+ goto out_drvdata;
+ }
+
+ if (!(alg_data->ioaddr =
+ (u32)ioremap(alg_data->base, I2C_PNX_REGION_SIZE))) {
+ dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");
+ ret = -ENOMEM;
+ goto out_release;
+ }
+
+ i2c_pnx->set_clock_run(pdev);
+
+ /*
+ * Clock Divisor High This value is the number of system clocks
+ * the serial clock (SCL) will be high.
+ * For example, if the system clock period is 50 ns and the maximum
+ * desired serial period is 10000 ns (100 kHz), then CLKHI would be
+ * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value
+ * programmed into CLKHI will vary from this slightly due to
+ * variations in the output pad's rise and fall times as well as
+ * the deglitching filter length.
+ */
+
+ tmp = ((freq_mhz * 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;
+ iowrite32(tmp, I2C_REG_CKH(alg_data));
+ iowrite32(tmp, I2C_REG_CKL(alg_data));
+
+ iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));
+ if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) {
+ ret = -ENODEV;
+ goto out_unmap;
+ }
+ init_completion(&alg_data->mif.complete);
+
+ ret = request_irq(alg_data->irq, i2c_pnx_interrupt,
+ 0, pdev->name, i2c_pnx->adapter);
+ if (ret)
+ goto out_clock;
+
+ /* Register this adapter with the I2C subsystem */
+ i2c_pnx->adapter->dev.parent = &pdev->dev;
+ ret = i2c_add_adapter(i2c_pnx->adapter);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2C: Failed to add bus\n");
+ goto out_irq;
+ }
+
+ dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",
+ i2c_pnx->adapter->name, alg_data->base, alg_data->irq);
+
+ return 0;
+
+out_irq:
+ free_irq(alg_data->irq, alg_data);
+out_clock:
+ i2c_pnx->set_clock_stop(pdev);
+out_unmap:
+ iounmap((void *)alg_data->ioaddr);
+out_release:
+ release_region(alg_data->base, I2C_PNX_REGION_SIZE);
+out_drvdata:
+ platform_set_drvdata(pdev, NULL);
+out:
+ return ret;
+}
+
+static int __devexit i2c_pnx_remove(struct platform_device *pdev)
+{
+ struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
+ struct i2c_adapter *adap = i2c_pnx->adapter;
+ struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+
+ free_irq(alg_data->irq, alg_data);
+ i2c_del_adapter(adap);
+ i2c_pnx->set_clock_stop(pdev);
+ iounmap((void *)alg_data->ioaddr);
+ release_region(alg_data->base, I2C_PNX_REGION_SIZE);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver i2c_pnx_driver = {
+ .driver = {
+ .name = "pnx-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = i2c_pnx_probe,
+ .remove = __devexit_p(i2c_pnx_remove),
+ .suspend = i2c_pnx_controller_suspend,
+ .resume = i2c_pnx_controller_resume,
+};
+
+static int __init i2c_adap_pnx_init(void)
+{
+ return platform_driver_register(&i2c_pnx_driver);
+}
+
+static void __exit i2c_adap_pnx_exit(void)
+{
+ platform_driver_unregister(&i2c_pnx_driver);
+}
+
+MODULE_AUTHOR("Vitaly Wool, Dennis Kovalev <source@mvista.com>");
+MODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_I2C_PNX_EARLY
+/* We need to make sure I2C is initialized before USB */
+subsys_initcall(i2c_adap_pnx_init);
+#else
+mudule_init(i2c_adap_pnx_init);
+#endif
+module_exit(i2c_adap_pnx_exit);
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c
index 7745e21..07c1f1e 100644
--- a/drivers/i2c/busses/i2c-prosavage.c
+++ b/drivers/i2c/busses/i2c-prosavage.c
@@ -212,7 +212,7 @@ static void prosavage_remove(struct pci_dev *dev)
if (chip->i2c_bus[i].adap_ok == 0)
continue;
- ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap);
+ ret = i2c_del_adapter(&chip->i2c_bus[i].adap);
if (ret) {
dev_err(&dev->dev, "%s not removed\n",
chip->i2c_bus[i].adap.name);
diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c
index 209f47e..844b4ff 100644
--- a/drivers/i2c/busses/i2c-savage4.c
+++ b/drivers/i2c/busses/i2c-savage4.c
@@ -173,7 +173,7 @@ static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_
static void __devexit savage4_remove(struct pci_dev *dev)
{
- i2c_bit_del_bus(&savage4_i2c_adapter);
+ i2c_del_adapter(&savage4_i2c_adapter);
iounmap(ioaddr);
}
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
new file mode 100644
index 0000000..081d957
--- /dev/null
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -0,0 +1,153 @@
+/*
+ * i2c-versatile.c
+ *
+ * Copyright (C) 2006 ARM Ltd.
+ * written by Russell King, Deep Blue Solutions 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#define I2C_CONTROL 0x00
+#define I2C_CONTROLS 0x00
+#define I2C_CONTROLC 0x04
+#define SCL (1 << 0)
+#define SDA (1 << 1)
+
+struct i2c_versatile {
+ struct i2c_adapter adap;
+ struct i2c_algo_bit_data algo;
+ void __iomem *base;
+};
+
+static void i2c_versatile_setsda(void *data, int state)
+{
+ struct i2c_versatile *i2c = data;
+
+ writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC));
+}
+
+static void i2c_versatile_setscl(void *data, int state)
+{
+ struct i2c_versatile *i2c = data;
+
+ writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC));
+}
+
+static int i2c_versatile_getsda(void *data)
+{
+ struct i2c_versatile *i2c = data;
+ return !!(readl(i2c->base + I2C_CONTROL) & SDA);
+}
+
+static int i2c_versatile_getscl(void *data)
+{
+ struct i2c_versatile *i2c = data;
+ return !!(readl(i2c->base + I2C_CONTROL) & SCL);
+}
+
+static struct i2c_algo_bit_data i2c_versatile_algo = {
+ .setsda = i2c_versatile_setsda,
+ .setscl = i2c_versatile_setscl,
+ .getsda = i2c_versatile_getsda,
+ .getscl = i2c_versatile_getscl,
+ .udelay = 30,
+ .timeout = HZ,
+};
+
+static int i2c_versatile_probe(struct platform_device *dev)
+{
+ struct i2c_versatile *i2c;
+ struct resource *r;
+ int ret;
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (!request_mem_region(r->start, r->end - r->start + 1, "versatile-i2c")) {
+ ret = -EBUSY;
+ goto err_out;
+ }
+
+ i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL);
+ if (!i2c) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ i2c->base = ioremap(r->start, r->end - r->start + 1);
+ if (!i2c->base) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ writel(SCL | SDA, i2c->base + I2C_CONTROLS);
+
+ i2c->adap.owner = THIS_MODULE;
+ strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
+ i2c->adap.algo_data = &i2c->algo;
+ i2c->adap.dev.parent = &dev->dev;
+ i2c->algo = i2c_versatile_algo;
+ i2c->algo.data = i2c;
+
+ ret = i2c_bit_add_bus(&i2c->adap);
+ if (ret >= 0) {
+ platform_set_drvdata(dev, i2c);
+ return 0;
+ }
+
+ iounmap(i2c->base);
+ err_free:
+ kfree(i2c);
+ err_release:
+ release_mem_region(r->start, r->end - r->start + 1);
+ err_out:
+ return ret;
+}
+
+static int i2c_versatile_remove(struct platform_device *dev)
+{
+ struct i2c_versatile *i2c = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+
+ i2c_del_adapter(&i2c->adap);
+ return 0;
+}
+
+static struct platform_driver i2c_versatile_driver = {
+ .probe = i2c_versatile_probe,
+ .remove = i2c_versatile_remove,
+ .driver = {
+ .name = "versatile-i2c",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init i2c_versatile_init(void)
+{
+ return platform_driver_register(&i2c_versatile_driver);
+}
+
+static void __exit i2c_versatile_exit(void)
+{
+ platform_driver_unregister(&i2c_versatile_driver);
+}
+
+module_init(i2c_versatile_init);
+module_exit(i2c_versatile_exit);
+
+MODULE_DESCRIPTION("ARM Versatile I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 910e200..15d7e00 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -151,7 +151,7 @@ static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_i
static void __devexit vt586b_remove(struct pci_dev *dev)
{
- i2c_bit_del_bus(&vt586b_adapter);
+ i2c_del_adapter(&vt586b_adapter);
release_region(I2C_DIR, IOSPACE);
pm_io_base = 0;
}
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
index 6c8d251..b0377b8 100644
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ b/drivers/i2c/busses/i2c-voodoo3.c
@@ -211,14 +211,14 @@ static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_
return retval;
retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
if (retval)
- i2c_bit_del_bus(&voodoo3_i2c_adapter);
+ i2c_del_adapter(&voodoo3_i2c_adapter);
return retval;
}
static void __devexit voodoo3_remove(struct pci_dev *dev)
{
- i2c_bit_del_bus(&voodoo3_i2c_adapter);
- i2c_bit_del_bus(&voodoo3_ddc_adapter);
+ i2c_del_adapter(&voodoo3_i2c_adapter);
+ i2c_del_adapter(&voodoo3_ddc_adapter);
iounmap(ioaddr);
}
diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c
index 8ddbae4..6cd96e4 100644
--- a/drivers/i2c/busses/scx200_i2c.c
+++ b/drivers/i2c/busses/scx200_i2c.c
@@ -116,7 +116,7 @@ static int scx200_i2c_init(void)
static void scx200_i2c_cleanup(void)
{
- i2c_bit_del_bus(&scx200_i2c_ops);
+ i2c_del_adapter(&scx200_i2c_ops);
}
module_init(scx200_i2c_init);
diff --git a/drivers/i2c/chips/ds1337.c b/drivers/i2c/chips/ds1337.c
index 93d483b..ec17d6b 100644
--- a/drivers/i2c/chips/ds1337.c
+++ b/drivers/i2c/chips/ds1337.c
@@ -347,13 +347,19 @@ static void ds1337_init_client(struct i2c_client *client)
if ((status & 0x80) || (control & 0x80)) {
/* RTC not running */
- u8 buf[16];
+ u8 buf[1+16]; /* First byte is interpreted as address */
struct i2c_msg msg[1];
dev_dbg(&client->dev, "%s: RTC not running!\n", __FUNCTION__);
/* Initialize all, including STATUS and CONTROL to zero */
memset(buf, 0, sizeof(buf));
+
+ /* Write valid values in the date/time registers */
+ buf[1+DS1337_REG_DAY] = 1;
+ buf[1+DS1337_REG_DATE] = 1;
+ buf[1+DS1337_REG_MONTH] = 1;
+
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = sizeof(buf);
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
index 60bef94..4ee56de 100644
--- a/drivers/i2c/chips/tps65010.c
+++ b/drivers/i2c/chips/tps65010.c
@@ -82,7 +82,7 @@ struct tps65010 {
struct i2c_client client;
struct mutex lock;
int irq;
- struct work_struct work;
+ struct delayed_work work;
struct dentry *file;
unsigned charging:1;
unsigned por:1;
@@ -328,7 +328,7 @@ static void tps65010_interrupt(struct tps65010 *tps)
{
u8 tmp = 0, mask, poll;
- /* IRQs won't trigger irqs for certain events, but we can get
+ /* IRQs won't trigger for certain events, but we can get
* others by polling (normally, with external power applied).
*/
poll = 0;
@@ -411,10 +411,11 @@ static void tps65010_interrupt(struct tps65010 *tps)
}
/* handle IRQs and polling using keventd for now */
-static void tps65010_work(void *_tps)
+static void tps65010_work(struct work_struct *work)
{
- struct tps65010 *tps = _tps;
+ struct tps65010 *tps;
+ tps = container_of(work, struct tps65010, work.work);
mutex_lock(&tps->lock);
tps65010_interrupt(tps);
@@ -452,7 +453,7 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
disable_irq_nosync(irq);
set_bit(FLAG_IRQ_ENABLE, &tps->flags);
- (void) schedule_work(&tps->work);
+ (void) schedule_work(&tps->work.work);
return IRQ_HANDLED;
}
@@ -465,13 +466,15 @@ static int __exit tps65010_detach_client(struct i2c_client *client)
struct tps65010 *tps;
tps = container_of(client, struct tps65010, client);
+ free_irq(tps->irq, tps);
#ifdef CONFIG_ARM
if (machine_is_omap_h2())
omap_free_gpio(58);
if (machine_is_omap_osk())
omap_free_gpio(OMAP_MPUIO(1));
#endif
- free_irq(tps->irq, tps);
+ cancel_delayed_work(&tps->work);
+ flush_scheduled_work();
debugfs_remove(tps->file);
if (i2c_detach_client(client) == 0)
kfree(tps);
@@ -505,7 +508,7 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind)
return 0;
mutex_init(&tps->lock);
- INIT_WORK(&tps->work, tps65010_work, tps);
+ INIT_DELAYED_WORK(&tps->work, tps65010_work);
tps->irq = -1;
tps->client.addr = address;
tps->client.adapter = bus;
@@ -620,7 +623,7 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind)
(void) i2c_smbus_write_byte_data(&tps->client, TPS_MASK3, 0x0f
| i2c_smbus_read_byte_data(&tps->client, TPS_MASK3));
- tps65010_work(tps);
+ tps65010_work(&tps->work.work);
tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
tps, DEBUG_FOPS);
@@ -672,7 +675,7 @@ int tps65010_set_vbus_draw(unsigned mA)
&& test_and_set_bit(
FLAG_VBUS_CHANGED, &the_tps->flags)) {
/* gadget drivers call this in_irq() */
- (void) schedule_work(&the_tps->work);
+ (void) schedule_work(&the_tps->work.work);
}
local_irq_restore(flags);
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 7ca81f42..3e31f1d 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -127,20 +127,17 @@ static ssize_t show_client_name(struct device *dev, struct device_attribute *att
return sprintf(buf, "%s\n", client->name);
}
-/*
- * We can't use the DEVICE_ATTR() macro here as we want the same filename for a
- * different type of a device. So beware if the DEVICE_ATTR() macro ever
- * changes, this definition will also have to change.
+/*
+ * We can't use the DEVICE_ATTR() macro here, as we used the same name for
+ * an i2c adapter attribute (above).
*/
-static struct device_attribute dev_attr_client_name = {
- .attr = {.name = "name", .mode = S_IRUGO, .owner = THIS_MODULE },
- .show = &show_client_name,
-};
+static struct device_attribute dev_attr_client_name =
+ __ATTR(name, S_IRUGO, &show_client_name, NULL);
/* ---------------------------------------------------
- * registering functions
- * ---------------------------------------------------
+ * registering functions
+ * ---------------------------------------------------
*/
/* -----
@@ -314,7 +311,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
res = driver_register(&driver->driver);
if (res)
return res;
-
+
mutex_lock(&core_lists);
list_add_tail(&driver->list,&drivers);
@@ -338,13 +335,13 @@ int i2c_del_driver(struct i2c_driver *driver)
struct list_head *item1, *item2, *_n;
struct i2c_client *client;
struct i2c_adapter *adap;
-
+
int res = 0;
mutex_lock(&core_lists);
/* Have a look at each adapter, if clients of this driver are still
- * attached. If so, detach them to be able to kill the driver
+ * attached. If so, detach them to be able to kill the driver
* afterwards.
*/
list_for_each(item1,&adapters) {
@@ -419,14 +416,14 @@ int i2c_attach_client(struct i2c_client *client)
goto out_unlock;
}
list_add_tail(&client->list,&adapter->clients);
-
+
client->usage_count = 0;
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
-
+
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
@@ -467,7 +464,7 @@ int i2c_detach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
-
+
if (client->usage_count > 0) {
dev_warn(&client->dev, "Client [%s] still busy, "
"can't detach\n", client->name);
@@ -535,10 +532,10 @@ int i2c_release_client(struct i2c_client *client)
__FUNCTION__);
return -EPERM;
}
-
+
client->usage_count--;
i2c_dec_use_client(client);
-
+
return 0;
}
@@ -603,7 +600,7 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
}
#endif
- mutex_lock(&adap->bus_lock);
+ mutex_lock_nested(&adap->bus_lock, adap->level);
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
@@ -624,7 +621,7 @@ int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
-
+
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
@@ -757,7 +754,7 @@ int i2c_probe(struct i2c_adapter *adapter,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
if (address_data->probe[0] == I2C_CLIENT_END
&& address_data->normal_i2c[0] == I2C_CLIENT_END)
- return 0;
+ return 0;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
"can't probe for chips\n");
@@ -817,7 +814,7 @@ int i2c_probe(struct i2c_adapter *adapter,
struct i2c_adapter* i2c_get_adapter(int id)
{
struct i2c_adapter *adapter;
-
+
mutex_lock(&core_lists);
adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
if (adapter && !try_module_get(adapter->owner))
@@ -834,14 +831,14 @@ void i2c_put_adapter(struct i2c_adapter *adap)
/* The SMBus parts */
-#define POLY (0x1070U << 3)
+#define POLY (0x1070U << 3)
static u8
crc8(u16 data)
{
int i;
-
+
for(i = 0; i < 8; i++) {
- if (data & 0x8000)
+ if (data & 0x8000)
data = data ^ POLY;
data = data << 1;
}
@@ -891,13 +888,13 @@ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
rpec, cpec);
return -1;
}
- return 0;
+ return 0;
}
s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- value,0,I2C_SMBUS_QUICK,NULL);
+ value,0,I2C_SMBUS_QUICK,NULL);
}
s32 i2c_smbus_read_byte(struct i2c_client *client)
@@ -996,11 +993,11 @@ s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
}
-/* Simulate a SMBus command using the i2c protocol
+/* Simulate a SMBus command using the i2c protocol
No checking of parameters is done! */
-static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
+static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned short flags,
- char read_write, u8 command, int size,
+ char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
/* So we need to generate a series of msgs. In the case of writing, we
@@ -1010,7 +1007,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
int num = read_write == I2C_SMBUS_READ?2:1;
- struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
+ struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
{ addr, flags | I2C_M_RD, 0, msgbuf1 }
};
int i;
@@ -1103,14 +1100,14 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
if (i) {
/* Compute PEC if first message is a write */
if (!(msg[0].flags & I2C_M_RD)) {
- if (num == 1) /* Write only */
+ if (num == 1) /* Write only */
i2c_smbus_add_pec(&msg[0]);
else /* Write followed by read */
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
}
/* Ask for PEC if last message is a read */
if (msg[num-1].flags & I2C_M_RD)
- msg[num-1].len++;
+ msg[num-1].len++;
}
if (i2c_transfer(adapter, msg, num) < 0)
@@ -1130,7 +1127,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0];
break;
- case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
@@ -1146,7 +1143,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
- char read_write, u8 command, int size,
+ char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
s32 res;
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 2e22a2f..ac5bd2a 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -1,5 +1,5 @@
/*
- i2c-dev.c - i2c-bus driver, char device interface
+ i2c-dev.c - i2c-bus driver, char device interface
Copyright (C) 1995-97 Simon G. Vogl
Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
@@ -90,6 +90,7 @@ static void return_i2c_dev(struct i2c_dev *i2c_dev)
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list);
spin_unlock(&i2c_dev_list_lock);
+ kfree(i2c_dev);
}
static ssize_t show_adapter_name(struct device *dev,
@@ -172,7 +173,7 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
switch ( cmd ) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
- if ((arg > 0x3ff) ||
+ if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg))
@@ -193,12 +194,11 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
- return (copy_to_user((unsigned long __user *)arg, &funcs,
- sizeof(unsigned long)))?-EFAULT:0;
+ return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR:
- if (copy_from_user(&rdwr_arg,
- (struct i2c_rdwr_ioctl_data __user *)arg,
+ if (copy_from_user(&rdwr_arg,
+ (struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
@@ -206,9 +206,9 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
-
+
rdwr_pa = (struct i2c_msg *)
- kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
+ kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
if (rdwr_pa == NULL) return -ENOMEM;
@@ -278,9 +278,9 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
- if ((data_arg.size != I2C_SMBUS_BYTE) &&
+ if ((data_arg.size != I2C_SMBUS_BYTE) &&
(data_arg.size != I2C_SMBUS_QUICK) &&
- (data_arg.size != I2C_SMBUS_BYTE_DATA) &&
+ (data_arg.size != I2C_SMBUS_BYTE_DATA) &&
(data_arg.size != I2C_SMBUS_WORD_DATA) &&
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
@@ -291,11 +291,11 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
data_arg.size);
return -EINVAL;
}
- /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
+ /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
so the check is valid if size==I2C_SMBUS_QUICK too. */
- if ((data_arg.read_write != I2C_SMBUS_READ) &&
+ if ((data_arg.read_write != I2C_SMBUS_READ) &&
(data_arg.read_write != I2C_SMBUS_WRITE)) {
- dev_dbg(&client->adapter->dev,
+ dev_dbg(&client->adapter->dev,
"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.read_write);
return -EINVAL;
@@ -304,7 +304,7 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
/* Note that command values are always valid! */
if ((data_arg.size == I2C_SMBUS_QUICK) ||
- ((data_arg.size == I2C_SMBUS_BYTE) &&
+ ((data_arg.size == I2C_SMBUS_BYTE) &&
(data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr,
@@ -322,14 +322,14 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
(data_arg.size == I2C_SMBUS_BYTE))
datasize = sizeof(data_arg.data->byte);
- else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
+ else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
(data_arg.size == I2C_SMBUS_PROC_CALL))
datasize = sizeof(data_arg.data->word);
else /* size == smbus block, i2c block, or block proc. call */
datasize = sizeof(data_arg.data->block);
- if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
- (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
+ if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
+ (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
@@ -337,8 +337,8 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
data_arg.read_write,
data_arg.command,data_arg.size,&temp);
- if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
- (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
+ if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
+ (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
@@ -417,8 +417,8 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap)
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr),
"i2c-%d", adap->nr);
- if (!i2c_dev->dev) {
- res = -ENODEV;
+ if (IS_ERR(i2c_dev->dev)) {
+ res = PTR_ERR(i2c_dev->dev);
goto error;
}
res = device_create_file(i2c_dev->dev, &dev_attr_name);
@@ -432,7 +432,6 @@ error_destroy:
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
- kfree(i2c_dev);
return res;
}
@@ -447,7 +446,6 @@ static int i2cdev_detach_adapter(struct i2c_adapter *adap)
device_remove_file(i2c_dev->dev, &dev_attr_name);
return_i2c_dev(i2c_dev);
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
- kfree(i2c_dev);
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
return 0;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index e23bc0d..3f82805 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -796,7 +796,7 @@ endchoice
config BLK_DEV_IDE_AU1XXX_SEQTS_PER_RQ
int "Maximum transfer size (KB) per request (up to 128)"
default "128"
- depends BLK_DEV_IDE_AU1XXX
+ depends on BLK_DEV_IDE_AU1XXX
config IDE_ARM
def_bool ARM && (ARCH_A5K || ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK)
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 8821494..5969cec 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -687,8 +687,15 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 sta
static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
{
struct request *rq = HWGROUP(drive)->rq;
+ ide_hwif_t *hwif = HWIF(drive);
int stat, err, sense_key;
+ /* We may have bogus DMA interrupts in PIO state here */
+ if (HWIF(drive)->dma_status && hwif->atapi_irq_bogon) {
+ stat = hwif->INB(hwif->dma_status);
+ /* Should we force the bit as well ? */
+ hwif->OUTB(stat, hwif->dma_status);
+ }
/* Check for errors. */
stat = HWIF(drive)->INB(IDE_STATUS_REG);
if (stat_ret)
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index e3a2676..d33717c 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -2147,7 +2147,7 @@ static int ide_floppy_probe(ide_drive_t *drive)
printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name);
goto failed;
}
- if ((floppy = (idefloppy_floppy_t *) kzalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
+ if ((floppy = kzalloc(sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
goto failed;
}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index dad9c47..5a5c565 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1000,10 +1000,6 @@ static int ide_init_queue(ide_drive_t *drive)
/* needs drive->queue to be set */
ide_toggle_bounce(drive, 1);
- /* enable led activity for disk drives only */
- if (drive->media == ide_disk && hwif->led_act)
- blk_queue_activity_fn(q, hwif->led_act, drive);
-
return 0;
}
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index e2f4bb5..b3bcd1d 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -2573,11 +2573,11 @@ static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full,
int pages = tape->pages_per_stage;
char *b_data = NULL;
- if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
+ if ((stage = kmalloc(sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
return NULL;
stage->next = NULL;
- bh = stage->bh = (struct idetape_bh *)kmalloc(sizeof(struct idetape_bh), GFP_KERNEL);
+ bh = stage->bh = kmalloc(sizeof(struct idetape_bh), GFP_KERNEL);
if (bh == NULL)
goto abort;
bh->b_reqnext = NULL;
@@ -2607,7 +2607,7 @@ static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full,
continue;
}
prev_bh = bh;
- if ((bh = (struct idetape_bh *)kmalloc(sizeof(struct idetape_bh), GFP_KERNEL)) == NULL) {
+ if ((bh = kmalloc(sizeof(struct idetape_bh), GFP_KERNEL)) == NULL) {
free_page((unsigned long) b_data);
goto abort;
}
@@ -4860,7 +4860,7 @@ static int ide_tape_probe(ide_drive_t *drive)
printk(KERN_WARNING "ide-tape: Use drive %s with ide-scsi emulation and osst.\n", drive->name);
printk(KERN_WARNING "ide-tape: OnStream support will be removed soon from ide-tape!\n");
}
- tape = (idetape_tape_t *) kzalloc (sizeof (idetape_tape_t), GFP_KERNEL);
+ tape = kzalloc(sizeof (idetape_tape_t), GFP_KERNEL);
if (tape == NULL) {
printk(KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
goto failed;
diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
index e993a51..08119da 100644
--- a/drivers/ide/pci/hpt366.c
+++ b/drivers/ide/pci/hpt366.c
@@ -4,6 +4,7 @@
* Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org>
* Portions Copyright (C) 2001 Sun Microsystems, Inc.
* Portions Copyright (C) 2003 Red Hat Inc
+ * Portions Copyright (C) 2005-2006 MontaVista Software, Inc.
*
* Thanks to HighPoint Technologies for their assistance, and hardware.
* Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
@@ -11,9 +12,11 @@
* development and support.
*
*
- * Highpoint have their own driver (source except for the raid part)
- * available from http://www.highpoint-tech.com/hpt3xx-opensource-v131.tgz
- * This may be useful to anyone wanting to work on the mainstream hpt IDE.
+ * HighPoint has its own drivers (open source except for the RAID part)
+ * available from http://www.highpoint-tech.com/BIOS%20+%20Driver/.
+ * This may be useful to anyone wanting to work on this driver, however do not
+ * trust them too much since the code tends to become less and less meaningful
+ * as the time passes... :-/
*
* Note that final HPT370 support was done by force extraction of GPL.
*
@@ -52,6 +55,29 @@
* keeping me sane.
* Alan Cox <alan@redhat.com>
*
+ * - fix the clock turnaround code: it was writing to the wrong ports when
+ * called for the secondary channel, caching the current clock mode per-
+ * channel caused the cached register value to get out of sync with the
+ * actual one, the channels weren't serialized, the turnaround shouldn't
+ * be done on 66 MHz PCI bus
+ * - avoid calibrating PLL twice as the second time results in a wrong PCI
+ * frequency and thus in the wrong timings for the secondary channel
+ * - disable UltraATA/133 for HPT372 by default (50 MHz DPLL clock do not
+ * allow for this speed anyway)
+ * - add support for HPT302N and HPT371N clocking (the same as for HPT372N)
+ * - HPT371/N are single channel chips, so avoid touching the primary channel
+ * which exists only virtually (there's no pins for it)
+ * - fix/remove bad/unused timing tables and use one set of tables for the whole
+ * HPT37x chip family; save space by introducing the separate transfer mode
+ * table in which the mode lookup is done
+ * - use f_CNT value saved by the HighPoint BIOS as reading it directly gives
+ * the wrong PCI frequency since DPLL has already been calibrated by BIOS
+ * - fix the hotswap code: it caused RESET- to glitch when tristating the bus,
+ * and for HPT36x the obsolete HDIO_TRISTATE_HWIF handler was called instead
+ * - pass to init_chipset() handlers a copy of the IDE PCI device structure as
+ * they tamper with its fields
+ * <source@mvista.com>
+ *
*/
@@ -76,8 +102,8 @@
/* various tuning parameters */
#define HPT_RESET_STATE_ENGINE
-#undef HPT_DELAY_INTERRUPT
-#undef HPT_SERIALIZE_IO
+#undef HPT_DELAY_INTERRUPT
+#define HPT_SERIALIZE_IO 0
static const char *quirk_drives[] = {
"QUANTUM FIREBALLlct08 08",
@@ -141,305 +167,175 @@ static const char *bad_ata33[] = {
NULL
};
-struct chipset_bus_clock_list_entry {
- u8 xfer_speed;
- unsigned int chipset_settings;
+static u8 xfer_speeds[] = {
+ XFER_UDMA_6,
+ XFER_UDMA_5,
+ XFER_UDMA_4,
+ XFER_UDMA_3,
+ XFER_UDMA_2,
+ XFER_UDMA_1,
+ XFER_UDMA_0,
+
+ XFER_MW_DMA_2,
+ XFER_MW_DMA_1,
+ XFER_MW_DMA_0,
+
+ XFER_PIO_4,
+ XFER_PIO_3,
+ XFER_PIO_2,
+ XFER_PIO_1,
+ XFER_PIO_0
};
-/* key for bus clock timings
- * bit
- * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW
- * DMA. cycles = value + 1
- * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW
- * DMA. cycles = value + 1
- * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file
- * register access.
- * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file
- * register access.
- * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer.
- * during task file register access.
- * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA
- * xfer.
- * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task
- * register access.
- * 28 UDMA enable
- * 29 DMA enable
- * 30 PIO_MST enable. if set, the chip is in bus master mode during
- * PIO.
- * 31 FIFO enable.
+/* Key for bus clock timings
+ * 36x 37x
+ * bits bits
+ * 0:3 0:3 data_high_time. Inactive time of DIOW_/DIOR_ for PIO and MW DMA.
+ * cycles = value + 1
+ * 4:7 4:8 data_low_time. Active time of DIOW_/DIOR_ for PIO and MW DMA.
+ * cycles = value + 1
+ * 8:11 9:12 cmd_high_time. Inactive time of DIOW_/DIOR_ during task file
+ * register access.
+ * 12:15 13:17 cmd_low_time. Active time of DIOW_/DIOR_ during task file
+ * register access.
+ * 16:18 18:20 udma_cycle_time. Clock cycles for UDMA xfer.
+ * - 21 CLK frequency: 0=ATA clock, 1=dual ATA clock.
+ * 19:21 22:24 pre_high_time. Time to initialize the 1st cycle for PIO and
+ * MW DMA xfer.
+ * 22:24 25:27 cmd_pre_high_time. Time to initialize the 1st PIO cycle for
+ * task file register access.
+ * 28 28 UDMA enable.
+ * 29 29 DMA enable.
+ * 30 30 PIO MST enable. If set, the chip is in bus master mode during
+ * PIO xfer.
+ * 31 31 FIFO enable.
*/
-static struct chipset_bus_clock_list_entry forty_base_hpt366[] = {
- { XFER_UDMA_4, 0x900fd943 },
- { XFER_UDMA_3, 0x900ad943 },
- { XFER_UDMA_2, 0x900bd943 },
- { XFER_UDMA_1, 0x9008d943 },
- { XFER_UDMA_0, 0x9008d943 },
-
- { XFER_MW_DMA_2, 0xa008d943 },
- { XFER_MW_DMA_1, 0xa010d955 },
- { XFER_MW_DMA_0, 0xa010d9fc },
-
- { XFER_PIO_4, 0xc008d963 },
- { XFER_PIO_3, 0xc010d974 },
- { XFER_PIO_2, 0xc010d997 },
- { XFER_PIO_1, 0xc010d9c7 },
- { XFER_PIO_0, 0xc018d9d9 },
- { 0, 0x0120d9d9 }
-};
-
-static struct chipset_bus_clock_list_entry thirty_three_base_hpt366[] = {
- { XFER_UDMA_4, 0x90c9a731 },
- { XFER_UDMA_3, 0x90cfa731 },
- { XFER_UDMA_2, 0x90caa731 },
- { XFER_UDMA_1, 0x90cba731 },
- { XFER_UDMA_0, 0x90c8a731 },
-
- { XFER_MW_DMA_2, 0xa0c8a731 },
- { XFER_MW_DMA_1, 0xa0c8a732 }, /* 0xa0c8a733 */
- { XFER_MW_DMA_0, 0xa0c8a797 },
-
- { XFER_PIO_4, 0xc0c8a731 },
- { XFER_PIO_3, 0xc0c8a742 },
- { XFER_PIO_2, 0xc0d0a753 },
- { XFER_PIO_1, 0xc0d0a7a3 }, /* 0xc0d0a793 */
- { XFER_PIO_0, 0xc0d0a7aa }, /* 0xc0d0a7a7 */
- { 0, 0x0120a7a7 }
-};
-
-static struct chipset_bus_clock_list_entry twenty_five_base_hpt366[] = {
- { XFER_UDMA_4, 0x90c98521 },
- { XFER_UDMA_3, 0x90cf8521 },
- { XFER_UDMA_2, 0x90cf8521 },
- { XFER_UDMA_1, 0x90cb8521 },
- { XFER_UDMA_0, 0x90cb8521 },
-
- { XFER_MW_DMA_2, 0xa0ca8521 },
- { XFER_MW_DMA_1, 0xa0ca8532 },
- { XFER_MW_DMA_0, 0xa0ca8575 },
-
- { XFER_PIO_4, 0xc0ca8521 },
- { XFER_PIO_3, 0xc0ca8532 },
- { XFER_PIO_2, 0xc0ca8542 },
- { XFER_PIO_1, 0xc0d08572 },
- { XFER_PIO_0, 0xc0d08585 },
- { 0, 0x01208585 }
-};
-
-/* from highpoint documentation. these are old values */
-static struct chipset_bus_clock_list_entry thirty_three_base_hpt370[] = {
-/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */
- { XFER_UDMA_5, 0x16454e31 },
- { XFER_UDMA_4, 0x16454e31 },
- { XFER_UDMA_3, 0x166d4e31 },
- { XFER_UDMA_2, 0x16494e31 },
- { XFER_UDMA_1, 0x164d4e31 },
- { XFER_UDMA_0, 0x16514e31 },
-
- { XFER_MW_DMA_2, 0x26514e21 },
- { XFER_MW_DMA_1, 0x26514e33 },
- { XFER_MW_DMA_0, 0x26514e97 },
-
- { XFER_PIO_4, 0x06514e21 },
- { XFER_PIO_3, 0x06514e22 },
- { XFER_PIO_2, 0x06514e33 },
- { XFER_PIO_1, 0x06914e43 },
- { XFER_PIO_0, 0x06914e57 },
- { 0, 0x06514e57 }
-};
-
-static struct chipset_bus_clock_list_entry sixty_six_base_hpt370[] = {
- { XFER_UDMA_5, 0x14846231 },
- { XFER_UDMA_4, 0x14886231 },
- { XFER_UDMA_3, 0x148c6231 },
- { XFER_UDMA_2, 0x148c6231 },
- { XFER_UDMA_1, 0x14906231 },
- { XFER_UDMA_0, 0x14986231 },
-
- { XFER_MW_DMA_2, 0x26514e21 },
- { XFER_MW_DMA_1, 0x26514e33 },
- { XFER_MW_DMA_0, 0x26514e97 },
-
- { XFER_PIO_4, 0x06514e21 },
- { XFER_PIO_3, 0x06514e22 },
- { XFER_PIO_2, 0x06514e33 },
- { XFER_PIO_1, 0x06914e43 },
- { XFER_PIO_0, 0x06914e57 },
- { 0, 0x06514e57 }
-};
-
-/* these are the current (4 sep 2001) timings from highpoint */
-static struct chipset_bus_clock_list_entry thirty_three_base_hpt370a[] = {
- { XFER_UDMA_5, 0x12446231 },
- { XFER_UDMA_4, 0x12446231 },
- { XFER_UDMA_3, 0x126c6231 },
- { XFER_UDMA_2, 0x12486231 },
- { XFER_UDMA_1, 0x124c6233 },
- { XFER_UDMA_0, 0x12506297 },
-
- { XFER_MW_DMA_2, 0x22406c31 },
- { XFER_MW_DMA_1, 0x22406c33 },
- { XFER_MW_DMA_0, 0x22406c97 },
-
- { XFER_PIO_4, 0x06414e31 },
- { XFER_PIO_3, 0x06414e42 },
- { XFER_PIO_2, 0x06414e53 },
- { XFER_PIO_1, 0x06814e93 },
- { XFER_PIO_0, 0x06814ea7 },
- { 0, 0x06814ea7 }
-};
-
-/* 2x 33MHz timings */
-static struct chipset_bus_clock_list_entry sixty_six_base_hpt370a[] = {
- { XFER_UDMA_5, 0x1488e673 },
- { XFER_UDMA_4, 0x1488e673 },
- { XFER_UDMA_3, 0x1498e673 },
- { XFER_UDMA_2, 0x1490e673 },
- { XFER_UDMA_1, 0x1498e677 },
- { XFER_UDMA_0, 0x14a0e73f },
-
- { XFER_MW_DMA_2, 0x2480fa73 },
- { XFER_MW_DMA_1, 0x2480fa77 },
- { XFER_MW_DMA_0, 0x2480fb3f },
-
- { XFER_PIO_4, 0x0c82be73 },
- { XFER_PIO_3, 0x0c82be95 },
- { XFER_PIO_2, 0x0c82beb7 },
- { XFER_PIO_1, 0x0d02bf37 },
- { XFER_PIO_0, 0x0d02bf5f },
- { 0, 0x0d02bf5f }
-};
-static struct chipset_bus_clock_list_entry fifty_base_hpt370a[] = {
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x0ac1f48a }
+static u32 forty_base_hpt36x[] = {
+ /* XFER_UDMA_6 */ 0x900fd943,
+ /* XFER_UDMA_5 */ 0x900fd943,
+ /* XFER_UDMA_4 */ 0x900fd943,
+ /* XFER_UDMA_3 */ 0x900ad943,
+ /* XFER_UDMA_2 */ 0x900bd943,
+ /* XFER_UDMA_1 */ 0x9008d943,
+ /* XFER_UDMA_0 */ 0x9008d943,
+
+ /* XFER_MW_DMA_2 */ 0xa008d943,
+ /* XFER_MW_DMA_1 */ 0xa010d955,
+ /* XFER_MW_DMA_0 */ 0xa010d9fc,
+
+ /* XFER_PIO_4 */ 0xc008d963,
+ /* XFER_PIO_3 */ 0xc010d974,
+ /* XFER_PIO_2 */ 0xc010d997,
+ /* XFER_PIO_1 */ 0xc010d9c7,
+ /* XFER_PIO_0 */ 0xc018d9d9
};
-static struct chipset_bus_clock_list_entry thirty_three_base_hpt372[] = {
- { XFER_UDMA_6, 0x1c81dc62 },
- { XFER_UDMA_5, 0x1c6ddc62 },
- { XFER_UDMA_4, 0x1c8ddc62 },
- { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */
- { XFER_UDMA_2, 0x1c91dc62 },
- { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */
- { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */
-
- { XFER_MW_DMA_2, 0x2c829262 },
- { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */
- { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */
-
- { XFER_PIO_4, 0x0c829c62 },
- { XFER_PIO_3, 0x0c829c84 },
- { XFER_PIO_2, 0x0c829ca6 },
- { XFER_PIO_1, 0x0d029d26 },
- { XFER_PIO_0, 0x0d029d5e },
- { 0, 0x0d029d5e }
+static u32 thirty_three_base_hpt36x[] = {
+ /* XFER_UDMA_6 */ 0x90c9a731,
+ /* XFER_UDMA_5 */ 0x90c9a731,
+ /* XFER_UDMA_4 */ 0x90c9a731,
+ /* XFER_UDMA_3 */ 0x90cfa731,
+ /* XFER_UDMA_2 */ 0x90caa731,
+ /* XFER_UDMA_1 */ 0x90cba731,
+ /* XFER_UDMA_0 */ 0x90c8a731,
+
+ /* XFER_MW_DMA_2 */ 0xa0c8a731,
+ /* XFER_MW_DMA_1 */ 0xa0c8a732, /* 0xa0c8a733 */
+ /* XFER_MW_DMA_0 */ 0xa0c8a797,
+
+ /* XFER_PIO_4 */ 0xc0c8a731,
+ /* XFER_PIO_3 */ 0xc0c8a742,
+ /* XFER_PIO_2 */ 0xc0d0a753,
+ /* XFER_PIO_1 */ 0xc0d0a7a3, /* 0xc0d0a793 */
+ /* XFER_PIO_0 */ 0xc0d0a7aa /* 0xc0d0a7a7 */
};
-static struct chipset_bus_clock_list_entry fifty_base_hpt372[] = {
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x0a81f443 }
+static u32 twenty_five_base_hpt36x[] = {
+ /* XFER_UDMA_6 */ 0x90c98521,
+ /* XFER_UDMA_5 */ 0x90c98521,
+ /* XFER_UDMA_4 */ 0x90c98521,
+ /* XFER_UDMA_3 */ 0x90cf8521,
+ /* XFER_UDMA_2 */ 0x90cf8521,
+ /* XFER_UDMA_1 */ 0x90cb8521,
+ /* XFER_UDMA_0 */ 0x90cb8521,
+
+ /* XFER_MW_DMA_2 */ 0xa0ca8521,
+ /* XFER_MW_DMA_1 */ 0xa0ca8532,
+ /* XFER_MW_DMA_0 */ 0xa0ca8575,
+
+ /* XFER_PIO_4 */ 0xc0ca8521,
+ /* XFER_PIO_3 */ 0xc0ca8532,
+ /* XFER_PIO_2 */ 0xc0ca8542,
+ /* XFER_PIO_1 */ 0xc0d08572,
+ /* XFER_PIO_0 */ 0xc0d08585
};
-static struct chipset_bus_clock_list_entry sixty_six_base_hpt372[] = {
- { XFER_UDMA_6, 0x1c869c62 },
- { XFER_UDMA_5, 0x1cae9c62 },
- { XFER_UDMA_4, 0x1c8a9c62 },
- { XFER_UDMA_3, 0x1c8e9c62 },
- { XFER_UDMA_2, 0x1c929c62 },
- { XFER_UDMA_1, 0x1c9a9c62 },
- { XFER_UDMA_0, 0x1c829c62 },
-
- { XFER_MW_DMA_2, 0x2c829c62 },
- { XFER_MW_DMA_1, 0x2c829c66 },
- { XFER_MW_DMA_0, 0x2c829d2e },
-
- { XFER_PIO_4, 0x0c829c62 },
- { XFER_PIO_3, 0x0c829c84 },
- { XFER_PIO_2, 0x0c829ca6 },
- { XFER_PIO_1, 0x0d029d26 },
- { XFER_PIO_0, 0x0d029d5e },
- { 0, 0x0d029d26 }
+static u32 thirty_three_base_hpt37x[] = {
+ /* XFER_UDMA_6 */ 0x12446231, /* 0x12646231 ?? */
+ /* XFER_UDMA_5 */ 0x12446231,
+ /* XFER_UDMA_4 */ 0x12446231,
+ /* XFER_UDMA_3 */ 0x126c6231,
+ /* XFER_UDMA_2 */ 0x12486231,
+ /* XFER_UDMA_1 */ 0x124c6233,
+ /* XFER_UDMA_0 */ 0x12506297,
+
+ /* XFER_MW_DMA_2 */ 0x22406c31,
+ /* XFER_MW_DMA_1 */ 0x22406c33,
+ /* XFER_MW_DMA_0 */ 0x22406c97,
+
+ /* XFER_PIO_4 */ 0x06414e31,
+ /* XFER_PIO_3 */ 0x06414e42,
+ /* XFER_PIO_2 */ 0x06414e53,
+ /* XFER_PIO_1 */ 0x06814e93,
+ /* XFER_PIO_0 */ 0x06814ea7
};
-static struct chipset_bus_clock_list_entry thirty_three_base_hpt374[] = {
- { XFER_UDMA_6, 0x12808242 },
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x06814e93 }
+static u32 fifty_base_hpt37x[] = {
+ /* XFER_UDMA_6 */ 0x12848242,
+ /* XFER_UDMA_5 */ 0x12848242,
+ /* XFER_UDMA_4 */ 0x12ac8242,
+ /* XFER_UDMA_3 */ 0x128c8242,
+ /* XFER_UDMA_2 */ 0x120c8242,
+ /* XFER_UDMA_1 */ 0x12148254,
+ /* XFER_UDMA_0 */ 0x121882ea,
+
+ /* XFER_MW_DMA_2 */ 0x22808242,
+ /* XFER_MW_DMA_1 */ 0x22808254,
+ /* XFER_MW_DMA_0 */ 0x228082ea,
+
+ /* XFER_PIO_4 */ 0x0a81f442,
+ /* XFER_PIO_3 */ 0x0a81f443,
+ /* XFER_PIO_2 */ 0x0a81f454,
+ /* XFER_PIO_1 */ 0x0ac1f465,
+ /* XFER_PIO_0 */ 0x0ac1f48a
};
-/* FIXME: 50MHz timings for HPT374 */
-
-#if 0
-static struct chipset_bus_clock_list_entry sixty_six_base_hpt374[] = {
- { XFER_UDMA_6, 0x12406231 }, /* checkme */
- { XFER_UDMA_5, 0x12446231 }, /* 0x14846231 */
- { XFER_UDMA_4, 0x16814ea7 }, /* 0x14886231 */
- { XFER_UDMA_3, 0x16814ea7 }, /* 0x148c6231 */
- { XFER_UDMA_2, 0x16814ea7 }, /* 0x148c6231 */
- { XFER_UDMA_1, 0x16814ea7 }, /* 0x14906231 */
- { XFER_UDMA_0, 0x16814ea7 }, /* 0x14986231 */
- { XFER_MW_DMA_2, 0x16814ea7 }, /* 0x26514e21 */
- { XFER_MW_DMA_1, 0x16814ea7 }, /* 0x26514e97 */
- { XFER_MW_DMA_0, 0x16814ea7 }, /* 0x26514e97 */
- { XFER_PIO_4, 0x06814ea7 }, /* 0x06514e21 */
- { XFER_PIO_3, 0x06814ea7 }, /* 0x06514e22 */
- { XFER_PIO_2, 0x06814ea7 }, /* 0x06514e33 */
- { XFER_PIO_1, 0x06814ea7 }, /* 0x06914e43 */
- { XFER_PIO_0, 0x06814ea7 }, /* 0x06914e57 */
- { 0, 0x06814ea7 }
+static u32 sixty_six_base_hpt37x[] = {
+ /* XFER_UDMA_6 */ 0x1c869c62,
+ /* XFER_UDMA_5 */ 0x1cae9c62, /* 0x1c8a9c62 */
+ /* XFER_UDMA_4 */ 0x1c8a9c62,
+ /* XFER_UDMA_3 */ 0x1c8e9c62,
+ /* XFER_UDMA_2 */ 0x1c929c62,
+ /* XFER_UDMA_1 */ 0x1c9a9c62,
+ /* XFER_UDMA_0 */ 0x1c829c62,
+
+ /* XFER_MW_DMA_2 */ 0x2c829c62,
+ /* XFER_MW_DMA_1 */ 0x2c829c66,
+ /* XFER_MW_DMA_0 */ 0x2c829d2e,
+
+ /* XFER_PIO_4 */ 0x0c829c62,
+ /* XFER_PIO_3 */ 0x0c829c84,
+ /* XFER_PIO_2 */ 0x0c829ca6,
+ /* XFER_PIO_1 */ 0x0d029d26,
+ /* XFER_PIO_0 */ 0x0d029d5e
};
-#endif
#define HPT366_DEBUG_DRIVE_INFO 0
#define HPT374_ALLOW_ATA133_6 0
#define HPT371_ALLOW_ATA133_6 0
#define HPT302_ALLOW_ATA133_6 0
-#define HPT372_ALLOW_ATA133_6 1
+#define HPT372_ALLOW_ATA133_6 0
#define HPT370_ALLOW_ATA100_5 1
#define HPT366_ALLOW_ATA66_4 1
#define HPT366_ALLOW_ATA66_3 1
@@ -461,9 +357,10 @@ struct hpt_info
int revision; /* Chipset revision */
int flags; /* Chipset properties */
#define PLL_MODE 1
-#define IS_372N 2
+#define IS_3xxN 2
+#define PCI_66MHZ 4
/* Speed table */
- struct chipset_bus_clock_list_entry *speed;
+ u32 *speed;
};
/*
@@ -600,12 +497,20 @@ static int check_in_drive_lists (ide_drive_t *drive, const char **list)
return 0;
}
-static unsigned int pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
+static u32 pci_bus_clock_list(u8 speed, u32 *chipset_table)
{
- for ( ; chipset_table->xfer_speed ; chipset_table++)
- if (chipset_table->xfer_speed == speed)
- return chipset_table->chipset_settings;
- return chipset_table->chipset_settings;
+ int i;
+
+ /*
+ * Lookup the transfer mode table to get the index into
+ * the timing table.
+ *
+ * NOTE: For XFER_PIO_SLOW, PIO mode 0 timings will be used.
+ */
+ for (i = 0; i < ARRAY_SIZE(xfer_speeds) - 1; i++)
+ if (xfer_speeds[i] == speed)
+ break;
+ return chipset_table[i];
}
static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed)
@@ -956,156 +861,127 @@ static int hpt374_ide_dma_end (ide_drive_t *drive)
}
/**
- * hpt372n_set_clock - perform clock switching dance
- * @drive: Drive to switch
- * @mode: Switching mode (0x21 for write, 0x23 otherwise)
+ * hpt3xxn_set_clock - perform clock switching dance
+ * @hwif: hwif to switch
+ * @mode: clocking mode (0x21 for write, 0x23 otherwise)
*
- * Switch the DPLL clock on the HPT372N devices. This is a
- * right mess.
+ * Switch the DPLL clock on the HPT3xxN devices. This is a right mess.
+ * NOTE: avoid touching the disabled primary channel on HPT371N -- it
+ * doesn't physically exist anyway...
*/
-
-static void hpt372n_set_clock(ide_drive_t *drive, int mode)
+
+static void hpt3xxn_set_clock(ide_hwif_t *hwif, u8 mode)
{
- ide_hwif_t *hwif = HWIF(drive);
-
- /* FIXME: should we check for DMA active and BUG() */
+ u8 mcr1, scr2 = hwif->INB(hwif->dma_master + 0x7b);
+
+ if ((scr2 & 0x7f) == mode)
+ return;
+
+ /* MISC. control register 1 has the channel enable bit... */
+ mcr1 = hwif->INB(hwif->dma_master + 0x70);
+
/* Tristate the bus */
- outb(0x80, hwif->dma_base+0x73);
- outb(0x80, hwif->dma_base+0x77);
-
+ if (mcr1 & 0x04)
+ hwif->OUTB(0x80, hwif->dma_master + 0x73);
+ hwif->OUTB(0x80, hwif->dma_master + 0x77);
+
/* Switch clock and reset channels */
- outb(mode, hwif->dma_base+0x7B);
- outb(0xC0, hwif->dma_base+0x79);
-
+ hwif->OUTB(mode, hwif->dma_master + 0x7b);
+ hwif->OUTB(0xc0, hwif->dma_master + 0x79);
+
/* Reset state machines */
- outb(0x37, hwif->dma_base+0x70);
- outb(0x37, hwif->dma_base+0x74);
-
+ if (mcr1 & 0x04)
+ hwif->OUTB(0x37, hwif->dma_master + 0x70);
+ hwif->OUTB(0x37, hwif->dma_master + 0x74);
+
/* Complete reset */
- outb(0x00, hwif->dma_base+0x79);
-
+ hwif->OUTB(0x00, hwif->dma_master + 0x79);
+
/* Reconnect channels to bus */
- outb(0x00, hwif->dma_base+0x73);
- outb(0x00, hwif->dma_base+0x77);
+ if (mcr1 & 0x04)
+ hwif->OUTB(0x00, hwif->dma_master + 0x73);
+ hwif->OUTB(0x00, hwif->dma_master + 0x77);
}
/**
- * hpt372n_rw_disk - prepare for I/O
+ * hpt3xxn_rw_disk - prepare for I/O
* @drive: drive for command
* @rq: block request structure
*
- * This is called when a disk I/O is issued to the 372N.
+ * This is called when a disk I/O is issued to HPT3xxN.
* We need it because of the clock switching.
*/
-static void hpt372n_rw_disk(ide_drive_t *drive, struct request *rq)
-{
- ide_hwif_t *hwif = drive->hwif;
- int wantclock;
-
- wantclock = rq_data_dir(rq) ? 0x23 : 0x21;
-
- if (hwif->config_data != wantclock) {
- hpt372n_set_clock(drive, wantclock);
- hwif->config_data = wantclock;
- }
-}
-
-/*
- * Since SUN Cobalt is attempting to do this operation, I should disclose
- * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date
- * HOTSWAP ATA Infrastructure.
- */
-
-static void hpt3xx_reset (ide_drive_t *drive)
-{
-}
-
-static int hpt3xx_tristate (ide_drive_t * drive, int state)
+static void hpt3xxn_rw_disk(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
- struct pci_dev *dev = hwif->pci_dev;
- u8 reg59h = 0, reset = (hwif->channel) ? 0x80 : 0x40;
- u8 regXXh = 0, state_reg= (hwif->channel) ? 0x57 : 0x53;
-
- pci_read_config_byte(dev, 0x59, &reg59h);
- pci_read_config_byte(dev, state_reg, &regXXh);
+ u8 wantclock = rq_data_dir(rq) ? 0x23 : 0x21;
- if (state) {
- (void) ide_do_reset(drive);
- pci_write_config_byte(dev, state_reg, regXXh|0x80);
- pci_write_config_byte(dev, 0x59, reg59h|reset);
- } else {
- pci_write_config_byte(dev, 0x59, reg59h & ~(reset));
- pci_write_config_byte(dev, state_reg, regXXh & ~(0x80));
- (void) ide_do_reset(drive);
- }
- return 0;
+ hpt3xxn_set_clock(hwif, wantclock);
}
/*
- * set/get power state for a drive.
- * turning the power off does the following things:
- * 1) soft-reset the drive
- * 2) tri-states the ide bus
+ * Set/get power state for a drive.
*
- * when we turn things back on, we need to re-initialize things.
+ * When we turn the power back on, we need to re-initialize things.
*/
#define TRISTATE_BIT 0x8000
-static int hpt370_busproc(ide_drive_t * drive, int state)
+
+static int hpt3xx_busproc(ide_drive_t *drive, int state)
{
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = hwif->pci_dev;
- u8 tristate = 0, resetmask = 0, bus_reg = 0;
- u16 tri_reg;
+ u8 tristate, resetmask, bus_reg = 0;
+ u16 tri_reg = 0;
hwif->bus_state = state;
if (hwif->channel) {
/* secondary channel */
- tristate = 0x56;
- resetmask = 0x80;
+ tristate = 0x56;
+ resetmask = 0x80;
} else {
/* primary channel */
- tristate = 0x52;
+ tristate = 0x52;
resetmask = 0x40;
}
- /* grab status */
+ /* Grab the status. */
pci_read_config_word(dev, tristate, &tri_reg);
pci_read_config_byte(dev, 0x59, &bus_reg);
- /* set the state. we don't set it if we don't need to do so.
- * make sure that the drive knows that it has failed if it's off */
+ /*
+ * Set the state. We don't set it if we don't need to do so.
+ * Make sure that the drive knows that it has failed if it's off.
+ */
switch (state) {
case BUSSTATE_ON:
- hwif->drives[0].failures = 0;
- hwif->drives[1].failures = 0;
- if ((bus_reg & resetmask) == 0)
+ if (!(bus_reg & resetmask))
return 0;
- tri_reg &= ~TRISTATE_BIT;
- bus_reg &= ~resetmask;
- break;
+ hwif->drives[0].failures = hwif->drives[1].failures = 0;
+
+ pci_write_config_byte(dev, 0x59, bus_reg & ~resetmask);
+ pci_write_config_word(dev, tristate, tri_reg & ~TRISTATE_BIT);
+ return 0;
case BUSSTATE_OFF:
- hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
- hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
- if ((tri_reg & TRISTATE_BIT) == 0 && (bus_reg & resetmask))
+ if ((bus_reg & resetmask) && !(tri_reg & TRISTATE_BIT))
return 0;
tri_reg &= ~TRISTATE_BIT;
- bus_reg |= resetmask;
break;
case BUSSTATE_TRISTATE:
- hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
- hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
- if ((tri_reg & TRISTATE_BIT) && (bus_reg & resetmask))
+ if ((bus_reg & resetmask) && (tri_reg & TRISTATE_BIT))
return 0;
tri_reg |= TRISTATE_BIT;
- bus_reg |= resetmask;
break;
+ default:
+ return -EINVAL;
}
- pci_write_config_byte(dev, 0x59, bus_reg);
- pci_write_config_word(dev, tristate, tri_reg);
+ hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
+ hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
+
+ pci_write_config_word(dev, tristate, tri_reg);
+ pci_write_config_byte(dev, 0x59, bus_reg | resetmask);
return 0;
}
@@ -1119,14 +995,14 @@ static void __devinit hpt366_clocking(ide_hwif_t *hwif)
/* detect bus speed by looking at control reg timing: */
switch((reg1 >> 8) & 7) {
case 5:
- info->speed = forty_base_hpt366;
+ info->speed = forty_base_hpt36x;
break;
case 9:
- info->speed = twenty_five_base_hpt366;
+ info->speed = twenty_five_base_hpt36x;
break;
case 7:
default:
- info->speed = thirty_three_base_hpt366;
+ info->speed = thirty_three_base_hpt36x;
break;
}
}
@@ -1136,9 +1012,9 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
struct hpt_info *info = ide_get_hwifdata(hwif);
struct pci_dev *dev = hwif->pci_dev;
int adjust, i;
- u16 freq;
- u32 pll;
- u8 reg5bh;
+ u16 freq = 0;
+ u32 pll, temp = 0;
+ u8 reg5bh = 0, mcr1 = 0;
/*
* default to pci clock. make sure MA15/16 are set to output
@@ -1151,27 +1027,40 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
pci_write_config_byte(dev, 0x5b, 0x23);
/*
- * set up the PLL. we need to adjust it so that it's stable.
- * freq = Tpll * 192 / Tpci
+ * We'll have to read f_CNT value in order to determine
+ * the PCI clock frequency according to the following ratio:
+ *
+ * f_CNT = Fpci * 192 / Fdpll
+ *
+ * First try reading the register in which the HighPoint BIOS
+ * saves f_CNT value before reprogramming the DPLL from its
+ * default setting (which differs for the various chips).
+ * NOTE: This register is only accessible via I/O space.
*
- * Todo. For non x86 should probably check the dword is
- * set to 0xABCDExxx indicating the BIOS saved f_CNT
+ * In case the signature check fails, we'll have to resort to
+ * reading the f_CNT register itself in hopes that nobody has
+ * touched the DPLL yet...
*/
- pci_read_config_word(dev, 0x78, &freq);
- freq &= 0x1FF;
-
+ temp = inl(pci_resource_start(dev, 4) + 0x90);
+ if ((temp & 0xFFFFF000) != 0xABCDE000) {
+ printk(KERN_WARNING "HPT37X: no clock data saved by BIOS\n");
+
+ /* Calculate the average value of f_CNT */
+ for (temp = i = 0; i < 128; i++) {
+ pci_read_config_word(dev, 0x78, &freq);
+ temp += freq & 0x1ff;
+ mdelay(1);
+ }
+ freq = temp / 128;
+ } else
+ freq = temp & 0x1ff;
+
/*
- * The 372N uses different PCI clock information and has
- * some other complications
- * On PCI33 timing we must clock switch
- * On PCI66 timing we must NOT use the PCI clock
- *
- * Currently we always set up the PLL for the 372N
+ * HPT3xxN chips use different PCI clock information.
+ * Currently we always set up the PLL for them.
*/
-
- if(info->flags & IS_372N)
- {
- printk(KERN_INFO "hpt: HPT372N detected, using 372N timing.\n");
+
+ if (info->flags & IS_3xxN) {
if(freq < 0x55)
pll = F_LOW_PCI_33;
else if(freq < 0x70)
@@ -1180,10 +1069,8 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
pll = F_LOW_PCI_50;
else
pll = F_LOW_PCI_66;
-
- printk(KERN_INFO "FREQ: %d PLL: %d\n", freq, pll);
-
- /* We always use the pll not the PCI clock on 372N */
+
+ printk(KERN_INFO "HPT3xxN detected, FREQ: %d, PLL: %d\n", freq, pll);
}
else
{
@@ -1197,41 +1084,22 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
pll = F_LOW_PCI_66;
if (pll == F_LOW_PCI_33) {
- if (info->revision >= 8)
- info->speed = thirty_three_base_hpt374;
- else if (info->revision >= 5)
- info->speed = thirty_three_base_hpt372;
- else if (info->revision >= 4)
- info->speed = thirty_three_base_hpt370a;
- else
- info->speed = thirty_three_base_hpt370;
+ info->speed = thirty_three_base_hpt37x;
printk(KERN_DEBUG "HPT37X: using 33MHz PCI clock\n");
} else if (pll == F_LOW_PCI_40) {
/* Unsupported */
} else if (pll == F_LOW_PCI_50) {
- if (info->revision >= 8)
- info->speed = fifty_base_hpt370a;
- else if (info->revision >= 5)
- info->speed = fifty_base_hpt372;
- else if (info->revision >= 4)
- info->speed = fifty_base_hpt370a;
- else
- info->speed = fifty_base_hpt370a;
+ info->speed = fifty_base_hpt37x;
printk(KERN_DEBUG "HPT37X: using 50MHz PCI clock\n");
} else {
- if (info->revision >= 8) {
- printk(KERN_ERR "HPT37x: 66MHz timings are not supported.\n");
- }
- else if (info->revision >= 5)
- info->speed = sixty_six_base_hpt372;
- else if (info->revision >= 4)
- info->speed = sixty_six_base_hpt370a;
- else
- info->speed = sixty_six_base_hpt370;
+ info->speed = sixty_six_base_hpt37x;
printk(KERN_DEBUG "HPT37X: using 66MHz PCI clock\n");
}
}
-
+
+ if (pll == F_LOW_PCI_66)
+ info->flags |= PCI_66MHZ;
+
/*
* only try the pll if we don't have a table for the clock
* speed that we're running at. NOTE: the internal PLL will
@@ -1248,11 +1116,8 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
info->flags |= PLL_MODE;
/*
- * FIXME: make this work correctly, esp with 372N as per
- * reference driver code.
- *
- * adjust PLL based upon PCI clock, enable it, and wait for
- * stabilization.
+ * Adjust the PLL based upon the PCI clock, enable it, and
+ * wait for stabilization...
*/
adjust = 0;
freq = (pll < F_LOW_PCI_50) ? 2 : 4;
@@ -1275,22 +1140,12 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
pci_write_config_dword(dev, 0x5c,
pll & ~0x100);
pci_write_config_byte(dev, 0x5b, 0x21);
- if (info->revision >= 8)
- info->speed = fifty_base_hpt370a;
- else if (info->revision >= 5)
- info->speed = fifty_base_hpt372;
- else if (info->revision >= 4)
- info->speed = fifty_base_hpt370a;
- else
- info->speed = fifty_base_hpt370a;
+
+ info->speed = fifty_base_hpt37x;
printk("HPT37X: using 50MHz internal PLL\n");
goto init_hpt37X_done;
}
}
- if (!pci_get_drvdata(dev)) {
- printk("No Clock Stabilization!!!\n");
- return;
- }
pll_recal:
if (adjust & 1)
pll -= (adjust >> 1);
@@ -1300,11 +1155,16 @@ pll_recal:
init_hpt37X_done:
if (!info->speed)
- printk(KERN_ERR "HPT37X%s: unknown bus timing [%d %d].\n",
- (info->flags & IS_372N)?"N":"", pll, freq);
- /* reset state engine */
- pci_write_config_byte(dev, 0x50, 0x37);
- pci_write_config_byte(dev, 0x54, 0x37);
+ printk(KERN_ERR "HPT37x%s: unknown bus timing [%d %d].\n",
+ (info->flags & IS_3xxN) ? "N" : "", pll, freq);
+ /*
+ * Reset the state engines.
+ * NOTE: avoid accidentally enabling the primary channel on HPT371N.
+ */
+ pci_read_config_byte(dev, 0x50, &mcr1);
+ if (mcr1 & 0x04)
+ pci_write_config_byte(dev, 0x50, 0x37);
+ pci_write_config_byte(dev, 0x54, 0x37);
udelay(100);
}
@@ -1367,6 +1227,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
struct pci_dev *dev = hwif->pci_dev;
struct hpt_info *info = ide_get_hwifdata(hwif);
u8 ata66 = 0, regmask = (hwif->channel) ? 0x01 : 0x02;
+ int serialize = HPT_SERIALIZE_IO;
hwif->tuneproc = &hpt3xx_tune_drive;
hwif->speedproc = &hpt3xx_tune_chipset;
@@ -1374,8 +1235,20 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
hwif->intrproc = &hpt3xx_intrproc;
hwif->maskproc = &hpt3xx_maskproc;
- if(info->flags & IS_372N)
- hwif->rw_disk = &hpt372n_rw_disk;
+ /*
+ * HPT3xxN chips have some complications:
+ *
+ * - on 33 MHz PCI we must clock switch
+ * - on 66 MHz PCI we must NOT use the PCI clock
+ */
+ if ((info->flags & (IS_3xxN | PCI_66MHZ)) == IS_3xxN) {
+ /*
+ * Clock is shared between the channels,
+ * so we'll have to serialize them... :-(
+ */
+ serialize = 1;
+ hwif->rw_disk = &hpt3xxn_rw_disk;
+ }
/*
* The HPT37x uses the CBLID pins as outputs for MA15/MA16
@@ -1418,29 +1291,15 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
PCI_FUNC(hwif->pci_dev->devfn));
#endif /* DEBUG */
-#ifdef HPT_SERIALIZE_IO
- /* serialize access to this device */
- if (hwif->mate)
+ /* Serialize access to this device */
+ if (serialize && hwif->mate)
hwif->serialized = hwif->mate->serialized = 1;
-#endif
- if (info->revision >= 3) {
- u8 reg5ah = 0;
- pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10);
- /*
- * set up ioctl for power status.
- * note: power affects both
- * drives on each channel
- */
- hwif->resetproc = &hpt3xx_reset;
- hwif->busproc = &hpt370_busproc;
- } else if (info->revision >= 2) {
- hwif->resetproc = &hpt3xx_reset;
- hwif->busproc = &hpt3xx_tristate;
- } else {
- hwif->resetproc = &hpt3xx_reset;
- hwif->busproc = &hpt3xx_tristate;
- }
+ /*
+ * Set up ioctl for power status.
+ * NOTE: power affects both drives on each channel.
+ */
+ hwif->busproc = &hpt3xx_busproc;
if (!hwif->dma_base) {
hwif->drives[0].autotune = 1;
@@ -1490,7 +1349,7 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase)
return;
if(info->speed == NULL) {
- printk(KERN_WARNING "hpt: no known IDE timings, disabling DMA.\n");
+ printk(KERN_WARNING "hpt366: no known IDE timings, disabling DMA.\n");
return;
}
@@ -1519,9 +1378,10 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase)
static void __devinit init_iops_hpt366(ide_hwif_t *hwif)
{
- struct hpt_info *info = kzalloc(sizeof(struct hpt_info), GFP_KERNEL);
- unsigned long dmabase = pci_resource_start(hwif->pci_dev, 4);
- u8 did, rid;
+ struct hpt_info *info = kzalloc(sizeof(struct hpt_info), GFP_KERNEL);
+ struct pci_dev *dev = hwif->pci_dev;
+ u16 did = dev->device;
+ u8 rid = 0;
if(info == NULL) {
printk(KERN_WARNING "hpt366: out of memory.\n");
@@ -1529,15 +1389,22 @@ static void __devinit init_iops_hpt366(ide_hwif_t *hwif)
}
ide_set_hwifdata(hwif, info);
- if(dmabase) {
- did = inb(dmabase + 0x22);
- rid = inb(dmabase + 0x28);
-
- if((did == 4 && rid == 6) || (did == 5 && rid > 1))
- info->flags |= IS_372N;
+ /* Avoid doing the same thing twice. */
+ if (hwif->channel && hwif->mate) {
+ memcpy(info, ide_get_hwifdata(hwif->mate), sizeof(struct hpt_info));
+ return;
}
- info->revision = hpt_revision(hwif->pci_dev);
+ pci_read_config_byte(dev, PCI_CLASS_REVISION, &rid);
+
+ if (( did == PCI_DEVICE_ID_TTI_HPT366 && rid == 6) ||
+ ((did == PCI_DEVICE_ID_TTI_HPT372 ||
+ did == PCI_DEVICE_ID_TTI_HPT302 ||
+ did == PCI_DEVICE_ID_TTI_HPT371) && rid > 1) ||
+ did == PCI_DEVICE_ID_TTI_HPT372N)
+ info->flags |= IS_3xxN;
+
+ info->revision = hpt_revision(dev);
if (info->revision >= 3)
hpt37x_clocking(hwif);
@@ -1574,6 +1441,23 @@ static int __devinit init_setup_hpt37x(struct pci_dev *dev, ide_pci_device_t *d)
return ide_setup_pci_device(dev, d);
}
+static int __devinit init_setup_hpt371(struct pci_dev *dev, ide_pci_device_t *d)
+{
+ u8 mcr1 = 0;
+
+ /*
+ * HPT371 chips physically have only one channel, the secondary one,
+ * but the primary channel registers do exist! Go figure...
+ * So, we manually disable the non-existing channel here
+ * (if the BIOS hasn't done this already).
+ */
+ pci_read_config_byte(dev, 0x50, &mcr1);
+ if (mcr1 & 0x04)
+ pci_write_config_byte(dev, 0x50, (mcr1 & ~0x04));
+
+ return ide_setup_pci_device(dev, d);
+}
+
static int __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d)
{
struct pci_dev *findev = NULL;
@@ -1661,13 +1545,14 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = {
.bootable = OFF_BOARD,
},{ /* 3 */
.name = "HPT371",
- .init_setup = init_setup_hpt37x,
+ .init_setup = init_setup_hpt371,
.init_chipset = init_chipset_hpt366,
.init_iops = init_iops_hpt366,
.init_hwif = init_hwif_hpt366,
.init_dma = init_dma_hpt366,
.channels = 2,
.autodma = AUTODMA,
+ .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.bootable = OFF_BOARD,
},{ /* 4 */
.name = "HPT374",
@@ -1699,13 +1584,16 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = {
*
* Called when the PCI registration layer (or the IDE initialization)
* finds a device matching our IDE device tables.
+ *
+ * NOTE: since we'll have to modify some fields of the ide_pci_device_t
+ * structure depending on the chip's revision, we'd better pass a local
+ * copy down the call chain...
*/
-
static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- ide_pci_device_t *d = &hpt366_chipsets[id->driver_data];
+ ide_pci_device_t d = hpt366_chipsets[id->driver_data];
- return d->init_setup(dev, d);
+ return d.init_setup(dev, &d);
}
static struct pci_device_id hpt366_pci_tbl[] = {
diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c
index 3ca5810..7cb4857 100644
--- a/drivers/ide/pci/pdc202xx_new.c
+++ b/drivers/ide/pci/pdc202xx_new.c
@@ -39,6 +39,14 @@
#define PDC202_DEBUG_CABLE 0
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk("%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DBG(fmt, args...)
+#endif
+
static const char *pdc_quirk_drives[] = {
"QUANTUM FIREBALLlct08 08",
"QUANTUM FIREBALLP KA6.4",
@@ -51,37 +59,11 @@ static const char *pdc_quirk_drives[] = {
NULL
};
-#define set_2regs(a, b) \
- do { \
- hwif->OUTB((a + adj), indexreg); \
- hwif->OUTB(b, datareg); \
- } while(0)
-
-#define set_ultra(a, b, c) \
- do { \
- set_2regs(0x10,(a)); \
- set_2regs(0x11,(b)); \
- set_2regs(0x12,(c)); \
- } while(0)
-
-#define set_ata2(a, b) \
- do { \
- set_2regs(0x0e,(a)); \
- set_2regs(0x0f,(b)); \
- } while(0)
-
-#define set_pio(a, b, c) \
- do { \
- set_2regs(0x0c,(a)); \
- set_2regs(0x0d,(b)); \
- set_2regs(0x13,(c)); \
- } while(0)
-
-static u8 pdcnew_ratemask (ide_drive_t *drive)
+static u8 max_dma_rate(struct pci_dev *pdev)
{
u8 mode;
- switch(HWIF(drive)->pci_dev->device) {
+ switch(pdev->device) {
case PCI_DEVICE_ID_PROMISE_20277:
case PCI_DEVICE_ID_PROMISE_20276:
case PCI_DEVICE_ID_PROMISE_20275:
@@ -96,12 +78,21 @@ static u8 pdcnew_ratemask (ide_drive_t *drive)
default:
return 0;
}
- if (!eighty_ninty_three(drive))
- mode = min(mode, (u8)1);
+
return mode;
}
-static int check_in_drive_lists (ide_drive_t *drive, const char **list)
+static u8 pdcnew_ratemask(ide_drive_t *drive)
+{
+ u8 mode = max_dma_rate(HWIF(drive)->pci_dev);
+
+ if (!eighty_ninty_three(drive))
+ mode = min_t(u8, mode, 1);
+
+ return mode;
+}
+
+static int check_in_drive_lists(ide_drive_t *drive, const char **list)
{
struct hd_driveid *id = drive->id;
@@ -121,43 +112,141 @@ static int check_in_drive_lists (ide_drive_t *drive, const char **list)
return 0;
}
-static int pdcnew_new_tune_chipset (ide_drive_t *drive, u8 xferspeed)
+/**
+ * get_indexed_reg - Get indexed register
+ * @hwif: for the port address
+ * @index: index of the indexed register
+ */
+static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index)
+{
+ u8 value;
+
+ hwif->OUTB(index, hwif->dma_vendor1);
+ value = hwif->INB(hwif->dma_vendor3);
+
+ DBG("index[%02X] value[%02X]\n", index, value);
+ return value;
+}
+
+/**
+ * set_indexed_reg - Set indexed register
+ * @hwif: for the port address
+ * @index: index of the indexed register
+ */
+static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value)
+{
+ hwif->OUTB(index, hwif->dma_vendor1);
+ hwif->OUTB(value, hwif->dma_vendor3);
+ DBG("index[%02X] value[%02X]\n", index, value);
+}
+
+/*
+ * ATA Timing Tables based on 133 MHz PLL output clock.
+ *
+ * If the PLL outputs 100 MHz clock, the ASIC hardware will set
+ * the timing registers automatically when "set features" command is
+ * issued to the device. However, if the PLL output clock is 133 MHz,
+ * the following tables must be used.
+ */
+static struct pio_timing {
+ u8 reg0c, reg0d, reg13;
+} pio_timings [] = {
+ { 0xfb, 0x2b, 0xac }, /* PIO mode 0, IORDY off, Prefetch off */
+ { 0x46, 0x29, 0xa4 }, /* PIO mode 1, IORDY off, Prefetch off */
+ { 0x23, 0x26, 0x64 }, /* PIO mode 2, IORDY off, Prefetch off */
+ { 0x27, 0x0d, 0x35 }, /* PIO mode 3, IORDY on, Prefetch off */
+ { 0x23, 0x09, 0x25 }, /* PIO mode 4, IORDY on, Prefetch off */
+};
+
+static struct mwdma_timing {
+ u8 reg0e, reg0f;
+} mwdma_timings [] = {
+ { 0xdf, 0x5f }, /* MWDMA mode 0 */
+ { 0x6b, 0x27 }, /* MWDMA mode 1 */
+ { 0x69, 0x25 }, /* MWDMA mode 2 */
+};
+
+static struct udma_timing {
+ u8 reg10, reg11, reg12;
+} udma_timings [] = {
+ { 0x4a, 0x0f, 0xd5 }, /* UDMA mode 0 */
+ { 0x3a, 0x0a, 0xd0 }, /* UDMA mode 1 */
+ { 0x2a, 0x07, 0xcd }, /* UDMA mode 2 */
+ { 0x1a, 0x05, 0xcd }, /* UDMA mode 3 */
+ { 0x1a, 0x03, 0xcd }, /* UDMA mode 4 */
+ { 0x1a, 0x02, 0xcb }, /* UDMA mode 5 */
+ { 0x1a, 0x01, 0xcb }, /* UDMA mode 6 */
+};
+
+static int pdcnew_tune_chipset(ide_drive_t *drive, u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned long indexreg = hwif->dma_vendor1;
- unsigned long datareg = hwif->dma_vendor3;
- u8 thold = 0x10;
- u8 adj = (drive->dn%2) ? 0x08 : 0x00;
- u8 speed = ide_rate_filter(pdcnew_ratemask(drive), xferspeed);
-
- if (speed == XFER_UDMA_2) {
- hwif->OUTB((thold + adj), indexreg);
- hwif->OUTB((hwif->INB(datareg) & 0x7f), datareg);
- }
+ u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
+ int err;
- switch (speed) {
- case XFER_UDMA_7:
- speed = XFER_UDMA_6;
- case XFER_UDMA_6: set_ultra(0x1a, 0x01, 0xcb); break;
- case XFER_UDMA_5: set_ultra(0x1a, 0x02, 0xcb); break;
- case XFER_UDMA_4: set_ultra(0x1a, 0x03, 0xcd); break;
- case XFER_UDMA_3: set_ultra(0x1a, 0x05, 0xcd); break;
- case XFER_UDMA_2: set_ultra(0x2a, 0x07, 0xcd); break;
- case XFER_UDMA_1: set_ultra(0x3a, 0x0a, 0xd0); break;
- case XFER_UDMA_0: set_ultra(0x4a, 0x0f, 0xd5); break;
- case XFER_MW_DMA_2: set_ata2(0x69, 0x25); break;
- case XFER_MW_DMA_1: set_ata2(0x6b, 0x27); break;
- case XFER_MW_DMA_0: set_ata2(0xdf, 0x5f); break;
- case XFER_PIO_4: set_pio(0x23, 0x09, 0x25); break;
- case XFER_PIO_3: set_pio(0x27, 0x0d, 0x35); break;
- case XFER_PIO_2: set_pio(0x23, 0x26, 0x64); break;
- case XFER_PIO_1: set_pio(0x46, 0x29, 0xa4); break;
- case XFER_PIO_0: set_pio(0xfb, 0x2b, 0xac); break;
- default:
- ;
- }
+ speed = ide_rate_filter(pdcnew_ratemask(drive), speed);
+
+ /*
+ * Issue SETFEATURES_XFER to the drive first. PDC202xx hardware will
+ * automatically set the timing registers based on 100 MHz PLL output.
+ */
+ err = ide_config_drive_speed(drive, speed);
+
+ /*
+ * As we set up the PLL to output 133 MHz for UltraDMA/133 capable
+ * chips, we must override the default register settings...
+ */
+ if (max_dma_rate(hwif->pci_dev) == 4) {
+ u8 mode = speed & 0x07;
+
+ switch (speed) {
+ case XFER_UDMA_6:
+ case XFER_UDMA_5:
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ set_indexed_reg(hwif, 0x10 + adj,
+ udma_timings[mode].reg10);
+ set_indexed_reg(hwif, 0x11 + adj,
+ udma_timings[mode].reg11);
+ set_indexed_reg(hwif, 0x12 + adj,
+ udma_timings[mode].reg12);
+ break;
+
+ case XFER_MW_DMA_2:
+ case XFER_MW_DMA_1:
+ case XFER_MW_DMA_0:
+ set_indexed_reg(hwif, 0x0e + adj,
+ mwdma_timings[mode].reg0e);
+ set_indexed_reg(hwif, 0x0f + adj,
+ mwdma_timings[mode].reg0f);
+ break;
+ case XFER_PIO_4:
+ case XFER_PIO_3:
+ case XFER_PIO_2:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ set_indexed_reg(hwif, 0x0c + adj,
+ pio_timings[mode].reg0c);
+ set_indexed_reg(hwif, 0x0d + adj,
+ pio_timings[mode].reg0d);
+ set_indexed_reg(hwif, 0x13 + adj,
+ pio_timings[mode].reg13);
+ break;
+ default:
+ printk(KERN_ERR "pdc202xx_new: "
+ "Unknown speed %d ignored\n", speed);
+ }
+ } else if (speed == XFER_UDMA_2) {
+ /* Set tHOLD bit to 0 if using UDMA mode 2 */
+ u8 tmp = get_indexed_reg(hwif, 0x10 + adj);
- return (ide_config_drive_speed(drive, speed));
+ set_indexed_reg(hwif, 0x10 + adj, tmp & 0x7f);
+ }
+
+ return err;
}
/* 0 1 2 3 4 5 6 7 8
@@ -170,36 +259,42 @@ static int pdcnew_new_tune_chipset (ide_drive_t *drive, u8 xferspeed)
static void pdcnew_tune_drive(ide_drive_t *drive, u8 pio)
{
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
- (void)pdcnew_new_tune_chipset(drive, XFER_PIO_0 + pio);
+ (void)pdcnew_tune_chipset(drive, XFER_PIO_0 + pio);
}
-static u8 pdcnew_new_cable_detect (ide_hwif_t *hwif)
+static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
{
- hwif->OUTB(0x0b, hwif->dma_vendor1);
- return ((u8)((hwif->INB(hwif->dma_vendor3) & 0x04)));
+ return get_indexed_reg(hwif, 0x0b) & 0x04;
}
-static int config_chipset_for_dma (ide_drive_t *drive)
+
+static int config_chipset_for_dma(ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
ide_hwif_t *hwif = HWIF(drive);
- u8 speed = -1;
- u8 cable;
-
- u8 ultra_66 = ((id->dma_ultra & 0x0010) ||
- (id->dma_ultra & 0x0008)) ? 1 : 0;
-
- cable = pdcnew_new_cable_detect(hwif);
+ u8 ultra_66 = (id->dma_ultra & 0x0078) ? 1 : 0;
+ u8 cable = pdcnew_cable_detect(hwif);
+ u8 speed;
if (ultra_66 && cable) {
- printk(KERN_WARNING "Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary");
+ printk(KERN_WARNING "Warning: %s channel "
+ "requires an 80-pin cable for operation.\n",
+ hwif->channel ? "Secondary" : "Primary");
printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name);
}
if (drive->media != ide_disk)
return 0;
- if (id->capability & 4) { /* IORDY_EN & PREFETCH_EN */
- hwif->OUTB((0x13 + ((drive->dn%2) ? 0x08 : 0x00)), hwif->dma_vendor1);
- hwif->OUTB((hwif->INB(hwif->dma_vendor3)|0x03), hwif->dma_vendor3);
+
+ if (id->capability & 4) {
+ /*
+ * Set IORDY_EN & PREFETCH_EN (this seems to have
+ * NO real effect since this register is reloaded
+ * by hardware when the transfer mode is selected)
+ */
+ u8 tmp, adj = (drive->dn & 1) ? 0x08 : 0x00;
+
+ tmp = get_indexed_reg(hwif, 0x13 + adj);
+ set_indexed_reg(hwif, 0x13 + adj, tmp | 0x03);
}
speed = ide_dma_speed(drive, pdcnew_ratemask(drive));
@@ -211,7 +306,7 @@ static int config_chipset_for_dma (ide_drive_t *drive)
return ide_dma_enable(drive);
}
-static int pdcnew_config_drive_xfer_rate (ide_drive_t *drive)
+static int pdcnew_config_drive_xfer_rate(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct hd_driveid *id = drive->id;
@@ -236,9 +331,9 @@ fast_ata_pio:
return 0;
}
-static int pdcnew_quirkproc (ide_drive_t *drive)
+static int pdcnew_quirkproc(ide_drive_t *drive)
{
- return ((int) check_in_drive_lists(drive, pdc_quirk_drives));
+ return check_in_drive_lists(drive, pdc_quirk_drives);
}
static int pdcnew_ide_dma_lostirq(ide_drive_t *drive)
@@ -255,21 +350,100 @@ static int pdcnew_ide_dma_timeout(ide_drive_t *drive)
return __ide_dma_timeout(drive);
}
-static void pdcnew_new_reset (ide_drive_t *drive)
+static void pdcnew_reset(ide_drive_t *drive)
{
/*
* Deleted this because it is redundant from the caller.
*/
- printk(KERN_WARNING "PDC202XX: %s channel reset.\n",
+ printk(KERN_WARNING "pdc202xx_new: %s channel reset.\n",
HWIF(drive)->channel ? "Secondary" : "Primary");
}
+/**
+ * read_counter - Read the byte count registers
+ * @dma_base: for the port address
+ */
+static long __devinit read_counter(u32 dma_base)
+{
+ u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08;
+ u8 cnt0, cnt1, cnt2, cnt3;
+ long count = 0, last;
+ int retry = 3;
+
+ do {
+ last = count;
+
+ /* Read the current count */
+ outb(0x20, pri_dma_base + 0x01);
+ cnt0 = inb(pri_dma_base + 0x03);
+ outb(0x21, pri_dma_base + 0x01);
+ cnt1 = inb(pri_dma_base + 0x03);
+ outb(0x20, sec_dma_base + 0x01);
+ cnt2 = inb(sec_dma_base + 0x03);
+ outb(0x21, sec_dma_base + 0x01);
+ cnt3 = inb(sec_dma_base + 0x03);
+
+ count = (cnt3 << 23) | (cnt2 << 15) | (cnt1 << 8) | cnt0;
+
+ /*
+ * The 30-bit decrementing counter is read in 4 pieces.
+ * Incorrect value may be read when the most significant bytes
+ * are changing...
+ */
+ } while (retry-- && (((last ^ count) & 0x3fff8000) || last < count));
+
+ DBG("cnt0[%02X] cnt1[%02X] cnt2[%02X] cnt3[%02X]\n",
+ cnt0, cnt1, cnt2, cnt3);
+
+ return count;
+}
+
+/**
+ * detect_pll_input_clock - Detect the PLL input clock in Hz.
+ * @dma_base: for the port address
+ * E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock.
+ */
+static long __devinit detect_pll_input_clock(unsigned long dma_base)
+{
+ long start_count, end_count;
+ long pll_input;
+ u8 scr1;
+
+ start_count = read_counter(dma_base);
+
+ /* Start the test mode */
+ outb(0x01, dma_base + 0x01);
+ scr1 = inb(dma_base + 0x03);
+ DBG("scr1[%02X]\n", scr1);
+ outb(scr1 | 0x40, dma_base + 0x03);
+
+ /* Let the counter run for 10 ms. */
+ mdelay(10);
+
+ end_count = read_counter(dma_base);
+
+ /* Stop the test mode */
+ outb(0x01, dma_base + 0x01);
+ scr1 = inb(dma_base + 0x03);
+ DBG("scr1[%02X]\n", scr1);
+ outb(scr1 & ~0x40, dma_base + 0x03);
+
+ /*
+ * Calculate the input clock in Hz
+ * (the clock counter is 30 bit wide and counts down)
+ */
+ pll_input = ((start_count - end_count) & 0x3ffffff) * 100;
+
+ DBG("start[%ld] end[%ld]\n", start_count, end_count);
+
+ return pll_input;
+}
+
#ifdef CONFIG_PPC_PMAC
static void __devinit apple_kiwi_init(struct pci_dev *pdev)
{
struct device_node *np = pci_device_to_OF_node(pdev);
unsigned int class_rev = 0;
- void __iomem *mmio;
u8 conf;
if (np == NULL || !device_is_compatible(np, "kiwi-root"))
@@ -280,30 +454,20 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev)
if (class_rev >= 0x03) {
/* Setup chip magic config stuff (from darwin) */
- pci_read_config_byte(pdev, 0x40, &conf);
- pci_write_config_byte(pdev, 0x40, conf | 0x01);
- }
- mmio = ioremap(pci_resource_start(pdev, 5),
- pci_resource_len(pdev, 5));
-
- /* Setup some PLL stuffs */
- switch (pdev->device) {
- case PCI_DEVICE_ID_PROMISE_20270:
- writew(0x0d2b, mmio + 0x1202);
- mdelay(30);
- break;
- case PCI_DEVICE_ID_PROMISE_20271:
- writew(0x0826, mmio + 0x1202);
- mdelay(30);
- break;
+ pci_read_config_byte (pdev, 0x40, &conf);
+ pci_write_config_byte(pdev, 0x40, (conf | 0x01));
}
-
- iounmap(mmio);
}
#endif /* CONFIG_PPC_PMAC */
static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name)
{
+ unsigned long dma_base = pci_resource_start(dev, 4);
+ unsigned long sec_dma_base = dma_base + 0x08;
+ long pll_input, pll_output, ratio;
+ int f, r;
+ u8 pll_ctl0, pll_ctl1;
+
if (dev->resource[PCI_ROM_RESOURCE].start) {
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
@@ -315,6 +479,106 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
apple_kiwi_init(dev);
#endif
+ /* Calculate the required PLL output frequency */
+ switch(max_dma_rate(dev)) {
+ case 4: /* it's 133 MHz for Ultra133 chips */
+ pll_output = 133333333;
+ break;
+ case 3: /* and 100 MHz for Ultra100 chips */
+ default:
+ pll_output = 100000000;
+ break;
+ }
+
+ /*
+ * Detect PLL input clock.
+ * On some systems, where PCI bus is running at non-standard clock rate
+ * (e.g. 25 or 40 MHz), we have to adjust the cycle time.
+ * PDC20268 and newer chips employ PLL circuit to help correct timing
+ * registers setting.
+ */
+ pll_input = detect_pll_input_clock(dma_base);
+ printk("%s: PLL input clock is %ld kHz\n", name, pll_input / 1000);
+
+ /* Sanity check */
+ if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) {
+ printk(KERN_ERR "%s: Bad PLL input clock %ld Hz, giving up!\n",
+ name, pll_input);
+ goto out;
+ }
+
+#ifdef DEBUG
+ DBG("pll_output is %ld Hz\n", pll_output);
+
+ /* Show the current clock value of PLL control register
+ * (maybe already configured by the BIOS)
+ */
+ outb(0x02, sec_dma_base + 0x01);
+ pll_ctl0 = inb(sec_dma_base + 0x03);
+ outb(0x03, sec_dma_base + 0x01);
+ pll_ctl1 = inb(sec_dma_base + 0x03);
+
+ DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
+#endif
+
+ /*
+ * Calculate the ratio of F, R and NO
+ * POUT = (F + 2) / (( R + 2) * NO)
+ */
+ ratio = pll_output / (pll_input / 1000);
+ if (ratio < 8600L) { /* 8.6x */
+ /* Using NO = 0x01, R = 0x0d */
+ r = 0x0d;
+ } else if (ratio < 12900L) { /* 12.9x */
+ /* Using NO = 0x01, R = 0x08 */
+ r = 0x08;
+ } else if (ratio < 16100L) { /* 16.1x */
+ /* Using NO = 0x01, R = 0x06 */
+ r = 0x06;
+ } else if (ratio < 64000L) { /* 64x */
+ r = 0x00;
+ } else {
+ /* Invalid ratio */
+ printk(KERN_ERR "%s: Bad ratio %ld, giving up!\n", name, ratio);
+ goto out;
+ }
+
+ f = (ratio * (r + 2)) / 1000 - 2;
+
+ DBG("F[%d] R[%d] ratio*1000[%ld]\n", f, r, ratio);
+
+ if (unlikely(f < 0 || f > 127)) {
+ /* Invalid F */
+ printk(KERN_ERR "%s: F[%d] invalid!\n", name, f);
+ goto out;
+ }
+
+ pll_ctl0 = (u8) f;
+ pll_ctl1 = (u8) r;
+
+ DBG("Writing pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
+
+ outb(0x02, sec_dma_base + 0x01);
+ outb(pll_ctl0, sec_dma_base + 0x03);
+ outb(0x03, sec_dma_base + 0x01);
+ outb(pll_ctl1, sec_dma_base + 0x03);
+
+ /* Wait the PLL circuit to be stable */
+ mdelay(30);
+
+#ifdef DEBUG
+ /*
+ * Show the current clock value of PLL control register
+ */
+ outb(0x02, sec_dma_base + 0x01);
+ pll_ctl0 = inb(sec_dma_base + 0x03);
+ outb(0x03, sec_dma_base + 0x01);
+ pll_ctl1 = inb(sec_dma_base + 0x03);
+
+ DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
+#endif
+
+ out:
return dev->irq;
}
@@ -324,8 +588,8 @@ static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif)
hwif->tuneproc = &pdcnew_tune_drive;
hwif->quirkproc = &pdcnew_quirkproc;
- hwif->speedproc = &pdcnew_new_tune_chipset;
- hwif->resetproc = &pdcnew_new_reset;
+ hwif->speedproc = &pdcnew_tune_chipset;
+ hwif->resetproc = &pdcnew_reset;
hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
@@ -337,11 +601,14 @@ static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif)
hwif->ide_dma_check = &pdcnew_config_drive_xfer_rate;
hwif->ide_dma_lostirq = &pdcnew_ide_dma_lostirq;
hwif->ide_dma_timeout = &pdcnew_ide_dma_timeout;
- if (!(hwif->udma_four))
- hwif->udma_four = (pdcnew_new_cable_detect(hwif)) ? 0 : 1;
+
+ if (!hwif->udma_four)
+ hwif->udma_four = pdcnew_cable_detect(hwif) ? 0 : 1;
+
if (!noautodma)
hwif->autodma = 1;
hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma;
+
#if PDC202_DEBUG_CABLE
printk(KERN_DEBUG "%s: %s-pin cable\n",
hwif->name, hwif->udma_four ? "80" : "40");
diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
index cdc3aab..b1e9a8e 100644
--- a/drivers/ide/pci/piix.c
+++ b/drivers/ide/pci/piix.c
@@ -505,6 +505,10 @@ static void __devinit init_hwif_piix(ide_hwif_t *hwif)
/* This is a painful system best to let it self tune for now */
return;
}
+ /* ESB2 appears to generate spurious DMA interrupts in PIO mode
+ when in native mode */
+ if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_ESB2_18)
+ hwif->atapi_irq_bogon = 1;
hwif->autodma = 0;
hwif->tuneproc = &piix_tune_drive;
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index 0719b64..695e239 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -844,11 +844,11 @@ void __init ide_scan_pcibus (int scan_direction)
pre_init = 0;
if (!scan_direction) {
- while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
ide_scan_pcidev(dev);
}
} else {
- while ((dev = pci_find_device_reverse(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ while ((dev = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
ide_scan_pcidev(dev);
}
}
diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
index 13a6179..fbb7f14 100644
--- a/drivers/ieee1394/pcilynx.c
+++ b/drivers/ieee1394/pcilynx.c
@@ -1485,7 +1485,7 @@ static int __devinit add_card(struct pci_dev *dev,
}
- i2c_bit_del_bus(i2c_ad);
+ i2c_del_adapter(i2c_ad);
kfree(i2c_ad);
}
}
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index 163d991..50fb1cd 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -1,9 +1,11 @@
infiniband-$(CONFIG_INFINIBAND_ADDR_TRANS) := ib_addr.o rdma_cm.o
+user_access-$(CONFIG_INFINIBAND_ADDR_TRANS) := rdma_ucm.o
obj-$(CONFIG_INFINIBAND) += ib_core.o ib_mad.o ib_sa.o \
ib_cm.o iw_cm.o $(infiniband-y)
obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o
-obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o
+obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \
+ $(user_access-y)
ib_core-y := packer.o ud_header.o verbs.o sysfs.o \
device.o fmr_pool.o cache.o
@@ -18,6 +20,8 @@ iw_cm-y := iwcm.o
rdma_cm-y := cma.o
+rdma_ucm-y := ucma.o
+
ib_addr-y := addr.o
ib_umad-y := user_mad.o
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 79c937b..d446998 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -3289,6 +3289,10 @@ static int cm_init_qp_rts_attr(struct cm_id_private *cm_id_priv,
spin_lock_irqsave(&cm_id_priv->lock, flags);
switch (cm_id_priv->id.state) {
+ /* Allow transition to RTS before sending REP */
+ case IB_CM_REQ_RCVD:
+ case IB_CM_MRA_REQ_SENT:
+
case IB_CM_REP_RCVD:
case IB_CM_MRA_REP_SENT:
case IB_CM_REP_SENT:
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 985a6b5..533193d 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -70,6 +70,7 @@ static DEFINE_MUTEX(lock);
static struct workqueue_struct *cma_wq;
static DEFINE_IDR(sdp_ps);
static DEFINE_IDR(tcp_ps);
+static DEFINE_IDR(udp_ps);
struct cma_device {
struct list_head list;
@@ -133,7 +134,6 @@ struct rdma_id_private {
u32 seq_num;
u32 qp_num;
- enum ib_qp_type qp_type;
u8 srq;
};
@@ -392,7 +392,6 @@ int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd,
id->qp = qp;
id_priv->qp_num = qp->qp_num;
- id_priv->qp_type = qp->qp_type;
id_priv->srq = (qp->srq != NULL);
return 0;
err:
@@ -510,9 +509,17 @@ static inline int cma_any_addr(struct sockaddr *addr)
return cma_zero_addr(addr) || cma_loopback_addr(addr);
}
+static inline __be16 cma_port(struct sockaddr *addr)
+{
+ if (addr->sa_family == AF_INET)
+ return ((struct sockaddr_in *) addr)->sin_port;
+ else
+ return ((struct sockaddr_in6 *) addr)->sin6_port;
+}
+
static inline int cma_any_port(struct sockaddr *addr)
{
- return !((struct sockaddr_in *) addr)->sin_port;
+ return !cma_port(addr);
}
static int cma_get_net_info(void *hdr, enum rdma_port_space ps,
@@ -594,20 +601,6 @@ static inline int cma_user_data_offset(enum rdma_port_space ps)
}
}
-static int cma_notify_user(struct rdma_id_private *id_priv,
- enum rdma_cm_event_type type, int status,
- void *data, u8 data_len)
-{
- struct rdma_cm_event event;
-
- event.event = type;
- event.status = status;
- event.private_data = data;
- event.private_data_len = data_len;
-
- return id_priv->id.event_handler(&id_priv->id, &event);
-}
-
static void cma_cancel_route(struct rdma_id_private *id_priv)
{
switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
@@ -776,63 +769,61 @@ static int cma_verify_rep(struct rdma_id_private *id_priv, void *data)
return 0;
}
-static int cma_rtu_recv(struct rdma_id_private *id_priv)
+static void cma_set_rep_event_data(struct rdma_cm_event *event,
+ struct ib_cm_rep_event_param *rep_data,
+ void *private_data)
{
- int ret;
-
- ret = cma_modify_qp_rts(&id_priv->id);
- if (ret)
- goto reject;
-
- return 0;
-reject:
- cma_modify_qp_err(&id_priv->id);
- ib_send_cm_rej(id_priv->cm_id.ib, IB_CM_REJ_CONSUMER_DEFINED,
- NULL, 0, NULL, 0);
- return ret;
+ event->param.conn.private_data = private_data;
+ event->param.conn.private_data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
+ event->param.conn.responder_resources = rep_data->responder_resources;
+ event->param.conn.initiator_depth = rep_data->initiator_depth;
+ event->param.conn.flow_control = rep_data->flow_control;
+ event->param.conn.rnr_retry_count = rep_data->rnr_retry_count;
+ event->param.conn.srq = rep_data->srq;
+ event->param.conn.qp_num = rep_data->remote_qpn;
}
static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
{
struct rdma_id_private *id_priv = cm_id->context;
- enum rdma_cm_event_type event;
- u8 private_data_len = 0;
- int ret = 0, status = 0;
+ struct rdma_cm_event event;
+ int ret = 0;
atomic_inc(&id_priv->dev_remove);
if (!cma_comp(id_priv, CMA_CONNECT))
goto out;
+ memset(&event, 0, sizeof event);
switch (ib_event->event) {
case IB_CM_REQ_ERROR:
case IB_CM_REP_ERROR:
- event = RDMA_CM_EVENT_UNREACHABLE;
- status = -ETIMEDOUT;
+ event.event = RDMA_CM_EVENT_UNREACHABLE;
+ event.status = -ETIMEDOUT;
break;
case IB_CM_REP_RECEIVED:
- status = cma_verify_rep(id_priv, ib_event->private_data);
- if (status)
- event = RDMA_CM_EVENT_CONNECT_ERROR;
+ event.status = cma_verify_rep(id_priv, ib_event->private_data);
+ if (event.status)
+ event.event = RDMA_CM_EVENT_CONNECT_ERROR;
else if (id_priv->id.qp && id_priv->id.ps != RDMA_PS_SDP) {
- status = cma_rep_recv(id_priv);
- event = status ? RDMA_CM_EVENT_CONNECT_ERROR :
- RDMA_CM_EVENT_ESTABLISHED;
+ event.status = cma_rep_recv(id_priv);
+ event.event = event.status ? RDMA_CM_EVENT_CONNECT_ERROR :
+ RDMA_CM_EVENT_ESTABLISHED;
} else
- event = RDMA_CM_EVENT_CONNECT_RESPONSE;
- private_data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
+ event.event = RDMA_CM_EVENT_CONNECT_RESPONSE;
+ cma_set_rep_event_data(&event, &ib_event->param.rep_rcvd,
+ ib_event->private_data);
break;
case IB_CM_RTU_RECEIVED:
- status = cma_rtu_recv(id_priv);
- event = status ? RDMA_CM_EVENT_CONNECT_ERROR :
- RDMA_CM_EVENT_ESTABLISHED;
+ case IB_CM_USER_ESTABLISHED:
+ event.event = RDMA_CM_EVENT_ESTABLISHED;
break;
case IB_CM_DREQ_ERROR:
- status = -ETIMEDOUT; /* fall through */
+ event.status = -ETIMEDOUT; /* fall through */
case IB_CM_DREQ_RECEIVED:
case IB_CM_DREP_RECEIVED:
if (!cma_comp_exch(id_priv, CMA_CONNECT, CMA_DISCONNECT))
goto out;
- event = RDMA_CM_EVENT_DISCONNECTED;
+ event.event = RDMA_CM_EVENT_DISCONNECTED;
break;
case IB_CM_TIMEWAIT_EXIT:
case IB_CM_MRA_RECEIVED:
@@ -840,9 +831,10 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
goto out;
case IB_CM_REJ_RECEIVED:
cma_modify_qp_err(&id_priv->id);
- status = ib_event->param.rej_rcvd.reason;
- event = RDMA_CM_EVENT_REJECTED;
- private_data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
+ event.status = ib_event->param.rej_rcvd.reason;
+ event.event = RDMA_CM_EVENT_REJECTED;
+ event.param.conn.private_data = ib_event->private_data;
+ event.param.conn.private_data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
break;
default:
printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d",
@@ -850,8 +842,7 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
goto out;
}
- ret = cma_notify_user(id_priv, event, status, ib_event->private_data,
- private_data_len);
+ ret = id_priv->id.event_handler(&id_priv->id, &event);
if (ret) {
/* Destroy the CM ID by returning a non-zero value. */
id_priv->cm_id.ib = NULL;
@@ -865,8 +856,8 @@ out:
return ret;
}
-static struct rdma_id_private *cma_new_id(struct rdma_cm_id *listen_id,
- struct ib_cm_event *ib_event)
+static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
+ struct ib_cm_event *ib_event)
{
struct rdma_id_private *id_priv;
struct rdma_cm_id *id;
@@ -913,9 +904,61 @@ err:
return NULL;
}
+static struct rdma_id_private *cma_new_udp_id(struct rdma_cm_id *listen_id,
+ struct ib_cm_event *ib_event)
+{
+ struct rdma_id_private *id_priv;
+ struct rdma_cm_id *id;
+ union cma_ip_addr *src, *dst;
+ __u16 port;
+ u8 ip_ver;
+ int ret;
+
+ id = rdma_create_id(listen_id->event_handler, listen_id->context,
+ listen_id->ps);
+ if (IS_ERR(id))
+ return NULL;
+
+
+ if (cma_get_net_info(ib_event->private_data, listen_id->ps,
+ &ip_ver, &port, &src, &dst))
+ goto err;
+
+ cma_save_net_info(&id->route.addr, &listen_id->route.addr,
+ ip_ver, port, src, dst);
+
+ ret = rdma_translate_ip(&id->route.addr.src_addr,
+ &id->route.addr.dev_addr);
+ if (ret)
+ goto err;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ id_priv->state = CMA_CONNECT;
+ return id_priv;
+err:
+ rdma_destroy_id(id);
+ return NULL;
+}
+
+static void cma_set_req_event_data(struct rdma_cm_event *event,
+ struct ib_cm_req_event_param *req_data,
+ void *private_data, int offset)
+{
+ event->param.conn.private_data = private_data + offset;
+ event->param.conn.private_data_len = IB_CM_REQ_PRIVATE_DATA_SIZE - offset;
+ event->param.conn.responder_resources = req_data->responder_resources;
+ event->param.conn.initiator_depth = req_data->initiator_depth;
+ event->param.conn.flow_control = req_data->flow_control;
+ event->param.conn.retry_count = req_data->retry_count;
+ event->param.conn.rnr_retry_count = req_data->rnr_retry_count;
+ event->param.conn.srq = req_data->srq;
+ event->param.conn.qp_num = req_data->remote_qpn;
+}
+
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;
+ struct rdma_cm_event event;
int offset, ret;
listen_id = cm_id->context;
@@ -925,7 +968,19 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
goto out;
}
- conn_id = cma_new_id(&listen_id->id, ib_event);
+ 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.ps == RDMA_PS_UDP) {
+ 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 =
+ IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE - offset;
+ } else {
+ conn_id = cma_new_conn_id(&listen_id->id, ib_event);
+ cma_set_req_event_data(&event, &ib_event->param.req_rcvd,
+ ib_event->private_data, offset);
+ }
if (!conn_id) {
ret = -ENOMEM;
goto out;
@@ -942,10 +997,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
cm_id->context = conn_id;
cm_id->cm_handler = cma_ib_handler;
- offset = cma_user_data_offset(listen_id->id.ps);
- ret = cma_notify_user(conn_id, RDMA_CM_EVENT_CONNECT_REQUEST, 0,
- ib_event->private_data + offset,
- IB_CM_REQ_PRIVATE_DATA_SIZE - offset);
+ ret = conn_id->id.event_handler(&conn_id->id, &event);
if (!ret)
goto out;
@@ -964,8 +1016,7 @@ out:
static __be64 cma_get_service_id(enum rdma_port_space ps, struct sockaddr *addr)
{
- return cpu_to_be64(((u64)ps << 16) +
- be16_to_cpu(((struct sockaddr_in *) addr)->sin_port));
+ return cpu_to_be64(((u64)ps << 16) + be16_to_cpu(cma_port(addr)));
}
static void cma_set_compare_data(enum rdma_port_space ps, struct sockaddr *addr,
@@ -1021,15 +1072,16 @@ static void cma_set_compare_data(enum rdma_port_space ps, struct sockaddr *addr,
static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
{
struct rdma_id_private *id_priv = iw_id->context;
- enum rdma_cm_event_type event = 0;
+ struct rdma_cm_event event;
struct sockaddr_in *sin;
int ret = 0;
+ memset(&event, 0, sizeof event);
atomic_inc(&id_priv->dev_remove);
switch (iw_event->event) {
case IW_CM_EVENT_CLOSE:
- event = RDMA_CM_EVENT_DISCONNECTED;
+ event.event = RDMA_CM_EVENT_DISCONNECTED;
break;
case IW_CM_EVENT_CONNECT_REPLY:
sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
@@ -1037,20 +1089,21 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event)
sin = (struct sockaddr_in *) &id_priv->id.route.addr.dst_addr;
*sin = iw_event->remote_addr;
if (iw_event->status)
- event = RDMA_CM_EVENT_REJECTED;
+ event.event = RDMA_CM_EVENT_REJECTED;
else
- event = RDMA_CM_EVENT_ESTABLISHED;
+ event.event = RDMA_CM_EVENT_ESTABLISHED;
break;
case IW_CM_EVENT_ESTABLISHED:
- event = RDMA_CM_EVENT_ESTABLISHED;
+ event.event = RDMA_CM_EVENT_ESTABLISHED;
break;
default:
BUG_ON(1);
}
- ret = cma_notify_user(id_priv, event, iw_event->status,
- iw_event->private_data,
- iw_event->private_data_len);
+ event.status = iw_event->status;
+ event.param.conn.private_data = iw_event->private_data;
+ event.param.conn.private_data_len = iw_event->private_data_len;
+ ret = id_priv->id.event_handler(&id_priv->id, &event);
if (ret) {
/* Destroy the CM ID by returning a non-zero value. */
id_priv->cm_id.iw = NULL;
@@ -1071,6 +1124,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
struct rdma_id_private *listen_id, *conn_id;
struct sockaddr_in *sin;
struct net_device *dev = NULL;
+ struct rdma_cm_event event;
int ret;
listen_id = cm_id->context;
@@ -1124,9 +1178,11 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
sin = (struct sockaddr_in *) &new_cm_id->route.addr.dst_addr;
*sin = iw_event->remote_addr;
- ret = cma_notify_user(conn_id, RDMA_CM_EVENT_CONNECT_REQUEST, 0,
- iw_event->private_data,
- iw_event->private_data_len);
+ memset(&event, 0, sizeof event);
+ 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;
+ ret = conn_id->id.event_handler(&conn_id->id, &event);
if (ret) {
/* User wants to destroy the CM ID */
conn_id->cm_id.iw = NULL;
@@ -1515,8 +1571,9 @@ static void addr_handler(int status, struct sockaddr *src_addr,
struct rdma_dev_addr *dev_addr, void *context)
{
struct rdma_id_private *id_priv = context;
- enum rdma_cm_event_type event;
+ struct rdma_cm_event event;
+ memset(&event, 0, sizeof event);
atomic_inc(&id_priv->dev_remove);
/*
@@ -1536,14 +1593,15 @@ static void addr_handler(int status, struct sockaddr *src_addr,
if (status) {
if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ADDR_BOUND))
goto out;
- event = RDMA_CM_EVENT_ADDR_ERROR;
+ event.event = RDMA_CM_EVENT_ADDR_ERROR;
+ event.status = status;
} else {
memcpy(&id_priv->id.route.addr.src_addr, src_addr,
ip_addr_size(src_addr));
- event = RDMA_CM_EVENT_ADDR_RESOLVED;
+ event.event = RDMA_CM_EVENT_ADDR_RESOLVED;
}
- if (cma_notify_user(id_priv, event, status, NULL, 0)) {
+ if (id_priv->id.event_handler(&id_priv->id, &event)) {
cma_exch(id_priv, CMA_DESTROYING);
cma_release_remove(id_priv);
cma_deref_id(id_priv);
@@ -1733,6 +1791,9 @@ static int cma_get_port(struct rdma_id_private *id_priv)
case RDMA_PS_TCP:
ps = &tcp_ps;
break;
+ case RDMA_PS_UDP:
+ ps = &udp_ps;
+ break;
default:
return -EPROTONOSUPPORT;
}
@@ -1821,6 +1882,110 @@ static int cma_format_hdr(void *hdr, enum rdma_port_space ps,
return 0;
}
+static int cma_sidr_rep_handler(struct ib_cm_id *cm_id,
+ struct ib_cm_event *ib_event)
+{
+ struct rdma_id_private *id_priv = cm_id->context;
+ struct rdma_cm_event event;
+ struct ib_cm_sidr_rep_event_param *rep = &ib_event->param.sidr_rep_rcvd;
+ int ret = 0;
+
+ memset(&event, 0, sizeof event);
+ atomic_inc(&id_priv->dev_remove);
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ goto out;
+
+ switch (ib_event->event) {
+ case IB_CM_SIDR_REQ_ERROR:
+ event.event = RDMA_CM_EVENT_UNREACHABLE;
+ event.status = -ETIMEDOUT;
+ break;
+ case IB_CM_SIDR_REP_RECEIVED:
+ event.param.ud.private_data = ib_event->private_data;
+ event.param.ud.private_data_len = IB_CM_SIDR_REP_PRIVATE_DATA_SIZE;
+ if (rep->status != IB_SIDR_SUCCESS) {
+ event.event = RDMA_CM_EVENT_UNREACHABLE;
+ event.status = ib_event->param.sidr_rep_rcvd.status;
+ break;
+ }
+ if (rep->qkey != RDMA_UD_QKEY) {
+ event.event = RDMA_CM_EVENT_UNREACHABLE;
+ event.status = -EINVAL;
+ break;
+ }
+ ib_init_ah_from_path(id_priv->id.device, id_priv->id.port_num,
+ id_priv->id.route.path_rec,
+ &event.param.ud.ah_attr);
+ event.param.ud.qp_num = rep->qpn;
+ event.param.ud.qkey = rep->qkey;
+ event.event = RDMA_CM_EVENT_ESTABLISHED;
+ event.status = 0;
+ break;
+ default:
+ printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d",
+ ib_event->event);
+ goto out;
+ }
+
+ ret = id_priv->id.event_handler(&id_priv->id, &event);
+ if (ret) {
+ /* Destroy the CM ID by returning a non-zero value. */
+ id_priv->cm_id.ib = NULL;
+ cma_exch(id_priv, CMA_DESTROYING);
+ cma_release_remove(id_priv);
+ rdma_destroy_id(&id_priv->id);
+ return ret;
+ }
+out:
+ cma_release_remove(id_priv);
+ return ret;
+}
+
+static int cma_resolve_ib_udp(struct rdma_id_private *id_priv,
+ struct rdma_conn_param *conn_param)
+{
+ struct ib_cm_sidr_req_param req;
+ struct rdma_route *route;
+ int ret;
+
+ req.private_data_len = sizeof(struct cma_hdr) +
+ conn_param->private_data_len;
+ req.private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
+ if (!req.private_data)
+ return -ENOMEM;
+
+ if (conn_param->private_data && conn_param->private_data_len)
+ memcpy((void *) req.private_data + sizeof(struct cma_hdr),
+ conn_param->private_data, conn_param->private_data_len);
+
+ route = &id_priv->id.route;
+ ret = cma_format_hdr((void *) req.private_data, id_priv->id.ps, route);
+ if (ret)
+ goto out;
+
+ id_priv->cm_id.ib = ib_create_cm_id(id_priv->id.device,
+ cma_sidr_rep_handler, id_priv);
+ if (IS_ERR(id_priv->cm_id.ib)) {
+ ret = PTR_ERR(id_priv->cm_id.ib);
+ goto out;
+ }
+
+ req.path = route->path_rec;
+ req.service_id = cma_get_service_id(id_priv->id.ps,
+ &route->addr.dst_addr);
+ req.timeout_ms = 1 << (CMA_CM_RESPONSE_TIMEOUT - 8);
+ req.max_cm_retries = CMA_MAX_CM_RETRIES;
+
+ ret = ib_send_cm_sidr_req(id_priv->cm_id.ib, &req);
+ if (ret) {
+ ib_destroy_cm_id(id_priv->cm_id.ib);
+ id_priv->cm_id.ib = NULL;
+ }
+out:
+ kfree(req.private_data);
+ return ret;
+}
+
static int cma_connect_ib(struct rdma_id_private *id_priv,
struct rdma_conn_param *conn_param)
{
@@ -1860,7 +2025,7 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
req.service_id = cma_get_service_id(id_priv->id.ps,
&route->addr.dst_addr);
req.qp_num = id_priv->qp_num;
- req.qp_type = id_priv->qp_type;
+ req.qp_type = IB_QPT_RC;
req.starting_psn = id_priv->seq_num;
req.responder_resources = conn_param->responder_resources;
req.initiator_depth = conn_param->initiator_depth;
@@ -1937,13 +2102,15 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
if (!id->qp) {
id_priv->qp_num = conn_param->qp_num;
- id_priv->qp_type = conn_param->qp_type;
id_priv->srq = conn_param->srq;
}
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- ret = cma_connect_ib(id_priv, conn_param);
+ if (id->ps == RDMA_PS_UDP)
+ ret = cma_resolve_ib_udp(id_priv, conn_param);
+ else
+ ret = cma_connect_ib(id_priv, conn_param);
break;
case RDMA_TRANSPORT_IWARP:
ret = cma_connect_iw(id_priv, conn_param);
@@ -1966,11 +2133,25 @@ static int cma_accept_ib(struct rdma_id_private *id_priv,
struct rdma_conn_param *conn_param)
{
struct ib_cm_rep_param rep;
- int ret;
+ struct ib_qp_attr qp_attr;
+ int qp_attr_mask, ret;
- ret = cma_modify_qp_rtr(&id_priv->id);
- if (ret)
- return ret;
+ if (id_priv->id.qp) {
+ ret = cma_modify_qp_rtr(&id_priv->id);
+ if (ret)
+ goto out;
+
+ qp_attr.qp_state = IB_QPS_RTS;
+ ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, &qp_attr,
+ &qp_attr_mask);
+ if (ret)
+ goto out;
+
+ qp_attr.max_rd_atomic = conn_param->initiator_depth;
+ ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
+ if (ret)
+ goto out;
+ }
memset(&rep, 0, sizeof rep);
rep.qp_num = id_priv->qp_num;
@@ -1985,7 +2166,9 @@ static int cma_accept_ib(struct rdma_id_private *id_priv,
rep.rnr_retry_count = conn_param->rnr_retry_count;
rep.srq = id_priv->srq ? 1 : 0;
- return ib_send_cm_rep(id_priv->cm_id.ib, &rep);
+ ret = ib_send_cm_rep(id_priv->cm_id.ib, &rep);
+out:
+ return ret;
}
static int cma_accept_iw(struct rdma_id_private *id_priv,
@@ -2010,6 +2193,24 @@ static int cma_accept_iw(struct rdma_id_private *id_priv,
return iw_cm_accept(id_priv->cm_id.iw, &iw_param);
}
+static int cma_send_sidr_rep(struct rdma_id_private *id_priv,
+ enum ib_cm_sidr_status status,
+ const void *private_data, int private_data_len)
+{
+ struct ib_cm_sidr_rep_param rep;
+
+ memset(&rep, 0, sizeof rep);
+ rep.status = status;
+ if (status == IB_SIDR_SUCCESS) {
+ rep.qp_num = id_priv->qp_num;
+ rep.qkey = RDMA_UD_QKEY;
+ }
+ rep.private_data = private_data;
+ rep.private_data_len = private_data_len;
+
+ return ib_send_cm_sidr_rep(id_priv->cm_id.ib, &rep);
+}
+
int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
{
struct rdma_id_private *id_priv;
@@ -2021,13 +2222,16 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
if (!id->qp && conn_param) {
id_priv->qp_num = conn_param->qp_num;
- id_priv->qp_type = conn_param->qp_type;
id_priv->srq = conn_param->srq;
}
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (conn_param)
+ if (id->ps == RDMA_PS_UDP)
+ 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);
@@ -2051,6 +2255,27 @@ reject:
}
EXPORT_SYMBOL(rdma_accept);
+int rdma_notify(struct rdma_cm_id *id, enum ib_event_type event)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ return -EINVAL;
+
+ switch (id->device->node_type) {
+ case RDMA_NODE_IB_CA:
+ ret = ib_cm_notify(id_priv->cm_id.ib, event);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(rdma_notify);
+
int rdma_reject(struct rdma_cm_id *id, const void *private_data,
u8 private_data_len)
{
@@ -2063,9 +2288,13 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- ret = ib_send_cm_rej(id_priv->cm_id.ib,
- IB_CM_REJ_CONSUMER_DEFINED, NULL, 0,
- private_data, private_data_len);
+ if (id->ps == RDMA_PS_UDP)
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT,
+ private_data, private_data_len);
+ else
+ ret = ib_send_cm_rej(id_priv->cm_id.ib,
+ IB_CM_REJ_CONSUMER_DEFINED, NULL,
+ 0, private_data, private_data_len);
break;
case RDMA_TRANSPORT_IWARP:
ret = iw_cm_reject(id_priv->cm_id.iw,
@@ -2136,6 +2365,7 @@ static void cma_add_one(struct ib_device *device)
static int cma_remove_id_dev(struct rdma_id_private *id_priv)
{
+ struct rdma_cm_event event;
enum cma_state state;
/* Record that we want to remove the device */
@@ -2150,8 +2380,9 @@ static int cma_remove_id_dev(struct rdma_id_private *id_priv)
if (!cma_comp(id_priv, CMA_DEVICE_REMOVAL))
return 0;
- return cma_notify_user(id_priv, RDMA_CM_EVENT_DEVICE_REMOVAL,
- 0, NULL, 0);
+ memset(&event, 0, sizeof event);
+ event.event = RDMA_CM_EVENT_DEVICE_REMOVAL;
+ return id_priv->id.event_handler(&id_priv->id, &event);
}
static void cma_process_remove(struct cma_device *cma_dev)
@@ -2233,6 +2464,7 @@ static void cma_cleanup(void)
destroy_workqueue(cma_wq);
idr_destroy(&sdp_ps);
idr_destroy(&tcp_ps);
+ idr_destroy(&udp_ps);
}
module_init(cma_init);
diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c
index 86a3b2d..8926a2b 100644
--- a/drivers/infiniband/core/fmr_pool.c
+++ b/drivers/infiniband/core/fmr_pool.c
@@ -394,20 +394,12 @@ EXPORT_SYMBOL(ib_destroy_fmr_pool);
*/
int ib_flush_fmr_pool(struct ib_fmr_pool *pool)
{
- int serial;
-
- atomic_inc(&pool->req_ser);
- /*
- * It's OK if someone else bumps req_ser again here -- we'll
- * just wait a little longer.
- */
- serial = atomic_read(&pool->req_ser);
+ int serial = atomic_inc_return(&pool->req_ser);
wake_up_process(pool->thread);
if (wait_event_interruptible(pool->force_wait,
- atomic_read(&pool->flush_ser) -
- atomic_read(&pool->req_ser) >= 0))
+ atomic_read(&pool->flush_ser) - serial >= 0))
return -EINTR;
return 0;
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 15f38d9..5ed141e 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -998,17 +998,17 @@ int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr)
mad_agent = mad_send_wr->send_buf.mad_agent;
sge = mad_send_wr->sg_list;
- sge[0].addr = dma_map_single(mad_agent->device->dma_device,
- mad_send_wr->send_buf.mad,
- sge[0].length,
- DMA_TO_DEVICE);
- pci_unmap_addr_set(mad_send_wr, header_mapping, sge[0].addr);
-
- sge[1].addr = dma_map_single(mad_agent->device->dma_device,
- ib_get_payload(mad_send_wr),
- sge[1].length,
- DMA_TO_DEVICE);
- pci_unmap_addr_set(mad_send_wr, payload_mapping, sge[1].addr);
+ sge[0].addr = ib_dma_map_single(mad_agent->device,
+ mad_send_wr->send_buf.mad,
+ sge[0].length,
+ DMA_TO_DEVICE);
+ mad_send_wr->header_mapping = sge[0].addr;
+
+ sge[1].addr = ib_dma_map_single(mad_agent->device,
+ ib_get_payload(mad_send_wr),
+ sge[1].length,
+ DMA_TO_DEVICE);
+ mad_send_wr->payload_mapping = sge[1].addr;
spin_lock_irqsave(&qp_info->send_queue.lock, flags);
if (qp_info->send_queue.count < qp_info->send_queue.max_active) {
@@ -1026,12 +1026,12 @@ int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr)
}
spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
if (ret) {
- dma_unmap_single(mad_agent->device->dma_device,
- pci_unmap_addr(mad_send_wr, header_mapping),
- sge[0].length, DMA_TO_DEVICE);
- dma_unmap_single(mad_agent->device->dma_device,
- pci_unmap_addr(mad_send_wr, payload_mapping),
- sge[1].length, DMA_TO_DEVICE);
+ ib_dma_unmap_single(mad_agent->device,
+ mad_send_wr->header_mapping,
+ sge[0].length, DMA_TO_DEVICE);
+ ib_dma_unmap_single(mad_agent->device,
+ mad_send_wr->payload_mapping,
+ sge[1].length, DMA_TO_DEVICE);
}
return ret;
}
@@ -1850,11 +1850,11 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header,
mad_list);
recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
- dma_unmap_single(port_priv->device->dma_device,
- pci_unmap_addr(&recv->header, mapping),
- sizeof(struct ib_mad_private) -
- sizeof(struct ib_mad_private_header),
- DMA_FROM_DEVICE);
+ ib_dma_unmap_single(port_priv->device,
+ recv->header.mapping,
+ sizeof(struct ib_mad_private) -
+ sizeof(struct ib_mad_private_header),
+ DMA_FROM_DEVICE);
/* Setup MAD receive work completion from "normal" work completion */
recv->header.wc = *wc;
@@ -2080,12 +2080,12 @@ static void ib_mad_send_done_handler(struct ib_mad_port_private *port_priv,
qp_info = send_queue->qp_info;
retry:
- dma_unmap_single(mad_send_wr->send_buf.mad_agent->device->dma_device,
- pci_unmap_addr(mad_send_wr, header_mapping),
- mad_send_wr->sg_list[0].length, DMA_TO_DEVICE);
- dma_unmap_single(mad_send_wr->send_buf.mad_agent->device->dma_device,
- pci_unmap_addr(mad_send_wr, payload_mapping),
- mad_send_wr->sg_list[1].length, DMA_TO_DEVICE);
+ ib_dma_unmap_single(mad_send_wr->send_buf.mad_agent->device,
+ mad_send_wr->header_mapping,
+ mad_send_wr->sg_list[0].length, DMA_TO_DEVICE);
+ ib_dma_unmap_single(mad_send_wr->send_buf.mad_agent->device,
+ mad_send_wr->payload_mapping,
+ mad_send_wr->sg_list[1].length, DMA_TO_DEVICE);
queued_send_wr = NULL;
spin_lock_irqsave(&send_queue->lock, flags);
list_del(&mad_list->list);
@@ -2528,13 +2528,12 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
break;
}
}
- sg_list.addr = dma_map_single(qp_info->port_priv->
- device->dma_device,
- &mad_priv->grh,
- sizeof *mad_priv -
- sizeof mad_priv->header,
- DMA_FROM_DEVICE);
- pci_unmap_addr_set(&mad_priv->header, mapping, sg_list.addr);
+ sg_list.addr = ib_dma_map_single(qp_info->port_priv->device,
+ &mad_priv->grh,
+ sizeof *mad_priv -
+ sizeof mad_priv->header,
+ DMA_FROM_DEVICE);
+ mad_priv->header.mapping = sg_list.addr;
recv_wr.wr_id = (unsigned long)&mad_priv->header.mad_list;
mad_priv->header.mad_list.mad_queue = recv_queue;
@@ -2549,12 +2548,11 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
list_del(&mad_priv->header.mad_list.list);
recv_queue->count--;
spin_unlock_irqrestore(&recv_queue->lock, flags);
- dma_unmap_single(qp_info->port_priv->device->dma_device,
- pci_unmap_addr(&mad_priv->header,
- mapping),
- sizeof *mad_priv -
- sizeof mad_priv->header,
- DMA_FROM_DEVICE);
+ ib_dma_unmap_single(qp_info->port_priv->device,
+ mad_priv->header.mapping,
+ sizeof *mad_priv -
+ sizeof mad_priv->header,
+ DMA_FROM_DEVICE);
kmem_cache_free(ib_mad_cache, mad_priv);
printk(KERN_ERR PFX "ib_post_recv failed: %d\n", ret);
break;
@@ -2586,11 +2584,11 @@ static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
/* Remove from posted receive MAD list */
list_del(&mad_list->list);
- dma_unmap_single(qp_info->port_priv->device->dma_device,
- pci_unmap_addr(&recv->header, mapping),
- sizeof(struct ib_mad_private) -
- sizeof(struct ib_mad_private_header),
- DMA_FROM_DEVICE);
+ ib_dma_unmap_single(qp_info->port_priv->device,
+ recv->header.mapping,
+ sizeof(struct ib_mad_private) -
+ sizeof(struct ib_mad_private_header),
+ DMA_FROM_DEVICE);
kmem_cache_free(ib_mad_cache, recv);
}
diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h
index d5548e7..de89717 100644
--- a/drivers/infiniband/core/mad_priv.h
+++ b/drivers/infiniband/core/mad_priv.h
@@ -73,7 +73,7 @@ struct ib_mad_private_header {
struct ib_mad_list_head mad_list;
struct ib_mad_recv_wc recv_wc;
struct ib_wc wc;
- DECLARE_PCI_UNMAP_ADDR(mapping)
+ u64 mapping;
} __attribute__ ((packed));
struct ib_mad_private {
@@ -126,8 +126,8 @@ struct ib_mad_send_wr_private {
struct list_head agent_list;
struct ib_mad_agent_private *mad_agent_priv;
struct ib_mad_send_buf send_buf;
- DECLARE_PCI_UNMAP_ADDR(header_mapping)
- DECLARE_PCI_UNMAP_ADDR(payload_mapping)
+ u64 header_mapping;
+ u64 payload_mapping;
struct ib_send_wr send_wr;
struct ib_sge sg_list[IB_MAD_SEND_REQ_MAX_SG];
__be64 tid;
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
new file mode 100644
index 0000000..81a5cdc
--- /dev/null
+++ b/drivers/infiniband/core/ucma.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2005-2006 Intel Corporation. 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/completion.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/idr.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/miscdevice.h>
+
+#include <rdma/rdma_user_cm.h>
+#include <rdma/ib_marshall.h>
+#include <rdma/rdma_cm.h>
+
+MODULE_AUTHOR("Sean Hefty");
+MODULE_DESCRIPTION("RDMA Userspace Connection Manager Access");
+MODULE_LICENSE("Dual BSD/GPL");
+
+enum {
+ UCMA_MAX_BACKLOG = 128
+};
+
+struct ucma_file {
+ struct mutex mut;
+ struct file *filp;
+ struct list_head ctx_list;
+ struct list_head event_list;
+ wait_queue_head_t poll_wait;
+};
+
+struct ucma_context {
+ int id;
+ struct completion comp;
+ atomic_t ref;
+ int events_reported;
+ int backlog;
+
+ struct ucma_file *file;
+ struct rdma_cm_id *cm_id;
+ u64 uid;
+
+ struct list_head list;
+};
+
+struct ucma_event {
+ struct ucma_context *ctx;
+ struct list_head list;
+ struct rdma_cm_id *cm_id;
+ struct rdma_ucm_event_resp resp;
+};
+
+static DEFINE_MUTEX(mut);
+static DEFINE_IDR(ctx_idr);
+
+static inline struct ucma_context *_ucma_find_context(int id,
+ struct ucma_file *file)
+{
+ struct ucma_context *ctx;
+
+ ctx = idr_find(&ctx_idr, id);
+ if (!ctx)
+ ctx = ERR_PTR(-ENOENT);
+ else if (ctx->file != file)
+ ctx = ERR_PTR(-EINVAL);
+ return ctx;
+}
+
+static struct ucma_context *ucma_get_ctx(struct ucma_file *file, int id)
+{
+ struct ucma_context *ctx;
+
+ mutex_lock(&mut);
+ ctx = _ucma_find_context(id, file);
+ if (!IS_ERR(ctx))
+ atomic_inc(&ctx->ref);
+ mutex_unlock(&mut);
+ return ctx;
+}
+
+static void ucma_put_ctx(struct ucma_context *ctx)
+{
+ if (atomic_dec_and_test(&ctx->ref))
+ complete(&ctx->comp);
+}
+
+static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file)
+{
+ struct ucma_context *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ atomic_set(&ctx->ref, 1);
+ init_completion(&ctx->comp);
+ ctx->file = file;
+
+ do {
+ ret = idr_pre_get(&ctx_idr, GFP_KERNEL);
+ if (!ret)
+ goto error;
+
+ mutex_lock(&mut);
+ ret = idr_get_new(&ctx_idr, ctx, &ctx->id);
+ mutex_unlock(&mut);
+ } while (ret == -EAGAIN);
+
+ if (ret)
+ goto error;
+
+ list_add_tail(&ctx->list, &file->ctx_list);
+ return ctx;
+
+error:
+ kfree(ctx);
+ return NULL;
+}
+
+static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst,
+ struct rdma_conn_param *src)
+{
+ if (src->private_data_len)
+ memcpy(dst->private_data, src->private_data,
+ src->private_data_len);
+ dst->private_data_len = src->private_data_len;
+ dst->responder_resources =src->responder_resources;
+ dst->initiator_depth = src->initiator_depth;
+ dst->flow_control = src->flow_control;
+ dst->retry_count = src->retry_count;
+ dst->rnr_retry_count = src->rnr_retry_count;
+ dst->srq = src->srq;
+ dst->qp_num = src->qp_num;
+}
+
+static void ucma_copy_ud_event(struct rdma_ucm_ud_param *dst,
+ struct rdma_ud_param *src)
+{
+ if (src->private_data_len)
+ memcpy(dst->private_data, src->private_data,
+ src->private_data_len);
+ dst->private_data_len = src->private_data_len;
+ ib_copy_ah_attr_to_user(&dst->ah_attr, &src->ah_attr);
+ dst->qp_num = src->qp_num;
+ dst->qkey = src->qkey;
+}
+
+static void ucma_set_event_context(struct ucma_context *ctx,
+ struct rdma_cm_event *event,
+ struct ucma_event *uevent)
+{
+ uevent->ctx = ctx;
+ uevent->resp.uid = ctx->uid;
+ uevent->resp.id = ctx->id;
+}
+
+static int ucma_event_handler(struct rdma_cm_id *cm_id,
+ struct rdma_cm_event *event)
+{
+ struct ucma_event *uevent;
+ struct ucma_context *ctx = cm_id->context;
+ int ret = 0;
+
+ uevent = kzalloc(sizeof(*uevent), GFP_KERNEL);
+ if (!uevent)
+ return event->event == RDMA_CM_EVENT_CONNECT_REQUEST;
+
+ uevent->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)
+ ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud);
+ else
+ ucma_copy_conn_event(&uevent->resp.param.conn,
+ &event->param.conn);
+
+ mutex_lock(&ctx->file->mut);
+ if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
+ if (!ctx->backlog) {
+ ret = -EDQUOT;
+ goto out;
+ }
+ ctx->backlog--;
+ }
+ list_add_tail(&uevent->list, &ctx->file->event_list);
+ wake_up_interruptible(&ctx->file->poll_wait);
+out:
+ mutex_unlock(&ctx->file->mut);
+ return ret;
+}
+
+static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct ucma_context *ctx;
+ struct rdma_ucm_get_event cmd;
+ struct ucma_event *uevent;
+ int ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (out_len < sizeof uevent->resp)
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&file->mut);
+ while (list_empty(&file->event_list)) {
+ if (file->filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
+ mutex_unlock(&file->mut);
+ schedule();
+ mutex_lock(&file->mut);
+ finish_wait(&file->poll_wait, &wait);
+ }
+
+ if (ret)
+ goto done;
+
+ uevent = list_entry(file->event_list.next, struct ucma_event, list);
+
+ if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) {
+ ctx = ucma_alloc_ctx(file);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ uevent->ctx->backlog++;
+ ctx->cm_id = uevent->cm_id;
+ ctx->cm_id->context = ctx;
+ uevent->resp.id = ctx->id;
+ }
+
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &uevent->resp, sizeof uevent->resp)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ list_del(&uevent->list);
+ uevent->ctx->events_reported++;
+ kfree(uevent);
+done:
+ mutex_unlock(&file->mut);
+ return ret;
+}
+
+static ssize_t ucma_create_id(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_create_id cmd;
+ struct rdma_ucm_create_id_resp resp;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&file->mut);
+ ctx = ucma_alloc_ctx(file);
+ mutex_unlock(&file->mut);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->uid = cmd.uid;
+ ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, cmd.ps);
+ if (IS_ERR(ctx->cm_id)) {
+ ret = PTR_ERR(ctx->cm_id);
+ goto err1;
+ }
+
+ resp.id = ctx->id;
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err2;
+ }
+ return 0;
+
+err2:
+ rdma_destroy_id(ctx->cm_id);
+err1:
+ mutex_lock(&mut);
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&mut);
+ kfree(ctx);
+ return ret;
+}
+
+static void ucma_cleanup_events(struct ucma_context *ctx)
+{
+ struct ucma_event *uevent, *tmp;
+
+ list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) {
+ if (uevent->ctx != ctx)
+ continue;
+
+ list_del(&uevent->list);
+
+ /* clear incoming connections. */
+ if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST)
+ rdma_destroy_id(uevent->cm_id);
+
+ kfree(uevent);
+ }
+}
+
+static int ucma_free_ctx(struct ucma_context *ctx)
+{
+ int events_reported;
+
+ /* No new events will be generated after destroying the id. */
+ rdma_destroy_id(ctx->cm_id);
+
+ /* Cleanup events not yet reported to the user. */
+ mutex_lock(&ctx->file->mut);
+ ucma_cleanup_events(ctx);
+ list_del(&ctx->list);
+ mutex_unlock(&ctx->file->mut);
+
+ events_reported = ctx->events_reported;
+ kfree(ctx);
+ return events_reported;
+}
+
+static ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_destroy_id cmd;
+ struct rdma_ucm_destroy_id_resp resp;
+ struct ucma_context *ctx;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&mut);
+ ctx = _ucma_find_context(cmd.id, file);
+ if (!IS_ERR(ctx))
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&mut);
+
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ucma_put_ctx(ctx);
+ wait_for_completion(&ctx->comp);
+ resp.events_reported = ucma_free_ctx(ctx);
+
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static ssize_t ucma_bind_addr(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_bind_addr cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_resolve_addr(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_addr cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
+ (struct sockaddr *) &cmd.dst_addr,
+ cmd.timeout_ms);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_resolve_route(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_route cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_resolve_route(ctx->cm_id, cmd.timeout_ms);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp,
+ struct rdma_route *route)
+{
+ struct rdma_dev_addr *dev_addr;
+
+ resp->num_paths = route->num_paths;
+ switch (route->num_paths) {
+ case 0:
+ dev_addr = &route->addr.dev_addr;
+ ib_addr_get_dgid(dev_addr,
+ (union ib_gid *) &resp->ib_route[0].dgid);
+ ib_addr_get_sgid(dev_addr,
+ (union ib_gid *) &resp->ib_route[0].sgid);
+ resp->ib_route[0].pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr));
+ break;
+ case 2:
+ ib_copy_path_rec_to_user(&resp->ib_route[1],
+ &route->path_rec[1]);
+ /* fall through */
+ case 1:
+ ib_copy_path_rec_to_user(&resp->ib_route[0],
+ &route->path_rec[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+static ssize_t ucma_query_route(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_query_route cmd;
+ struct rdma_ucm_query_route_resp resp;
+ struct ucma_context *ctx;
+ struct sockaddr *addr;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ memset(&resp, 0, sizeof resp);
+ addr = &ctx->cm_id->route.addr.src_addr;
+ memcpy(&resp.src_addr, addr, addr->sa_family == AF_INET ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ addr = &ctx->cm_id->route.addr.dst_addr;
+ memcpy(&resp.dst_addr, addr, addr->sa_family == AF_INET ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ if (!ctx->cm_id->device)
+ goto out;
+
+ resp.node_guid = ctx->cm_id->device->node_guid;
+ resp.port_num = ctx->cm_id->port_num;
+ switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) {
+ case RDMA_TRANSPORT_IB:
+ ucma_copy_ib_route(&resp, &ctx->cm_id->route);
+ break;
+ default:
+ break;
+ }
+
+out:
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static void ucma_copy_conn_param(struct rdma_conn_param *dst,
+ struct rdma_ucm_conn_param *src)
+{
+ dst->private_data = src->private_data;
+ dst->private_data_len = src->private_data_len;
+ dst->responder_resources =src->responder_resources;
+ dst->initiator_depth = src->initiator_depth;
+ dst->flow_control = src->flow_control;
+ dst->retry_count = src->retry_count;
+ dst->rnr_retry_count = src->rnr_retry_count;
+ dst->srq = src->srq;
+ dst->qp_num = src->qp_num;
+}
+
+static ssize_t ucma_connect(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_connect cmd;
+ struct rdma_conn_param conn_param;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ if (!cmd.conn_param.valid)
+ return -EINVAL;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ret = rdma_connect(ctx->cm_id, &conn_param);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_listen cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->backlog = cmd.backlog > 0 && cmd.backlog < UCMA_MAX_BACKLOG ?
+ cmd.backlog : UCMA_MAX_BACKLOG;
+ ret = rdma_listen(ctx->cm_id, ctx->backlog);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_accept cmd;
+ struct rdma_conn_param conn_param;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ if (cmd.conn_param.valid) {
+ ctx->uid = cmd.uid;
+ ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ret = rdma_accept(ctx->cm_id, &conn_param);
+ } else
+ ret = rdma_accept(ctx->cm_id, NULL);
+
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_reject(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_reject cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_reject(ctx->cm_id, cmd.private_data, cmd.private_data_len);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_disconnect(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_disconnect cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_disconnect(ctx->cm_id);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_init_qp_attr(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_init_qp_attr cmd;
+ struct ib_uverbs_qp_attr resp;
+ struct ucma_context *ctx;
+ struct ib_qp_attr qp_attr;
+ int ret;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ resp.qp_attr_mask = 0;
+ memset(&qp_attr, 0, sizeof qp_attr);
+ qp_attr.qp_state = cmd.qp_state;
+ ret = rdma_init_qp_attr(ctx->cm_id, &qp_attr, &resp.qp_attr_mask);
+ if (ret)
+ goto out;
+
+ ib_copy_qp_attr_to_user(&resp, &qp_attr);
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+out:
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_notify cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_notify(ctx->cm_id, (enum ib_event_type) cmd.event);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t (*ucma_cmd_table[])(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len) = {
+ [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id,
+ [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id,
+ [RDMA_USER_CM_CMD_BIND_ADDR] = ucma_bind_addr,
+ [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr,
+ [RDMA_USER_CM_CMD_RESOLVE_ROUTE]= ucma_resolve_route,
+ [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route,
+ [RDMA_USER_CM_CMD_CONNECT] = ucma_connect,
+ [RDMA_USER_CM_CMD_LISTEN] = ucma_listen,
+ [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept,
+ [RDMA_USER_CM_CMD_REJECT] = ucma_reject,
+ [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect,
+ [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr,
+ [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event,
+ [RDMA_USER_CM_CMD_GET_OPTION] = NULL,
+ [RDMA_USER_CM_CMD_SET_OPTION] = NULL,
+ [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify,
+};
+
+static ssize_t ucma_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *pos)
+{
+ struct ucma_file *file = filp->private_data;
+ struct rdma_ucm_cmd_hdr hdr;
+ ssize_t ret;
+
+ if (len < sizeof(hdr))
+ return -EINVAL;
+
+ if (copy_from_user(&hdr, buf, sizeof(hdr)))
+ return -EFAULT;
+
+ if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
+ return -EINVAL;
+
+ if (hdr.in + sizeof(hdr) > len)
+ return -EINVAL;
+
+ if (!ucma_cmd_table[hdr.cmd])
+ return -ENOSYS;
+
+ ret = ucma_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out);
+ if (!ret)
+ ret = len;
+
+ return ret;
+}
+
+static unsigned int ucma_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct ucma_file *file = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &file->poll_wait, wait);
+
+ if (!list_empty(&file->event_list))
+ mask = POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int ucma_open(struct inode *inode, struct file *filp)
+{
+ struct ucma_file *file;
+
+ file = kmalloc(sizeof *file, GFP_KERNEL);
+ if (!file)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&file->event_list);
+ INIT_LIST_HEAD(&file->ctx_list);
+ init_waitqueue_head(&file->poll_wait);
+ mutex_init(&file->mut);
+
+ filp->private_data = file;
+ file->filp = filp;
+ return 0;
+}
+
+static int ucma_close(struct inode *inode, struct file *filp)
+{
+ struct ucma_file *file = filp->private_data;
+ struct ucma_context *ctx, *tmp;
+
+ mutex_lock(&file->mut);
+ list_for_each_entry_safe(ctx, tmp, &file->ctx_list, list) {
+ mutex_unlock(&file->mut);
+
+ mutex_lock(&mut);
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&mut);
+
+ ucma_free_ctx(ctx);
+ mutex_lock(&file->mut);
+ }
+ mutex_unlock(&file->mut);
+ kfree(file);
+ return 0;
+}
+
+static struct file_operations ucma_fops = {
+ .owner = THIS_MODULE,
+ .open = ucma_open,
+ .release = ucma_close,
+ .write = ucma_write,
+ .poll = ucma_poll,
+};
+
+static struct miscdevice ucma_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "rdma_cm",
+ .fops = &ucma_fops,
+};
+
+static ssize_t show_abi_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", RDMA_USER_CM_ABI_VERSION);
+}
+static DEVICE_ATTR(abi_version, S_IRUGO, show_abi_version, NULL);
+
+static int __init ucma_init(void)
+{
+ int ret;
+
+ ret = misc_register(&ucma_misc);
+ if (ret)
+ return ret;
+
+ ret = device_create_file(ucma_misc.this_device, &dev_attr_abi_version);
+ if (ret) {
+ printk(KERN_ERR "rdma_ucm: couldn't create abi_version attr\n");
+ goto err;
+ }
+ return 0;
+err:
+ misc_deregister(&ucma_misc);
+ return ret;
+}
+
+static void __exit ucma_cleanup(void)
+{
+ device_remove_file(ucma_misc.this_device, &dev_attr_abi_version);
+ misc_deregister(&ucma_misc);
+ idr_destroy(&ctx_idr);
+}
+
+module_init(ucma_init);
+module_exit(ucma_cleanup);
diff --git a/drivers/infiniband/core/uverbs_marshall.c b/drivers/infiniband/core/uverbs_marshall.c
index ce46b13..5440da0 100644
--- a/drivers/infiniband/core/uverbs_marshall.c
+++ b/drivers/infiniband/core/uverbs_marshall.c
@@ -32,8 +32,8 @@
#include <rdma/ib_marshall.h>
-static void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst,
- struct ib_ah_attr *src)
+void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst,
+ struct ib_ah_attr *src)
{
memcpy(dst->grh.dgid, src->grh.dgid.raw, sizeof src->grh.dgid);
dst->grh.flow_label = src->grh.flow_label;
@@ -47,6 +47,7 @@ static void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst,
dst->is_global = src->ah_flags & IB_AH_GRH ? 1 : 0;
dst->port_num = src->port_num;
}
+EXPORT_SYMBOL(ib_copy_ah_attr_to_user);
void ib_copy_qp_attr_to_user(struct ib_uverbs_qp_attr *dst,
struct ib_qp_attr *src)
diff --git a/drivers/infiniband/core/uverbs_mem.c b/drivers/infiniband/core/uverbs_mem.c
index db12cc0..c95fe952 100644
--- a/drivers/infiniband/core/uverbs_mem.c
+++ b/drivers/infiniband/core/uverbs_mem.c
@@ -52,8 +52,8 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d
int i;
list_for_each_entry_safe(chunk, tmp, &umem->chunk_list, list) {
- dma_unmap_sg(dev->dma_device, chunk->page_list,
- chunk->nents, DMA_BIDIRECTIONAL);
+ ib_dma_unmap_sg(dev, chunk->page_list,
+ chunk->nents, DMA_BIDIRECTIONAL);
for (i = 0; i < chunk->nents; ++i) {
if (umem->writable && dirty)
set_page_dirty_lock(chunk->page_list[i].page);
@@ -136,10 +136,10 @@ int ib_umem_get(struct ib_device *dev, struct ib_umem *mem,
chunk->page_list[i].length = PAGE_SIZE;
}
- chunk->nmap = dma_map_sg(dev->dma_device,
- &chunk->page_list[0],
- chunk->nents,
- DMA_BIDIRECTIONAL);
+ chunk->nmap = ib_dma_map_sg(dev,
+ &chunk->page_list[0],
+ chunk->nents,
+ DMA_BIDIRECTIONAL);
if (chunk->nmap <= 0) {
for (i = 0; i < chunk->nents; ++i)
put_page(chunk->page_list[i].page);
diff --git a/drivers/infiniband/hw/amso1100/c2_qp.c b/drivers/infiniband/hw/amso1100/c2_qp.c
index 179d005..420c138 100644
--- a/drivers/infiniband/hw/amso1100/c2_qp.c
+++ b/drivers/infiniband/hw/amso1100/c2_qp.c
@@ -161,8 +161,10 @@ int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
if (attr_mask & IB_QP_STATE) {
/* Ensure the state is valid */
- if (attr->qp_state < 0 || attr->qp_state > IB_QPS_ERR)
- return -EINVAL;
+ if (attr->qp_state < 0 || attr->qp_state > IB_QPS_ERR) {
+ err = -EINVAL;
+ goto bail0;
+ }
wr.next_qp_state = cpu_to_be32(to_c2_state(attr->qp_state));
@@ -184,9 +186,10 @@ int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
if (attr->cur_qp_state != IB_QPS_RTR &&
attr->cur_qp_state != IB_QPS_RTS &&
attr->cur_qp_state != IB_QPS_SQD &&
- attr->cur_qp_state != IB_QPS_SQE)
- return -EINVAL;
- else
+ attr->cur_qp_state != IB_QPS_SQE) {
+ err = -EINVAL;
+ goto bail0;
+ } else
wr.next_qp_state =
cpu_to_be32(to_c2_state(attr->cur_qp_state));
diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile
index 7dc1055..ec2e603 100644
--- a/drivers/infiniband/hw/ipath/Makefile
+++ b/drivers/infiniband/hw/ipath/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o
ib_ipath-y := \
ipath_cq.o \
ipath_diag.o \
+ ipath_dma.o \
ipath_driver.o \
ipath_eeprom.o \
ipath_file_ops.o \
diff --git a/drivers/infiniband/hw/ipath/ipath_dma.c b/drivers/infiniband/hw/ipath/ipath_dma.c
new file mode 100644
index 0000000..6e0f2b8
--- /dev/null
+++ b/drivers/infiniband/hw/ipath/ipath_dma.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2006 QLogic, Corporation. 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 <rdma/ib_verbs.h>
+
+#include "ipath_verbs.h"
+
+#define BAD_DMA_ADDRESS ((u64) 0)
+
+/*
+ * The following functions implement driver specific replacements
+ * for the ib_dma_*() functions.
+ *
+ * These functions return kernel virtual addresses instead of
+ * device bus addresses since the driver uses the CPU to copy
+ * data instead of using hardware DMA.
+ */
+
+static int ipath_mapping_error(struct ib_device *dev, u64 dma_addr)
+{
+ return dma_addr == BAD_DMA_ADDRESS;
+}
+
+static u64 ipath_dma_map_single(struct ib_device *dev,
+ void *cpu_addr, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+ return (u64) cpu_addr;
+}
+
+static void ipath_dma_unmap_single(struct ib_device *dev,
+ u64 addr, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+}
+
+static u64 ipath_dma_map_page(struct ib_device *dev,
+ struct page *page,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ u64 addr;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ if (offset + size > PAGE_SIZE) {
+ addr = BAD_DMA_ADDRESS;
+ goto done;
+ }
+
+ addr = (u64) page_address(page);
+ if (addr)
+ addr += offset;
+ /* TODO: handle highmem pages */
+
+done:
+ return addr;
+}
+
+static void ipath_dma_unmap_page(struct ib_device *dev,
+ u64 addr, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+}
+
+int ipath_map_sg(struct ib_device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ u64 addr;
+ int i;
+ int ret = nents;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ for (i = 0; i < nents; i++) {
+ addr = (u64) page_address(sg[i].page);
+ /* TODO: handle highmem pages */
+ if (!addr) {
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ipath_unmap_sg(struct ib_device *dev,
+ struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+}
+
+static u64 ipath_sg_dma_address(struct ib_device *dev, struct scatterlist *sg)
+{
+ u64 addr = (u64) page_address(sg->page);
+
+ if (addr)
+ addr += sg->offset;
+ return addr;
+}
+
+static unsigned int ipath_sg_dma_len(struct ib_device *dev,
+ struct scatterlist *sg)
+{
+ return sg->length;
+}
+
+static void ipath_sync_single_for_cpu(struct ib_device *dev,
+ u64 addr,
+ size_t size,
+ enum dma_data_direction dir)
+{
+}
+
+static void ipath_sync_single_for_device(struct ib_device *dev,
+ u64 addr,
+ size_t size,
+ enum dma_data_direction dir)
+{
+}
+
+static void *ipath_dma_alloc_coherent(struct ib_device *dev, size_t size,
+ u64 *dma_handle, gfp_t flag)
+{
+ struct page *p;
+ void *addr = NULL;
+
+ p = alloc_pages(flag, get_order(size));
+ if (p)
+ addr = page_address(p);
+ if (dma_handle)
+ *dma_handle = (u64) addr;
+ return addr;
+}
+
+static void ipath_dma_free_coherent(struct ib_device *dev, size_t size,
+ void *cpu_addr, dma_addr_t dma_handle)
+{
+ free_pages((unsigned long) cpu_addr, get_order(size));
+}
+
+struct ib_dma_mapping_ops ipath_dma_mapping_ops = {
+ ipath_mapping_error,
+ ipath_dma_map_single,
+ ipath_dma_unmap_single,
+ ipath_dma_map_page,
+ ipath_dma_unmap_page,
+ ipath_map_sg,
+ ipath_unmap_sg,
+ ipath_sg_dma_address,
+ ipath_sg_dma_len,
+ ipath_sync_single_for_cpu,
+ ipath_sync_single_for_device,
+ ipath_dma_alloc_coherent,
+ ipath_dma_free_coherent
+};
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index 1aeddb4..ae7f21a 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -1825,8 +1825,6 @@ void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno,
*/
void ipath_shutdown_device(struct ipath_devdata *dd)
{
- u64 val;
-
ipath_dbg("Shutting down the device\n");
dd->ipath_flags |= IPATH_LINKUNK;
@@ -1849,7 +1847,7 @@ void ipath_shutdown_device(struct ipath_devdata *dd)
*/
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0ULL);
/* flush it */
- val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
/*
* enough for anything that's going to trickle out to have actually
* done so.
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 340f27e..b932bcb 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -699,7 +699,6 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
int start_stop)
{
struct ipath_devdata *dd = pd->port_dd;
- u64 tval;
ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n",
start_stop ? "en" : "dis", dd->ipath_unit,
@@ -729,7 +728,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
/* now be sure chip saw it before we return */
- tval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
if (start_stop) {
/*
* And try to be sure that tail reg update has happened too.
@@ -738,7 +737,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
* in memory copy, since we could overwrite an update by the
* chip if we did.
*/
- tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
+ ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
}
/* always; new head should be equal to new tail; see above */
bail:
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index e57c7a3..7468477 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -1447,7 +1447,7 @@ static void ipath_ht_tidtemplate(struct ipath_devdata *dd)
static int ipath_ht_early_init(struct ipath_devdata *dd)
{
u32 __iomem *piobuf;
- u32 pioincr, val32, egrsize;
+ u32 pioincr, val32;
int i;
/*
@@ -1467,7 +1467,6 @@ static int ipath_ht_early_init(struct ipath_devdata *dd)
* errors interrupts if we ever see one).
*/
dd->ipath_rcvegrbufsize = dd->ipath_piosize2k;
- egrsize = dd->ipath_rcvegrbufsize;
/*
* the min() check here is currently a nop, but it may not
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index 6af8968..ae8bf99 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -602,7 +602,7 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
*/
static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
{
- u64 val, tmp, config1, prev_val;
+ u64 val, config1, prev_val;
int ret = 0;
ipath_dbg("Trying to bringup serdes\n");
@@ -633,7 +633,7 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
| INFINIPATH_SERDC0_L1PWR_DN;
ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
/* be sure chip saw it */
- tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
udelay(5); /* need pll reset set at least for a bit */
/*
* after PLL is reset, set the per-lane Resets and TxIdle and
@@ -647,7 +647,7 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
"and txidle (%llx)\n", (unsigned long long) val);
ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
/* be sure chip saw it */
- tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
/* need PLL reset clear for at least 11 usec before lane
* resets cleared; give it a few more to be sure */
udelay(15);
@@ -851,12 +851,12 @@ static int ipath_setup_pe_config(struct ipath_devdata *dd,
int pos, ret;
dd->ipath_msi_lo = 0; /* used as a flag during reset processing */
- dd->ipath_irq = pdev->irq;
ret = pci_enable_msi(dd->pcidev);
if (ret)
ipath_dev_err(dd, "pci_enable_msi failed: %d, "
"interrupts may not work\n", ret);
/* continue even if it fails, we may still be OK... */
+ dd->ipath_irq = pdev->irq;
if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) {
u16 control;
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index d819cca..d4f6b523 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -347,10 +347,9 @@ done:
static int init_chip_reset(struct ipath_devdata *dd,
struct ipath_portdata **pdp)
{
- struct ipath_portdata *pd;
u32 rtmp;
- *pdp = pd = dd->ipath_pd[0];
+ *pdp = dd->ipath_pd[0];
/* ensure chip does no sends or receives while we re-initialize */
dd->ipath_control = dd->ipath_sendctrl = dd->ipath_rcvctrl = 0U;
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, 0);
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 5652a55..72b9e27 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -598,10 +598,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* on close
*/
if (errs & INFINIPATH_E_RRCVHDRFULL) {
- int any;
u32 hd, tl;
ipath_stats.sps_hdrqfull++;
- for (any = i = 0; i < dd->ipath_cfgports; i++) {
+ for (i = 0; i < dd->ipath_cfgports; i++) {
struct ipath_portdata *pd = dd->ipath_pd[i];
if (i == 0) {
hd = dd->ipath_port0head;
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index 9a6cbd0..851763d 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -134,7 +134,7 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
*/
if (sge->lkey == 0) {
isge->mr = NULL;
- isge->vaddr = bus_to_virt(sge->addr);
+ isge->vaddr = (void *) sge->addr;
isge->length = sge->length;
isge->sge_length = sge->length;
ret = 1;
@@ -202,12 +202,12 @@ int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
int ret;
/*
- * We use RKEY == zero for physical addresses
- * (see ipath_get_dma_mr).
+ * We use RKEY == zero for kernel virtual addresses
+ * (see ipath_get_dma_mr and ipath_dma.c).
*/
if (rkey == 0) {
sge->mr = NULL;
- sge->vaddr = phys_to_virt(vaddr);
+ sge->vaddr = (void *) vaddr;
sge->length = len;
sge->sge_length = len;
ss->sg_list = NULL;
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index a0673c1e..8cc8598 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -54,6 +54,8 @@ static inline struct ipath_fmr *to_ifmr(struct ib_fmr *ibfmr)
* @acc: access flags
*
* Returns the memory region on success, otherwise returns an errno.
+ * Note that all DMA addresses should be created via the
+ * struct ib_dma_mapping_ops functions (see ipath_dma.c).
*/
struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc)
{
@@ -149,8 +151,7 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
m = 0;
n = 0;
for (i = 0; i < num_phys_buf; i++) {
- mr->mr.map[m]->segs[n].vaddr =
- phys_to_virt(buffer_list[i].addr);
+ mr->mr.map[m]->segs[n].vaddr = (void *) buffer_list[i].addr;
mr->mr.map[m]->segs[n].length = buffer_list[i].size;
mr->mr.length += buffer_list[i].size;
n++;
@@ -347,7 +348,7 @@ int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list,
n = 0;
ps = 1 << fmr->page_shift;
for (i = 0; i < list_len; i++) {
- fmr->mr.map[m]->segs[n].vaddr = phys_to_virt(page_list[i]);
+ fmr->mr.map[m]->segs[n].vaddr = (void *) page_list[i];
fmr->mr.map[m]->segs[n].length = ps;
if (++n == IPATH_SEGSZ) {
m++;
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
index 182de34..ffa6318 100644
--- a/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -215,7 +215,6 @@ static ssize_t store_mlid(struct device *dev,
size_t count)
{
struct ipath_devdata *dd = dev_get_drvdata(dev);
- int unit;
u16 mlid;
int ret;
@@ -223,8 +222,6 @@ static ssize_t store_mlid(struct device *dev,
if (ret < 0 || mlid < IPATH_MULTICAST_LID_BASE)
goto invalid;
- unit = dd->ipath_unit;
-
dd->ipath_mlid = mlid;
goto bail;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index acdee33..2aaacdb 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -1599,6 +1599,7 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
dev->detach_mcast = ipath_multicast_detach;
dev->process_mad = ipath_process_mad;
dev->mmap = ipath_mmap;
+ dev->dma_ops = &ipath_dma_mapping_ops;
snprintf(dev->node_desc, sizeof(dev->node_desc),
IPATH_IDSTR " %s", init_utsname()->nodename);
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index 8039f6e..c0c8d5b 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -812,4 +812,6 @@ extern unsigned int ib_ipath_max_srq_wrs;
extern const u32 ib_ipath_rnr_table[];
+extern struct ib_dma_mapping_ops ipath_dma_mapping_ops;
+
#endif /* IPATH_VERBS_H */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 9954799..07deee8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -105,12 +105,12 @@ struct ipoib_mcast;
struct ipoib_rx_buf {
struct sk_buff *skb;
- dma_addr_t mapping;
+ u64 mapping;
};
struct ipoib_tx_buf {
struct sk_buff *skb;
- DECLARE_PCI_UNMAP_ADDR(mapping)
+ u64 mapping;
};
/*
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index f10fba5d..59d9594 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -109,9 +109,8 @@ static int ipoib_ib_post_receive(struct net_device *dev, int id)
ret = ib_post_recv(priv->qp, &param, &bad_wr);
if (unlikely(ret)) {
ipoib_warn(priv, "receive failed for buf %d (%d)\n", id, ret);
- dma_unmap_single(priv->ca->dma_device,
- priv->rx_ring[id].mapping,
- IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_single(priv->ca, priv->rx_ring[id].mapping,
+ IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(priv->rx_ring[id].skb);
priv->rx_ring[id].skb = NULL;
}
@@ -123,7 +122,7 @@ static int ipoib_alloc_rx_skb(struct net_device *dev, int id)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
- dma_addr_t addr;
+ u64 addr;
skb = dev_alloc_skb(IPOIB_BUF_SIZE + 4);
if (!skb)
@@ -136,10 +135,9 @@ static int ipoib_alloc_rx_skb(struct net_device *dev, int id)
*/
skb_reserve(skb, 4);
- addr = dma_map_single(priv->ca->dma_device,
- skb->data, IPOIB_BUF_SIZE,
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(addr))) {
+ addr = ib_dma_map_single(priv->ca, skb->data, IPOIB_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(ib_dma_mapping_error(priv->ca, addr))) {
dev_kfree_skb_any(skb);
return -EIO;
}
@@ -174,7 +172,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
struct ipoib_dev_priv *priv = netdev_priv(dev);
unsigned int wr_id = wc->wr_id & ~IPOIB_OP_RECV;
struct sk_buff *skb;
- dma_addr_t addr;
+ u64 addr;
ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n",
wr_id, wc->opcode, wc->status);
@@ -193,8 +191,8 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
ipoib_warn(priv, "failed recv event "
"(status=%d, wrid=%d vend_err %x)\n",
wc->status, wr_id, wc->vendor_err);
- dma_unmap_single(priv->ca->dma_device, addr,
- IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_single(priv->ca, addr,
+ IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
priv->rx_ring[wr_id].skb = NULL;
return;
@@ -212,8 +210,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
ipoib_dbg_data(priv, "received %d bytes, SLID 0x%04x\n",
wc->byte_len, wc->slid);
- dma_unmap_single(priv->ca->dma_device, addr,
- IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
+ ib_dma_unmap_single(priv->ca, addr, IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
skb_put(skb, wc->byte_len);
skb_pull(skb, IB_GRH_BYTES);
@@ -261,10 +258,8 @@ static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
tx_req = &priv->tx_ring[wr_id];
- dma_unmap_single(priv->ca->dma_device,
- pci_unmap_addr(tx_req, mapping),
- tx_req->skb->len,
- DMA_TO_DEVICE);
+ ib_dma_unmap_single(priv->ca, tx_req->mapping,
+ tx_req->skb->len, DMA_TO_DEVICE);
++priv->stats.tx_packets;
priv->stats.tx_bytes += tx_req->skb->len;
@@ -311,7 +306,7 @@ void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr)
static inline int post_send(struct ipoib_dev_priv *priv,
unsigned int wr_id,
struct ib_ah *address, u32 qpn,
- dma_addr_t addr, int len)
+ u64 addr, int len)
{
struct ib_send_wr *bad_wr;
@@ -330,7 +325,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_tx_buf *tx_req;
- dma_addr_t addr;
+ u64 addr;
if (unlikely(skb->len > dev->mtu + INFINIBAND_ALEN)) {
ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
@@ -353,21 +348,20 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
*/
tx_req = &priv->tx_ring[priv->tx_head & (ipoib_sendq_size - 1)];
tx_req->skb = skb;
- addr = dma_map_single(priv->ca->dma_device, skb->data, skb->len,
- DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(addr))) {
+ addr = ib_dma_map_single(priv->ca, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ if (unlikely(ib_dma_mapping_error(priv->ca, addr))) {
++priv->stats.tx_errors;
dev_kfree_skb_any(skb);
return;
}
- pci_unmap_addr_set(tx_req, mapping, addr);
+ tx_req->mapping = addr;
if (unlikely(post_send(priv, priv->tx_head & (ipoib_sendq_size - 1),
address->ah, qpn, addr, skb->len))) {
ipoib_warn(priv, "post_send failed\n");
++priv->stats.tx_errors;
- dma_unmap_single(priv->ca->dma_device, addr, skb->len,
- DMA_TO_DEVICE);
+ ib_dma_unmap_single(priv->ca, addr, skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
} else {
dev->trans_start = jiffies;
@@ -538,24 +532,27 @@ int ipoib_ib_dev_stop(struct net_device *dev)
while ((int) priv->tx_tail - (int) priv->tx_head < 0) {
tx_req = &priv->tx_ring[priv->tx_tail &
(ipoib_sendq_size - 1)];
- dma_unmap_single(priv->ca->dma_device,
- pci_unmap_addr(tx_req, mapping),
- tx_req->skb->len,
- DMA_TO_DEVICE);
+ ib_dma_unmap_single(priv->ca,
+ tx_req->mapping,
+ tx_req->skb->len,
+ DMA_TO_DEVICE);
dev_kfree_skb_any(tx_req->skb);
++priv->tx_tail;
}
- for (i = 0; i < ipoib_recvq_size; ++i)
- if (priv->rx_ring[i].skb) {
- dma_unmap_single(priv->ca->dma_device,
- pci_unmap_addr(&priv->rx_ring[i],
- mapping),
- IPOIB_BUF_SIZE,
- DMA_FROM_DEVICE);
- dev_kfree_skb_any(priv->rx_ring[i].skb);
- priv->rx_ring[i].skb = NULL;
- }
+ for (i = 0; i < ipoib_recvq_size; ++i) {
+ struct ipoib_rx_buf *rx_req;
+
+ rx_req = &priv->rx_ring[i];
+ if (!rx_req->skb)
+ continue;
+ ib_dma_unmap_single(priv->ca,
+ rx_req->mapping,
+ IPOIB_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(rx_req->skb);
+ rx_req->skb = NULL;
+ }
goto timeout;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index c092802..705eb1d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -497,8 +497,6 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
return;
}
- skb_queue_head_init(&neigh->queue);
-
/*
* We can only be called from ipoib_start_xmit, so we're
* inside tx_lock -- no need to save/restore flags.
@@ -806,6 +804,7 @@ struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour)
neigh->neighbour = neighbour;
*to_ipoib_neigh(neighbour) = neigh;
+ skb_queue_head_init(&neigh->queue);
return neigh;
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 234e5b0..cae8c96 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -182,7 +182,7 @@ struct iser_regd_buf {
struct iser_mem_reg reg; /* memory registration info */
void *virt_addr;
struct iser_device *device; /* device->device for dma_unmap */
- dma_addr_t dma_addr; /* if non zero, addr for dma_unmap */
+ u64 dma_addr; /* if non zero, addr for dma_unmap */
enum dma_data_direction direction; /* direction for dma_unmap */
unsigned int data_size;
atomic_t ref_count; /* refcount, freed when dec to 0 */
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 9b3d79c..e73c87b 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -487,10 +487,8 @@ int iser_send_control(struct iscsi_conn *conn,
struct iscsi_iser_conn *iser_conn = conn->dd_data;
struct iser_desc *mdesc = mtask->dd_data;
struct iser_dto *send_dto = NULL;
- unsigned int itt;
unsigned long data_seg_len;
int err = 0;
- unsigned char opcode;
struct iser_regd_buf *regd_buf;
struct iser_device *device;
@@ -512,8 +510,6 @@ int iser_send_control(struct iscsi_conn *conn,
iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
- itt = ntohl(mtask->hdr->itt);
- opcode = mtask->hdr->opcode & ISCSI_OPCODE_MASK;
data_seg_len = ntoh24(mtask->hdr->dlength);
if (data_seg_len > 0) {
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index 3aedd59..fc9f1fd 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -52,7 +52,7 @@
*/
int iser_regd_buff_release(struct iser_regd_buf *regd_buf)
{
- struct device *dma_device;
+ struct ib_device *dev;
if ((atomic_read(&regd_buf->ref_count) == 0) ||
atomic_dec_and_test(&regd_buf->ref_count)) {
@@ -61,8 +61,8 @@ int iser_regd_buff_release(struct iser_regd_buf *regd_buf)
iser_unreg_mem(&regd_buf->reg);
if (regd_buf->dma_addr) {
- dma_device = regd_buf->device->ib_device->dma_device;
- dma_unmap_single(dma_device,
+ dev = regd_buf->device->ib_device;
+ ib_dma_unmap_single(dev,
regd_buf->dma_addr,
regd_buf->data_size,
regd_buf->direction);
@@ -84,12 +84,12 @@ void iser_reg_single(struct iser_device *device,
struct iser_regd_buf *regd_buf,
enum dma_data_direction direction)
{
- dma_addr_t dma_addr;
+ u64 dma_addr;
- dma_addr = dma_map_single(device->ib_device->dma_device,
- regd_buf->virt_addr,
- regd_buf->data_size, direction);
- BUG_ON(dma_mapping_error(dma_addr));
+ dma_addr = ib_dma_map_single(device->ib_device,
+ regd_buf->virt_addr,
+ regd_buf->data_size, direction);
+ BUG_ON(ib_dma_mapping_error(device->ib_device, dma_addr));
regd_buf->reg.lkey = device->mr->lkey;
regd_buf->reg.len = regd_buf->data_size;
@@ -107,7 +107,7 @@ int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
enum iser_data_dir cmd_dir)
{
int dma_nents;
- struct device *dma_device;
+ struct ib_device *dev;
char *mem = NULL;
struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
unsigned long cmd_data_len = data->data_len;
@@ -147,17 +147,12 @@ int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
iser_ctask->data_copy[cmd_dir].copy_buf = mem;
- dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
-
- if (cmd_dir == ISER_DIR_OUT)
- dma_nents = dma_map_sg(dma_device,
- &iser_ctask->data_copy[cmd_dir].sg_single,
- 1, DMA_TO_DEVICE);
- else
- dma_nents = dma_map_sg(dma_device,
- &iser_ctask->data_copy[cmd_dir].sg_single,
- 1, DMA_FROM_DEVICE);
-
+ dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+ dma_nents = ib_dma_map_sg(dev,
+ &iser_ctask->data_copy[cmd_dir].sg_single,
+ 1,
+ (cmd_dir == ISER_DIR_OUT) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
BUG_ON(dma_nents == 0);
iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
@@ -170,19 +165,16 @@ int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
enum iser_data_dir cmd_dir)
{
- struct device *dma_device;
+ struct ib_device *dev;
struct iser_data_buf *mem_copy;
unsigned long cmd_data_len;
- dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
- mem_copy = &iser_ctask->data_copy[cmd_dir];
+ dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+ mem_copy = &iser_ctask->data_copy[cmd_dir];
- if (cmd_dir == ISER_DIR_OUT)
- dma_unmap_sg(dma_device, &mem_copy->sg_single, 1,
- DMA_TO_DEVICE);
- else
- dma_unmap_sg(dma_device, &mem_copy->sg_single, 1,
- DMA_FROM_DEVICE);
+ ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
+ (cmd_dir == ISER_DIR_OUT) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (cmd_dir == ISER_DIR_IN) {
char *mem;
@@ -231,11 +223,12 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
* consecutive elements. Also, it handles one entry SG.
*/
static int iser_sg_to_page_vec(struct iser_data_buf *data,
- struct iser_page_vec *page_vec)
+ struct iser_page_vec *page_vec,
+ struct ib_device *ibdev)
{
struct scatterlist *sg = (struct scatterlist *)data->buf;
- dma_addr_t first_addr, last_addr, page;
- int start_aligned, end_aligned;
+ u64 first_addr, last_addr, page;
+ int end_aligned;
unsigned int cur_page = 0;
unsigned long total_sz = 0;
int i;
@@ -244,19 +237,21 @@ static int iser_sg_to_page_vec(struct iser_data_buf *data,
page_vec->offset = (u64) sg[0].offset & ~MASK_4K;
for (i = 0; i < data->dma_nents; i++) {
- total_sz += sg_dma_len(&sg[i]);
+ unsigned int dma_len = ib_sg_dma_len(ibdev, &sg[i]);
+
+ total_sz += dma_len;
- first_addr = sg_dma_address(&sg[i]);
- last_addr = first_addr + sg_dma_len(&sg[i]);
+ first_addr = ib_sg_dma_address(ibdev, &sg[i]);
+ last_addr = first_addr + dma_len;
- start_aligned = !(first_addr & ~MASK_4K);
end_aligned = !(last_addr & ~MASK_4K);
/* continue to collect page fragments till aligned or SG ends */
while (!end_aligned && (i + 1 < data->dma_nents)) {
i++;
- total_sz += sg_dma_len(&sg[i]);
- last_addr = sg_dma_address(&sg[i]) + sg_dma_len(&sg[i]);
+ dma_len = ib_sg_dma_len(ibdev, &sg[i]);
+ total_sz += dma_len;
+ last_addr = ib_sg_dma_address(ibdev, &sg[i]) + dma_len;
end_aligned = !(last_addr & ~MASK_4K);
}
@@ -288,10 +283,11 @@ static int iser_sg_to_page_vec(struct iser_data_buf *data,
* the number of entries which are aligned correctly. Supports the case where
* consecutive SG elements are actually fragments of the same physcial page.
*/
-static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data)
+static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data,
+ struct ib_device *ibdev)
{
struct scatterlist *sg;
- dma_addr_t end_addr, next_addr;
+ u64 end_addr, next_addr;
int i, cnt;
unsigned int ret_len = 0;
@@ -303,12 +299,12 @@ static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data)
(unsigned long)page_to_phys(sg[i].page),
(unsigned long)sg[i].offset,
(unsigned long)sg[i].length); */
- end_addr = sg_dma_address(&sg[i]) +
- sg_dma_len(&sg[i]);
+ end_addr = ib_sg_dma_address(ibdev, &sg[i]) +
+ ib_sg_dma_len(ibdev, &sg[i]);
/* iser_dbg("Checking sg iobuf end address "
"0x%08lX\n", end_addr); */
if (i + 1 < data->dma_nents) {
- next_addr = sg_dma_address(&sg[i+1]);
+ next_addr = ib_sg_dma_address(ibdev, &sg[i+1]);
/* are i, i+1 fragments of the same page? */
if (end_addr == next_addr)
continue;
@@ -325,7 +321,8 @@ static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data)
return ret_len;
}
-static void iser_data_buf_dump(struct iser_data_buf *data)
+static void iser_data_buf_dump(struct iser_data_buf *data,
+ struct ib_device *ibdev)
{
struct scatterlist *sg = (struct scatterlist *)data->buf;
int i;
@@ -333,9 +330,9 @@ static void iser_data_buf_dump(struct iser_data_buf *data)
for (i = 0; i < data->dma_nents; i++)
iser_err("sg[%d] dma_addr:0x%lX page:0x%p "
"off:0x%x sz:0x%x dma_len:0x%x\n",
- i, (unsigned long)sg_dma_address(&sg[i]),
+ i, (unsigned long)ib_sg_dma_address(ibdev, &sg[i]),
sg[i].page, sg[i].offset,
- sg[i].length,sg_dma_len(&sg[i]));
+ sg[i].length, ib_sg_dma_len(ibdev, &sg[i]));
}
static void iser_dump_page_vec(struct iser_page_vec *page_vec)
@@ -349,7 +346,8 @@ static void iser_dump_page_vec(struct iser_page_vec *page_vec)
}
static void iser_page_vec_build(struct iser_data_buf *data,
- struct iser_page_vec *page_vec)
+ struct iser_page_vec *page_vec,
+ struct ib_device *ibdev)
{
int page_vec_len = 0;
@@ -357,14 +355,14 @@ static void iser_page_vec_build(struct iser_data_buf *data,
page_vec->offset = 0;
iser_dbg("Translating sg sz: %d\n", data->dma_nents);
- page_vec_len = iser_sg_to_page_vec(data,page_vec);
+ page_vec_len = iser_sg_to_page_vec(data, page_vec, ibdev);
iser_dbg("sg len %d page_vec_len %d\n", data->dma_nents,page_vec_len);
page_vec->length = page_vec_len;
if (page_vec_len * SIZE_4K < page_vec->data_size) {
iser_err("page_vec too short to hold this SG\n");
- iser_data_buf_dump(data);
+ iser_data_buf_dump(data, ibdev);
iser_dump_page_vec(page_vec);
BUG();
}
@@ -375,13 +373,12 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir)
{
- struct device *dma_device;
+ struct ib_device *dev;
iser_ctask->dir[iser_dir] = 1;
- dma_device =
- iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+ dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
- data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir);
+ data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
if (data->dma_nents == 0) {
iser_err("dma_map_sg failed!!!\n");
return -EINVAL;
@@ -391,20 +388,19 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
{
- struct device *dma_device;
+ struct ib_device *dev;
struct iser_data_buf *data;
- dma_device =
- iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+ dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
if (iser_ctask->dir[ISER_DIR_IN]) {
data = &iser_ctask->data[ISER_DIR_IN];
- dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE);
+ ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
}
if (iser_ctask->dir[ISER_DIR_OUT]) {
data = &iser_ctask->data[ISER_DIR_OUT];
- dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE);
+ ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
}
}
@@ -419,6 +415,7 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
{
struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
struct iser_device *device = ib_conn->device;
+ struct ib_device *ibdev = device->ib_device;
struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
struct iser_regd_buf *regd_buf;
int aligned_len;
@@ -428,11 +425,11 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
regd_buf = &iser_ctask->rdma_regd[cmd_dir];
- aligned_len = iser_data_buf_aligned_len(mem);
+ aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) {
iser_err("rdma alignment violation %d/%d aligned\n",
aligned_len, mem->size);
- iser_data_buf_dump(mem);
+ iser_data_buf_dump(mem, ibdev);
/* unmap the command data before accessing it */
iser_dma_unmap_task_data(iser_ctask);
@@ -450,8 +447,8 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
regd_buf->reg.lkey = device->mr->lkey;
regd_buf->reg.rkey = device->mr->rkey;
- regd_buf->reg.len = sg_dma_len(&sg[0]);
- regd_buf->reg.va = sg_dma_address(&sg[0]);
+ regd_buf->reg.len = ib_sg_dma_len(ibdev, &sg[0]);
+ regd_buf->reg.va = ib_sg_dma_address(ibdev, &sg[0]);
regd_buf->reg.is_fmr = 0;
iser_dbg("PHYSICAL Mem.register: lkey: 0x%08X rkey: 0x%08X "
@@ -461,10 +458,10 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
(unsigned long)regd_buf->reg.va,
(unsigned long)regd_buf->reg.len);
} else { /* use FMR for multiple dma entries */
- iser_page_vec_build(mem, ib_conn->page_vec);
+ iser_page_vec_build(mem, ib_conn->page_vec, ibdev);
err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
if (err) {
- iser_data_buf_dump(mem);
+ iser_data_buf_dump(mem, ibdev);
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
ntoh24(iser_ctask->desc.iscsi_header.dlength));
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index a628959..e9b6a6f 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -122,9 +122,8 @@ static struct srp_iu *srp_alloc_iu(struct srp_host *host, size_t size,
if (!iu->buf)
goto out_free_iu;
- iu->dma = dma_map_single(host->dev->dev->dma_device,
- iu->buf, size, direction);
- if (dma_mapping_error(iu->dma))
+ iu->dma = ib_dma_map_single(host->dev->dev, iu->buf, size, direction);
+ if (ib_dma_mapping_error(host->dev->dev, iu->dma))
goto out_free_buf;
iu->size = size;
@@ -145,8 +144,7 @@ static void srp_free_iu(struct srp_host *host, struct srp_iu *iu)
if (!iu)
return;
- dma_unmap_single(host->dev->dev->dma_device,
- iu->dma, iu->size, iu->direction);
+ ib_dma_unmap_single(host->dev->dev, iu->dma, iu->size, iu->direction);
kfree(iu->buf);
kfree(iu);
}
@@ -482,8 +480,8 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
scat = &req->fake_sg;
}
- dma_unmap_sg(target->srp_host->dev->dev->dma_device, scat, nents,
- scmnd->sc_data_direction);
+ ib_dma_unmap_sg(target->srp_host->dev->dev, scat, nents,
+ scmnd->sc_data_direction);
}
static void srp_remove_req(struct srp_target_port *target, struct srp_request *req)
@@ -595,23 +593,26 @@ static int srp_map_fmr(struct srp_target_port *target, struct scatterlist *scat,
int i, j;
int ret;
struct srp_device *dev = target->srp_host->dev;
+ struct ib_device *ibdev = dev->dev;
if (!dev->fmr_pool)
return -ENODEV;
- if ((sg_dma_address(&scat[0]) & ~dev->fmr_page_mask) &&
+ if ((ib_sg_dma_address(ibdev, &scat[0]) & ~dev->fmr_page_mask) &&
mellanox_workarounds && !memcmp(&target->ioc_guid, mellanox_oui, 3))
return -EINVAL;
len = page_cnt = 0;
for (i = 0; i < sg_cnt; ++i) {
- if (sg_dma_address(&scat[i]) & ~dev->fmr_page_mask) {
+ unsigned int dma_len = ib_sg_dma_len(ibdev, &scat[i]);
+
+ if (ib_sg_dma_address(ibdev, &scat[i]) & ~dev->fmr_page_mask) {
if (i > 0)
return -EINVAL;
else
++page_cnt;
}
- if ((sg_dma_address(&scat[i]) + sg_dma_len(&scat[i])) &
+ if ((ib_sg_dma_address(ibdev, &scat[i]) + dma_len) &
~dev->fmr_page_mask) {
if (i < sg_cnt - 1)
return -EINVAL;
@@ -619,7 +620,7 @@ static int srp_map_fmr(struct srp_target_port *target, struct scatterlist *scat,
++page_cnt;
}
- len += sg_dma_len(&scat[i]);
+ len += dma_len;
}
page_cnt += len >> dev->fmr_page_shift;
@@ -631,10 +632,14 @@ static int srp_map_fmr(struct srp_target_port *target, struct scatterlist *scat,
return -ENOMEM;
page_cnt = 0;
- for (i = 0; i < sg_cnt; ++i)
- for (j = 0; j < sg_dma_len(&scat[i]); j += dev->fmr_page_size)
+ for (i = 0; i < sg_cnt; ++i) {
+ unsigned int dma_len = ib_sg_dma_len(ibdev, &scat[i]);
+
+ for (j = 0; j < dma_len; j += dev->fmr_page_size)
dma_pages[page_cnt++] =
- (sg_dma_address(&scat[i]) & dev->fmr_page_mask) + j;
+ (ib_sg_dma_address(ibdev, &scat[i]) &
+ dev->fmr_page_mask) + j;
+ }
req->fmr = ib_fmr_pool_map_phys(dev->fmr_pool,
dma_pages, page_cnt, io_addr);
@@ -644,7 +649,8 @@ static int srp_map_fmr(struct srp_target_port *target, struct scatterlist *scat,
goto out;
}
- buf->va = cpu_to_be64(sg_dma_address(&scat[0]) & ~dev->fmr_page_mask);
+ buf->va = cpu_to_be64(ib_sg_dma_address(ibdev, &scat[0]) &
+ ~dev->fmr_page_mask);
buf->key = cpu_to_be32(req->fmr->fmr->rkey);
buf->len = cpu_to_be32(len);
@@ -663,6 +669,8 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
struct srp_cmd *cmd = req->cmd->buf;
int len, nents, count;
u8 fmt = SRP_DATA_DESC_DIRECT;
+ struct srp_device *dev;
+ struct ib_device *ibdev;
if (!scmnd->request_buffer || scmnd->sc_data_direction == DMA_NONE)
return sizeof (struct srp_cmd);
@@ -687,8 +695,10 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
sg_init_one(scat, scmnd->request_buffer, scmnd->request_bufflen);
}
- count = dma_map_sg(target->srp_host->dev->dev->dma_device,
- scat, nents, scmnd->sc_data_direction);
+ dev = target->srp_host->dev;
+ ibdev = dev->dev;
+
+ count = ib_dma_map_sg(ibdev, scat, nents, scmnd->sc_data_direction);
fmt = SRP_DATA_DESC_DIRECT;
len = sizeof (struct srp_cmd) + sizeof (struct srp_direct_buf);
@@ -702,9 +712,9 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
*/
struct srp_direct_buf *buf = (void *) cmd->add_data;
- buf->va = cpu_to_be64(sg_dma_address(scat));
- buf->key = cpu_to_be32(target->srp_host->dev->mr->rkey);
- buf->len = cpu_to_be32(sg_dma_len(scat));
+ buf->va = cpu_to_be64(ib_sg_dma_address(ibdev, scat));
+ buf->key = cpu_to_be32(dev->mr->rkey);
+ buf->len = cpu_to_be32(ib_sg_dma_len(ibdev, scat));
} else if (srp_map_fmr(target, scat, count, req,
(void *) cmd->add_data)) {
/*
@@ -722,13 +732,14 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
count * sizeof (struct srp_direct_buf);
for (i = 0; i < count; ++i) {
+ unsigned int dma_len = ib_sg_dma_len(ibdev, &scat[i]);
+
buf->desc_list[i].va =
- cpu_to_be64(sg_dma_address(&scat[i]));
+ cpu_to_be64(ib_sg_dma_address(ibdev, &scat[i]));
buf->desc_list[i].key =
- cpu_to_be32(target->srp_host->dev->mr->rkey);
- buf->desc_list[i].len =
- cpu_to_be32(sg_dma_len(&scat[i]));
- datalen += sg_dma_len(&scat[i]);
+ cpu_to_be32(dev->mr->rkey);
+ buf->desc_list[i].len = cpu_to_be32(dma_len);
+ datalen += dma_len;
}
if (scmnd->sc_data_direction == DMA_TO_DEVICE)
@@ -808,13 +819,15 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
{
+ struct ib_device *dev;
struct srp_iu *iu;
u8 opcode;
iu = target->rx_ring[wc->wr_id & ~SRP_OP_RECV];
- dma_sync_single_for_cpu(target->srp_host->dev->dev->dma_device, iu->dma,
- target->max_ti_iu_len, DMA_FROM_DEVICE);
+ dev = target->srp_host->dev->dev;
+ ib_dma_sync_single_for_cpu(dev, iu->dma, target->max_ti_iu_len,
+ DMA_FROM_DEVICE);
opcode = *(u8 *) iu->buf;
@@ -850,8 +863,8 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
break;
}
- dma_sync_single_for_device(target->srp_host->dev->dev->dma_device, iu->dma,
- target->max_ti_iu_len, DMA_FROM_DEVICE);
+ ib_dma_sync_single_for_device(dev, iu->dma, target->max_ti_iu_len,
+ DMA_FROM_DEVICE);
}
static void srp_completion(struct ib_cq *cq, void *target_ptr)
@@ -969,6 +982,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
struct srp_request *req;
struct srp_iu *iu;
struct srp_cmd *cmd;
+ struct ib_device *dev;
int len;
if (target->state == SRP_TARGET_CONNECTING)
@@ -985,8 +999,9 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
if (!iu)
goto err;
- dma_sync_single_for_cpu(target->srp_host->dev->dev->dma_device, iu->dma,
- srp_max_iu_len, DMA_TO_DEVICE);
+ dev = target->srp_host->dev->dev;
+ ib_dma_sync_single_for_cpu(dev, iu->dma, srp_max_iu_len,
+ DMA_TO_DEVICE);
req = list_entry(target->free_reqs.next, struct srp_request, list);
@@ -1018,8 +1033,8 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
goto err_unmap;
}
- dma_sync_single_for_device(target->srp_host->dev->dev->dma_device, iu->dma,
- srp_max_iu_len, DMA_TO_DEVICE);
+ ib_dma_sync_single_for_device(dev, iu->dma, srp_max_iu_len,
+ DMA_TO_DEVICE);
if (__srp_post_send(target, iu, len)) {
printk(KERN_ERR PFX "Send failed\n");
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index d4e35ef..868a540 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -161,7 +161,7 @@ struct srp_target_port {
};
struct srp_iu {
- dma_addr_t dma;
+ u64 dma;
void *buf;
size_t size;
enum dma_data_direction direction;
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
index 54bc569..35461ea 100644
--- a/drivers/input/keyboard/hilkbd.c
+++ b/drivers/input/keyboard/hilkbd.c
@@ -23,7 +23,12 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/hil.h>
+#include <linux/io.h>
#include <linux/spinlock.h>
+#include <asm/irq.h>
+#ifdef CONFIG_HP300
+#include <asm/hwtest.h>
+#endif
MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 29ca0ab..6b46c9b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -146,7 +146,7 @@ config TOUCHSCREEN_TOUCHWIN
config TOUCHSCREEN_UCB1400
tristate "Philips UCB1400 touchscreen"
- select SND_AC97_BUS
+ select AC97_BUS
help
This enables support for the Philips UCB1400 touchscreen interface.
The UCB1400 is an AC97 audio codec. The touchscreen interface
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c
index 3cac237..09ea50d 100644
--- a/drivers/isdn/act2000/act2000_isa.c
+++ b/drivers/isdn/act2000/act2000_isa.c
@@ -408,7 +408,7 @@ act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
p = cblock.buffer;
if (!access_ok(VERIFY_READ, p, length))
return -EFAULT;
- buf = (u_char *) kmalloc(1024, GFP_KERNEL);
+ buf = kmalloc(1024, GFP_KERNEL);
if (!buf)
return -ENOMEM;
timeout = 0;
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
index 097bfa7..c4d438c 100644
--- a/drivers/isdn/capi/capidrv.c
+++ b/drivers/isdn/capi/capidrv.c
@@ -2013,7 +2013,7 @@ static int capidrv_addcontr(u16 contr, struct capi_profile *profp)
strcpy(card->name, id);
card->contrnr = contr;
card->nbchan = profp->nbchannel;
- card->bchans = (capidrv_bchan *) kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
+ card->bchans = kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
if (!card->bchans) {
printk(KERN_WARNING
"capidrv: (%s) Could not allocate bchan-structs.\n", id);
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c
index 399b316..06967da 100644
--- a/drivers/isdn/divert/divert_procfs.c
+++ b/drivers/isdn/divert/divert_procfs.c
@@ -45,7 +45,7 @@ put_info_buffer(char *cp)
return;
if (!*cp)
return;
- if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
+ if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
return; /* no memory */
strcpy(ib->info_start, cp); /* set output string */
ib->next = NULL;
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
index 03319ea..7d97d54 100644
--- a/drivers/isdn/divert/isdn_divert.c
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -153,7 +153,7 @@ int cf_command(int drvid, int mode,
*ielenp = p - ielenp - 1; /* set total IE length */
/* allocate mem for information struct */
- if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
+ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
return(-ENOMEM); /* no memory */
init_timer(&cs->timer);
cs->info[0] = '\0';
@@ -276,7 +276,7 @@ int insertrule(int idx, divert_rule *newrule)
{ struct deflect_struc *ds,*ds1=NULL;
unsigned long flags;
- if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc),
+ if (!(ds = kmalloc(sizeof(struct deflect_struc),
GFP_KERNEL)))
return(-ENOMEM); /* no memory */
@@ -451,7 +451,7 @@ static int isdn_divert_icall(isdn_ctrl *ic)
if (dv->rule.action == DEFLECT_PROCEED)
if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
return(0); /* no external deflection needed */
- if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
+ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
return(0); /* no memory */
init_timer(&cs->timer);
cs->info[0] = '\0';
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 63b629b..b5e7f9c 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -1853,20 +1853,24 @@ static int gigaset_write_cmd(struct cardstate *cs,
{
struct cmdbuf_t *cb;
unsigned long flags;
- int status;
+ int rc;
gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf);
- if (len <= 0)
- return 0; /* nothing to do */
+ if (len <= 0) {
+ /* nothing to do */
+ rc = 0;
+ goto notqueued;
+ }
if (len > IF_WRITEBUF)
len = IF_WRITEBUF;
if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto notqueued;
}
memcpy(cb->buf, buf, len);
@@ -1891,11 +1895,21 @@ static int gigaset_write_cmd(struct cardstate *cs,
if (unlikely(!cs->connected)) {
spin_unlock_irqrestore(&cs->lock, flags);
gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+ /* flush command queue */
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ while (cs->cmdbuf != NULL)
+ complete_cb(cs);
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
return -ENODEV;
}
- status = start_cbsend(cs);
+ rc = start_cbsend(cs);
spin_unlock_irqrestore(&cs->lock, flags);
- return status < 0 ? status : len;
+ return rc < 0 ? rc : len;
+
+notqueued: /* request handled without queuing */
+ if (wake_tasklet)
+ tasklet_schedule(wake_tasklet);
+ return rc;
}
/* gigaset_write_room
@@ -1964,20 +1978,15 @@ static int gigaset_freebcshw(struct bc_state *bcs)
/* kill URBs and tasklets before freeing - better safe than sorry */
atomic_set(&ubc->running, 0);
- for (i = 0; i < BAS_OUTURBS; ++i)
- if (ubc->isoouturbs[i].urb) {
- gig_dbg(DEBUG_INIT, "%s: killing iso out URB %d",
- __func__, i);
- usb_kill_urb(ubc->isoouturbs[i].urb);
- usb_free_urb(ubc->isoouturbs[i].urb);
- }
- for (i = 0; i < BAS_INURBS; ++i)
- if (ubc->isoinurbs[i]) {
- gig_dbg(DEBUG_INIT, "%s: killing iso in URB %d",
- __func__, i);
- usb_kill_urb(ubc->isoinurbs[i]);
- usb_free_urb(ubc->isoinurbs[i]);
- }
+ gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__);
+ for (i = 0; i < BAS_OUTURBS; ++i) {
+ usb_kill_urb(ubc->isoouturbs[i].urb);
+ usb_free_urb(ubc->isoouturbs[i].urb);
+ }
+ for (i = 0; i < BAS_INURBS; ++i) {
+ usb_kill_urb(ubc->isoinurbs[i]);
+ usb_free_urb(ubc->isoinurbs[i]);
+ }
tasklet_kill(&ubc->sent_tasklet);
tasklet_kill(&ubc->rcvd_tasklet);
kfree(ubc->isooutbuf);
@@ -2099,55 +2108,32 @@ static void freeurbs(struct cardstate *cs)
struct bas_bc_state *ubc;
int i, j;
+ gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
for (j = 0; j < 2; ++j) {
ubc = cs->bcs[j].hw.bas;
- for (i = 0; i < BAS_OUTURBS; ++i)
- if (ubc->isoouturbs[i].urb) {
- usb_kill_urb(ubc->isoouturbs[i].urb);
- gig_dbg(DEBUG_INIT,
- "%s: isoc output URB %d/%d unlinked",
- __func__, j, i);
- usb_free_urb(ubc->isoouturbs[i].urb);
- ubc->isoouturbs[i].urb = NULL;
- }
- for (i = 0; i < BAS_INURBS; ++i)
- if (ubc->isoinurbs[i]) {
- usb_kill_urb(ubc->isoinurbs[i]);
- gig_dbg(DEBUG_INIT,
- "%s: isoc input URB %d/%d unlinked",
- __func__, j, i);
- usb_free_urb(ubc->isoinurbs[i]);
- ubc->isoinurbs[i] = NULL;
- }
- }
- if (ucs->urb_int_in) {
- usb_kill_urb(ucs->urb_int_in);
- gig_dbg(DEBUG_INIT, "%s: interrupt input URB unlinked",
- __func__);
- usb_free_urb(ucs->urb_int_in);
- ucs->urb_int_in = NULL;
- }
- if (ucs->urb_cmd_out) {
- usb_kill_urb(ucs->urb_cmd_out);
- gig_dbg(DEBUG_INIT, "%s: command output URB unlinked",
- __func__);
- usb_free_urb(ucs->urb_cmd_out);
- ucs->urb_cmd_out = NULL;
- }
- if (ucs->urb_cmd_in) {
- usb_kill_urb(ucs->urb_cmd_in);
- gig_dbg(DEBUG_INIT, "%s: command input URB unlinked",
- __func__);
- usb_free_urb(ucs->urb_cmd_in);
- ucs->urb_cmd_in = NULL;
- }
- if (ucs->urb_ctrl) {
- usb_kill_urb(ucs->urb_ctrl);
- gig_dbg(DEBUG_INIT, "%s: control output URB unlinked",
- __func__);
- usb_free_urb(ucs->urb_ctrl);
- ucs->urb_ctrl = NULL;
+ for (i = 0; i < BAS_OUTURBS; ++i) {
+ usb_kill_urb(ubc->isoouturbs[i].urb);
+ usb_free_urb(ubc->isoouturbs[i].urb);
+ ubc->isoouturbs[i].urb = NULL;
+ }
+ for (i = 0; i < BAS_INURBS; ++i) {
+ usb_kill_urb(ubc->isoinurbs[i]);
+ usb_free_urb(ubc->isoinurbs[i]);
+ ubc->isoinurbs[i] = NULL;
+ }
}
+ usb_kill_urb(ucs->urb_int_in);
+ usb_free_urb(ucs->urb_int_in);
+ ucs->urb_int_in = NULL;
+ usb_kill_urb(ucs->urb_cmd_out);
+ usb_free_urb(ucs->urb_cmd_out);
+ ucs->urb_cmd_out = NULL;
+ usb_kill_urb(ucs->urb_cmd_in);
+ usb_free_urb(ucs->urb_cmd_in);
+ ucs->urb_cmd_in = NULL;
+ usb_kill_urb(ucs->urb_ctrl);
+ usb_free_urb(ucs->urb_ctrl);
+ ucs->urb_ctrl = NULL;
}
/* gigaset_probe
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
index 6fa12cc..34ab5f7 100644
--- a/drivers/isdn/hisax/Kconfig
+++ b/drivers/isdn/hisax/Kconfig
@@ -110,7 +110,7 @@ config HISAX_16_3
config HISAX_TELESPCI
bool "Teles PCI"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Teles PCI.
See <file:Documentation/isdn/README.HiSax> on how to configure it.
@@ -238,7 +238,7 @@ config HISAX_MIC
config HISAX_NETJET
bool "NETjet card"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the NetJet from Traverse
Technologies.
@@ -249,7 +249,7 @@ config HISAX_NETJET
config HISAX_NETJET_U
bool "NETspider U card"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Netspider U interface ISDN card
from Traverse Technologies.
@@ -317,7 +317,7 @@ config HISAX_GAZEL
config HISAX_HFC_PCI
bool "HFC PCI-Bus cards"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
@@ -344,7 +344,7 @@ config HISAX_HFC_SX
config HISAX_ENTERNOW_PCI
bool "Formula-n enter:now PCI card"
- depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || FRV))
+ depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Formula-n enter:now PCI
ISDN card.
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
index 8e2b038..94a93508 100644
--- a/drivers/isdn/hysdn/hysdn_procconf.c
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -275,7 +275,7 @@ hysdn_conf_open(struct inode *ino, struct file *filep)
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
/* read access -> output card info data */
- if (!(tmp = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+ if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
unlock_kernel();
return (-EFAULT); /* out of memory */
}
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
index f241f5e..375d956 100644
--- a/drivers/isdn/hysdn/hysdn_proclog.c
+++ b/drivers/isdn/hysdn/hysdn_proclog.c
@@ -111,7 +111,7 @@ put_log_buffer(hysdn_card * card, char *cp)
if (pd->if_used <= 0)
return; /* no open file for read */
- if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+ if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
return; /* no memory */
strcpy(ib->log_start, cp); /* set output string */
ib->next = NULL;
diff --git a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c
index 2cc56d6..fb350c5 100644
--- a/drivers/isdn/i4l/isdn_audio.c
+++ b/drivers/isdn/i4l/isdn_audio.c
@@ -328,7 +328,7 @@ adpcm_state *
isdn_audio_adpcm_init(adpcm_state * s, int nbits)
{
if (!s)
- s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+ s = kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
if (s) {
s->a = 0;
s->d = 5;
@@ -343,7 +343,7 @@ dtmf_state *
isdn_audio_dtmf_init(dtmf_state * s)
{
if (!s)
- s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+ s = kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
if (s) {
s->idx = 0;
s->last = ' ';
@@ -621,7 +621,7 @@ silence_state *
isdn_audio_silence_init(silence_state * s)
{
if (!s)
- s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+ s = kmalloc(sizeof(silence_state), GFP_ATOMIC);
if (s) {
s->idx = 0;
s->state = 0;
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index c36c817..838b373 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -2948,7 +2948,7 @@ isdn_net_addphone(isdn_net_ioctl_phone * phone)
isdn_net_phone *n;
if (p) {
- if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+ if (!(n = kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
return -ENOMEM;
strcpy(n->num, phone->phone);
n->next = p->local->phone[phone->outgoing & 1];
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 4381179..1726131b 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -717,7 +717,7 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
printk(KERN_DEBUG "ippp: device not activated.\n");
return 0;
}
- nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
+ nbuf = kmalloc(len + 4, GFP_ATOMIC);
if (!nbuf) {
printk(KERN_WARNING "ippp: Can't alloc buf\n");
return 0;
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
index 6ff8557..eafcce5 100644
--- a/drivers/isdn/pcbit/layer2.c
+++ b/drivers/isdn/pcbit/layer2.c
@@ -100,7 +100,7 @@ pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
dev_kfree_skb(skb);
return -1;
}
- if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+ if ((frame = kmalloc(sizeof(struct frame_buf),
GFP_ATOMIC)) == NULL) {
printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
dev_kfree_skb(skb);
diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig
new file mode 100644
index 0000000..703cc88
--- /dev/null
+++ b/drivers/kvm/Kconfig
@@ -0,0 +1,37 @@
+#
+# KVM configuration
+#
+menu "Virtualization"
+
+config KVM
+ tristate "Kernel-based Virtual Machine (KVM) support"
+ depends on X86 && EXPERIMENTAL
+ ---help---
+ Support hosting fully virtualized guest machines using hardware
+ virtualization extensions. You will need a fairly recent
+ processor equipped with virtualization extensions. You will also
+ need to select one or more of the processor modules below.
+
+ This module provides access to the hardware capabilities through
+ a character device node named /dev/kvm.
+
+ To compile this as a module, choose M here: the module
+ will be called kvm.
+
+ If unsure, say N.
+
+config KVM_INTEL
+ tristate "KVM for Intel processors support"
+ depends on KVM
+ ---help---
+ Provides support for KVM on Intel processors equipped with the VT
+ extensions.
+
+config KVM_AMD
+ tristate "KVM for AMD processors support"
+ depends on KVM
+ ---help---
+ Provides support for KVM on AMD processors equipped with the AMD-V
+ (SVM) extensions.
+
+endmenu
diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile
new file mode 100644
index 0000000..c0a789f
--- /dev/null
+++ b/drivers/kvm/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for Kernel-based Virtual Machine module
+#
+
+kvm-objs := kvm_main.o mmu.o x86_emulate.o
+obj-$(CONFIG_KVM) += kvm.o
+kvm-intel-objs = vmx.o
+obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
+kvm-amd-objs = svm.o
+obj-$(CONFIG_KVM_AMD) += kvm-amd.o
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
new file mode 100644
index 0000000..930e04c
--- /dev/null
+++ b/drivers/kvm/kvm.h
@@ -0,0 +1,551 @@
+#ifndef __KVM_H
+#define __KVM_H
+
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include "vmx.h"
+#include <linux/kvm.h>
+
+#define CR0_PE_MASK (1ULL << 0)
+#define CR0_TS_MASK (1ULL << 3)
+#define CR0_NE_MASK (1ULL << 5)
+#define CR0_WP_MASK (1ULL << 16)
+#define CR0_NW_MASK (1ULL << 29)
+#define CR0_CD_MASK (1ULL << 30)
+#define CR0_PG_MASK (1ULL << 31)
+
+#define CR3_WPT_MASK (1ULL << 3)
+#define CR3_PCD_MASK (1ULL << 4)
+
+#define CR3_RESEVED_BITS 0x07ULL
+#define CR3_L_MODE_RESEVED_BITS (~((1ULL << 40) - 1) | 0x0fe7ULL)
+#define CR3_FLAGS_MASK ((1ULL << 5) - 1)
+
+#define CR4_VME_MASK (1ULL << 0)
+#define CR4_PSE_MASK (1ULL << 4)
+#define CR4_PAE_MASK (1ULL << 5)
+#define CR4_PGE_MASK (1ULL << 7)
+#define CR4_VMXE_MASK (1ULL << 13)
+
+#define KVM_GUEST_CR0_MASK \
+ (CR0_PG_MASK | CR0_PE_MASK | CR0_WP_MASK | CR0_NE_MASK \
+ | CR0_NW_MASK | CR0_CD_MASK)
+#define KVM_VM_CR0_ALWAYS_ON \
+ (CR0_PG_MASK | CR0_PE_MASK | CR0_WP_MASK | CR0_NE_MASK)
+#define KVM_GUEST_CR4_MASK \
+ (CR4_PSE_MASK | CR4_PAE_MASK | CR4_PGE_MASK | CR4_VMXE_MASK | CR4_VME_MASK)
+#define KVM_PMODE_VM_CR4_ALWAYS_ON (CR4_VMXE_MASK | CR4_PAE_MASK)
+#define KVM_RMODE_VM_CR4_ALWAYS_ON (CR4_VMXE_MASK | CR4_PAE_MASK | CR4_VME_MASK)
+
+#define INVALID_PAGE (~(hpa_t)0)
+#define UNMAPPED_GVA (~(gpa_t)0)
+
+#define KVM_MAX_VCPUS 1
+#define KVM_MEMORY_SLOTS 4
+#define KVM_NUM_MMU_PAGES 256
+
+#define FX_IMAGE_SIZE 512
+#define FX_IMAGE_ALIGN 16
+#define FX_BUF_SIZE (2 * FX_IMAGE_SIZE + FX_IMAGE_ALIGN)
+
+#define DE_VECTOR 0
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+
+#define SELECTOR_TI_MASK (1 << 2)
+#define SELECTOR_RPL_MASK 0x03
+
+#define IOPL_SHIFT 12
+
+/*
+ * Address types:
+ *
+ * gva - guest virtual address
+ * gpa - guest physical address
+ * gfn - guest frame number
+ * hva - host virtual address
+ * hpa - host physical address
+ * hfn - host frame number
+ */
+
+typedef unsigned long gva_t;
+typedef u64 gpa_t;
+typedef unsigned long gfn_t;
+
+typedef unsigned long hva_t;
+typedef u64 hpa_t;
+typedef unsigned long hfn_t;
+
+struct kvm_mmu_page {
+ struct list_head link;
+ hpa_t page_hpa;
+ unsigned long slot_bitmap; /* One bit set per slot which has memory
+ * in this shadow page.
+ */
+ int global; /* Set if all ptes in this page are global */
+ u64 *parent_pte;
+};
+
+struct vmcs {
+ u32 revision_id;
+ u32 abort;
+ char data[0];
+};
+
+#define vmx_msr_entry kvm_msr_entry
+
+struct kvm_vcpu;
+
+/*
+ * x86 supports 3 paging modes (4-level 64-bit, 3-level 64-bit, and 2-level
+ * 32-bit). The kvm_mmu structure abstracts the details of the current mmu
+ * mode.
+ */
+struct kvm_mmu {
+ void (*new_cr3)(struct kvm_vcpu *vcpu);
+ int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err);
+ void (*inval_page)(struct kvm_vcpu *vcpu, gva_t gva);
+ void (*free)(struct kvm_vcpu *vcpu);
+ gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva);
+ hpa_t root_hpa;
+ int root_level;
+ int shadow_root_level;
+};
+
+struct kvm_guest_debug {
+ int enabled;
+ unsigned long bp[4];
+ int singlestep;
+};
+
+enum {
+ VCPU_REGS_RAX = 0,
+ VCPU_REGS_RCX = 1,
+ VCPU_REGS_RDX = 2,
+ VCPU_REGS_RBX = 3,
+ VCPU_REGS_RSP = 4,
+ VCPU_REGS_RBP = 5,
+ VCPU_REGS_RSI = 6,
+ VCPU_REGS_RDI = 7,
+#ifdef CONFIG_X86_64
+ VCPU_REGS_R8 = 8,
+ VCPU_REGS_R9 = 9,
+ VCPU_REGS_R10 = 10,
+ VCPU_REGS_R11 = 11,
+ VCPU_REGS_R12 = 12,
+ VCPU_REGS_R13 = 13,
+ VCPU_REGS_R14 = 14,
+ VCPU_REGS_R15 = 15,
+#endif
+ NR_VCPU_REGS
+};
+
+enum {
+ VCPU_SREG_CS,
+ VCPU_SREG_DS,
+ VCPU_SREG_ES,
+ VCPU_SREG_FS,
+ VCPU_SREG_GS,
+ VCPU_SREG_SS,
+ VCPU_SREG_TR,
+ VCPU_SREG_LDTR,
+};
+
+struct kvm_vcpu {
+ struct kvm *kvm;
+ union {
+ struct vmcs *vmcs;
+ struct vcpu_svm *svm;
+ };
+ struct mutex mutex;
+ int cpu;
+ int launched;
+ unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */
+#define NR_IRQ_WORDS KVM_IRQ_BITMAP_SIZE(unsigned long)
+ unsigned long irq_pending[NR_IRQ_WORDS];
+ unsigned long regs[NR_VCPU_REGS]; /* for rsp: vcpu_load_rsp_rip() */
+ unsigned long rip; /* needs vcpu_load_rsp_rip() */
+
+ unsigned long cr0;
+ unsigned long cr2;
+ unsigned long cr3;
+ unsigned long cr4;
+ unsigned long cr8;
+ u64 shadow_efer;
+ u64 apic_base;
+ int nmsrs;
+ struct vmx_msr_entry *guest_msrs;
+ struct vmx_msr_entry *host_msrs;
+
+ struct list_head free_pages;
+ struct kvm_mmu_page page_header_buf[KVM_NUM_MMU_PAGES];
+ struct kvm_mmu mmu;
+
+ struct kvm_guest_debug guest_debug;
+
+ char fx_buf[FX_BUF_SIZE];
+ char *host_fx_image;
+ char *guest_fx_image;
+
+ int mmio_needed;
+ int mmio_read_completed;
+ int mmio_is_write;
+ int mmio_size;
+ unsigned char mmio_data[8];
+ gpa_t mmio_phys_addr;
+
+ struct {
+ int active;
+ u8 save_iopl;
+ struct kvm_save_segment {
+ u16 selector;
+ unsigned long base;
+ u32 limit;
+ u32 ar;
+ } tr, es, ds, fs, gs;
+ } rmode;
+};
+
+struct kvm_memory_slot {
+ gfn_t base_gfn;
+ unsigned long npages;
+ unsigned long flags;
+ struct page **phys_mem;
+ unsigned long *dirty_bitmap;
+};
+
+struct kvm {
+ spinlock_t lock; /* protects everything except vcpus */
+ int nmemslots;
+ struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS];
+ struct list_head active_mmu_pages;
+ struct kvm_vcpu vcpus[KVM_MAX_VCPUS];
+ int memory_config_version;
+ int busy;
+};
+
+struct kvm_stat {
+ u32 pf_fixed;
+ u32 pf_guest;
+ u32 tlb_flush;
+ u32 invlpg;
+
+ u32 exits;
+ u32 io_exits;
+ u32 mmio_exits;
+ u32 signal_exits;
+ u32 irq_exits;
+};
+
+struct descriptor_table {
+ u16 limit;
+ unsigned long base;
+} __attribute__((packed));
+
+struct kvm_arch_ops {
+ int (*cpu_has_kvm_support)(void); /* __init */
+ int (*disabled_by_bios)(void); /* __init */
+ void (*hardware_enable)(void *dummy); /* __init */
+ void (*hardware_disable)(void *dummy);
+ int (*hardware_setup)(void); /* __init */
+ void (*hardware_unsetup)(void); /* __exit */
+
+ int (*vcpu_create)(struct kvm_vcpu *vcpu);
+ void (*vcpu_free)(struct kvm_vcpu *vcpu);
+
+ struct kvm_vcpu *(*vcpu_load)(struct kvm_vcpu *vcpu);
+ void (*vcpu_put)(struct kvm_vcpu *vcpu);
+
+ int (*set_guest_debug)(struct kvm_vcpu *vcpu,
+ struct kvm_debug_guest *dbg);
+ int (*get_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata);
+ int (*set_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 data);
+ u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg);
+ void (*get_segment)(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg);
+ void (*set_segment)(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg);
+ int (*is_long_mode)(struct kvm_vcpu *vcpu);
+ void (*get_cs_db_l_bits)(struct kvm_vcpu *vcpu, int *db, int *l);
+ void (*set_cr0)(struct kvm_vcpu *vcpu, unsigned long cr0);
+ void (*set_cr0_no_modeswitch)(struct kvm_vcpu *vcpu,
+ unsigned long cr0);
+ void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3);
+ void (*set_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4);
+ void (*set_efer)(struct kvm_vcpu *vcpu, u64 efer);
+ void (*get_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
+ void (*set_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
+ void (*get_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
+ void (*set_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
+ unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr);
+ void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value,
+ int *exception);
+ void (*cache_regs)(struct kvm_vcpu *vcpu);
+ void (*decache_regs)(struct kvm_vcpu *vcpu);
+ unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
+ void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags);
+
+ void (*invlpg)(struct kvm_vcpu *vcpu, gva_t addr);
+ void (*tlb_flush)(struct kvm_vcpu *vcpu);
+ void (*inject_page_fault)(struct kvm_vcpu *vcpu,
+ unsigned long addr, u32 err_code);
+
+ void (*inject_gp)(struct kvm_vcpu *vcpu, unsigned err_code);
+
+ int (*run)(struct kvm_vcpu *vcpu, struct kvm_run *run);
+ int (*vcpu_setup)(struct kvm_vcpu *vcpu);
+ void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu);
+};
+
+extern struct kvm_stat kvm_stat;
+extern struct kvm_arch_ops *kvm_arch_ops;
+
+#define kvm_printf(kvm, fmt ...) printk(KERN_DEBUG fmt)
+#define vcpu_printf(vcpu, fmt...) kvm_printf(vcpu->kvm, fmt)
+
+int kvm_init_arch(struct kvm_arch_ops *ops, struct module *module);
+void kvm_exit_arch(void);
+
+void kvm_mmu_destroy(struct kvm_vcpu *vcpu);
+int kvm_mmu_init(struct kvm_vcpu *vcpu);
+
+int kvm_mmu_reset_context(struct kvm_vcpu *vcpu);
+void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot);
+
+hpa_t gpa_to_hpa(struct kvm_vcpu *vcpu, gpa_t gpa);
+#define HPA_MSB ((sizeof(hpa_t) * 8) - 1)
+#define HPA_ERR_MASK ((hpa_t)1 << HPA_MSB)
+static inline int is_error_hpa(hpa_t hpa) { return hpa >> HPA_MSB; }
+hpa_t gva_to_hpa(struct kvm_vcpu *vcpu, gva_t gva);
+
+void kvm_emulator_want_group7_invlpg(void);
+
+extern hpa_t bad_page_address;
+
+static inline struct page *gfn_to_page(struct kvm_memory_slot *slot, gfn_t gfn)
+{
+ return slot->phys_mem[gfn - slot->base_gfn];
+}
+
+struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
+void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
+
+enum emulation_result {
+ EMULATE_DONE, /* no further processing */
+ EMULATE_DO_MMIO, /* kvm_run filled with mmio request */
+ EMULATE_FAIL, /* can't emulate this instruction */
+};
+
+int emulate_instruction(struct kvm_vcpu *vcpu, struct kvm_run *run,
+ unsigned long cr2, u16 error_code);
+void realmode_lgdt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
+void realmode_lidt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
+void realmode_lmsw(struct kvm_vcpu *vcpu, unsigned long msw,
+ unsigned long *rflags);
+
+unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr);
+void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long value,
+ unsigned long *rflags);
+
+struct x86_emulate_ctxt;
+
+int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address);
+int emulate_clts(struct kvm_vcpu *vcpu);
+int emulator_get_dr(struct x86_emulate_ctxt* ctxt, int dr,
+ unsigned long *dest);
+int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr,
+ unsigned long value);
+
+void set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
+void set_cr3(struct kvm_vcpu *vcpu, unsigned long cr0);
+void set_cr4(struct kvm_vcpu *vcpu, unsigned long cr0);
+void set_cr8(struct kvm_vcpu *vcpu, unsigned long cr0);
+void lmsw(struct kvm_vcpu *vcpu, unsigned long msw);
+
+#ifdef CONFIG_X86_64
+void set_efer(struct kvm_vcpu *vcpu, u64 efer);
+#endif
+
+void fx_init(struct kvm_vcpu *vcpu);
+
+void load_msrs(struct vmx_msr_entry *e, int n);
+void save_msrs(struct vmx_msr_entry *e, int n);
+void kvm_resched(struct kvm_vcpu *vcpu);
+
+int kvm_read_guest(struct kvm_vcpu *vcpu,
+ gva_t addr,
+ unsigned long size,
+ void *dest);
+
+int kvm_write_guest(struct kvm_vcpu *vcpu,
+ gva_t addr,
+ unsigned long size,
+ void *data);
+
+unsigned long segment_base(u16 selector);
+
+static inline struct page *_gfn_to_page(struct kvm *kvm, gfn_t gfn)
+{
+ struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
+ return (slot) ? slot->phys_mem[gfn - slot->base_gfn] : NULL;
+}
+
+static inline int is_pae(struct kvm_vcpu *vcpu)
+{
+ return vcpu->cr4 & CR4_PAE_MASK;
+}
+
+static inline int is_pse(struct kvm_vcpu *vcpu)
+{
+ return vcpu->cr4 & CR4_PSE_MASK;
+}
+
+static inline int is_paging(struct kvm_vcpu *vcpu)
+{
+ return vcpu->cr0 & CR0_PG_MASK;
+}
+
+static inline int memslot_id(struct kvm *kvm, struct kvm_memory_slot *slot)
+{
+ return slot - kvm->memslots;
+}
+
+static inline struct kvm_mmu_page *page_header(hpa_t shadow_page)
+{
+ struct page *page = pfn_to_page(shadow_page >> PAGE_SHIFT);
+
+ return (struct kvm_mmu_page *)page->private;
+}
+
+static inline u16 read_fs(void)
+{
+ u16 seg;
+ asm ("mov %%fs, %0" : "=g"(seg));
+ return seg;
+}
+
+static inline u16 read_gs(void)
+{
+ u16 seg;
+ asm ("mov %%gs, %0" : "=g"(seg));
+ return seg;
+}
+
+static inline u16 read_ldt(void)
+{
+ u16 ldt;
+ asm ("sldt %0" : "=g"(ldt));
+ return ldt;
+}
+
+static inline void load_fs(u16 sel)
+{
+ asm ("mov %0, %%fs" : : "rm"(sel));
+}
+
+static inline void load_gs(u16 sel)
+{
+ asm ("mov %0, %%gs" : : "rm"(sel));
+}
+
+#ifndef load_ldt
+static inline void load_ldt(u16 sel)
+{
+ asm ("lldt %0" : : "g"(sel));
+}
+#endif
+
+static inline void get_idt(struct descriptor_table *table)
+{
+ asm ("sidt %0" : "=m"(*table));
+}
+
+static inline void get_gdt(struct descriptor_table *table)
+{
+ asm ("sgdt %0" : "=m"(*table));
+}
+
+static inline unsigned long read_tr_base(void)
+{
+ u16 tr;
+ asm ("str %0" : "=g"(tr));
+ return segment_base(tr);
+}
+
+#ifdef CONFIG_X86_64
+static inline unsigned long read_msr(unsigned long msr)
+{
+ u64 value;
+
+ rdmsrl(msr, value);
+ return value;
+}
+#endif
+
+static inline void fx_save(void *image)
+{
+ asm ("fxsave (%0)":: "r" (image));
+}
+
+static inline void fx_restore(void *image)
+{
+ asm ("fxrstor (%0)":: "r" (image));
+}
+
+static inline void fpu_init(void)
+{
+ asm ("finit");
+}
+
+static inline u32 get_rdx_init_val(void)
+{
+ return 0x600; /* P6 family */
+}
+
+#define ASM_VMX_VMCLEAR_RAX ".byte 0x66, 0x0f, 0xc7, 0x30"
+#define ASM_VMX_VMLAUNCH ".byte 0x0f, 0x01, 0xc2"
+#define ASM_VMX_VMRESUME ".byte 0x0f, 0x01, 0xc3"
+#define ASM_VMX_VMPTRLD_RAX ".byte 0x0f, 0xc7, 0x30"
+#define ASM_VMX_VMREAD_RDX_RAX ".byte 0x0f, 0x78, 0xd0"
+#define ASM_VMX_VMWRITE_RAX_RDX ".byte 0x0f, 0x79, 0xd0"
+#define ASM_VMX_VMWRITE_RSP_RDX ".byte 0x0f, 0x79, 0xd4"
+#define ASM_VMX_VMXOFF ".byte 0x0f, 0x01, 0xc4"
+#define ASM_VMX_VMXON_RAX ".byte 0xf3, 0x0f, 0xc7, 0x30"
+
+#define MSR_IA32_TIME_STAMP_COUNTER 0x010
+
+#define TSS_IOPB_BASE_OFFSET 0x66
+#define TSS_BASE_SIZE 0x68
+#define TSS_IOPB_SIZE (65536 / 8)
+#define TSS_REDIRECTION_SIZE (256 / 8)
+#define RMODE_TSS_SIZE (TSS_BASE_SIZE + TSS_REDIRECTION_SIZE + TSS_IOPB_SIZE + 1)
+
+#ifdef CONFIG_X86_64
+
+/*
+ * When emulating 32-bit mode, cr3 is only 32 bits even on x86_64. Therefore
+ * we need to allocate shadow page tables in the first 4GB of memory, which
+ * happens to fit the DMA32 zone.
+ */
+#define GFP_KVM_MMU (GFP_KERNEL | __GFP_DMA32)
+
+#else
+
+#define GFP_KVM_MMU GFP_KERNEL
+
+#endif
+
+#endif
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
new file mode 100644
index 0000000..fd1bb87
--- /dev/null
+++ b/drivers/kvm/kvm_main.c
@@ -0,0 +1,1917 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables machines with Intel VT-x extensions to run virtual
+ * machines without emulation or binary translation.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "kvm.h"
+
+#include <linux/kvm.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/processor.h>
+#include <linux/percpu.h>
+#include <linux/gfp.h>
+#include <asm/msr.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <linux/reboot.h>
+#include <asm/io.h>
+#include <linux/debugfs.h>
+#include <linux/highmem.h>
+#include <linux/file.h>
+#include <asm/desc.h>
+
+#include "x86_emulate.h"
+#include "segment_descriptor.h"
+
+MODULE_AUTHOR("Qumranet");
+MODULE_LICENSE("GPL");
+
+struct kvm_arch_ops *kvm_arch_ops;
+struct kvm_stat kvm_stat;
+EXPORT_SYMBOL_GPL(kvm_stat);
+
+static struct kvm_stats_debugfs_item {
+ const char *name;
+ u32 *data;
+ struct dentry *dentry;
+} debugfs_entries[] = {
+ { "pf_fixed", &kvm_stat.pf_fixed },
+ { "pf_guest", &kvm_stat.pf_guest },
+ { "tlb_flush", &kvm_stat.tlb_flush },
+ { "invlpg", &kvm_stat.invlpg },
+ { "exits", &kvm_stat.exits },
+ { "io_exits", &kvm_stat.io_exits },
+ { "mmio_exits", &kvm_stat.mmio_exits },
+ { "signal_exits", &kvm_stat.signal_exits },
+ { "irq_exits", &kvm_stat.irq_exits },
+ { 0, 0 }
+};
+
+static struct dentry *debugfs_dir;
+
+#define MAX_IO_MSRS 256
+
+#define CR0_RESEVED_BITS 0xffffffff1ffaffc0ULL
+#define LMSW_GUEST_MASK 0x0eULL
+#define CR4_RESEVED_BITS (~((1ULL << 11) - 1))
+#define CR8_RESEVED_BITS (~0x0fULL)
+#define EFER_RESERVED_BITS 0xfffffffffffff2fe
+
+#ifdef CONFIG_X86_64
+// LDT or TSS descriptor in the GDT. 16 bytes.
+struct segment_descriptor_64 {
+ struct segment_descriptor s;
+ u32 base_higher;
+ u32 pad_zero;
+};
+
+#endif
+
+unsigned long segment_base(u16 selector)
+{
+ struct descriptor_table gdt;
+ struct segment_descriptor *d;
+ unsigned long table_base;
+ typedef unsigned long ul;
+ unsigned long v;
+
+ if (selector == 0)
+ return 0;
+
+ asm ("sgdt %0" : "=m"(gdt));
+ table_base = gdt.base;
+
+ if (selector & 4) { /* from ldt */
+ u16 ldt_selector;
+
+ asm ("sldt %0" : "=g"(ldt_selector));
+ table_base = segment_base(ldt_selector);
+ }
+ d = (struct segment_descriptor *)(table_base + (selector & ~7));
+ v = d->base_low | ((ul)d->base_mid << 16) | ((ul)d->base_high << 24);
+#ifdef CONFIG_X86_64
+ if (d->system == 0
+ && (d->type == 2 || d->type == 9 || d->type == 11))
+ v |= ((ul)((struct segment_descriptor_64 *)d)->base_higher) << 32;
+#endif
+ return v;
+}
+EXPORT_SYMBOL_GPL(segment_base);
+
+int kvm_read_guest(struct kvm_vcpu *vcpu,
+ gva_t addr,
+ unsigned long size,
+ void *dest)
+{
+ unsigned char *host_buf = dest;
+ unsigned long req_size = size;
+
+ while (size) {
+ hpa_t paddr;
+ unsigned now;
+ unsigned offset;
+ hva_t guest_buf;
+
+ paddr = gva_to_hpa(vcpu, addr);
+
+ if (is_error_hpa(paddr))
+ break;
+
+ guest_buf = (hva_t)kmap_atomic(
+ pfn_to_page(paddr >> PAGE_SHIFT),
+ KM_USER0);
+ offset = addr & ~PAGE_MASK;
+ guest_buf |= offset;
+ now = min(size, PAGE_SIZE - offset);
+ memcpy(host_buf, (void*)guest_buf, now);
+ host_buf += now;
+ addr += now;
+ size -= now;
+ kunmap_atomic((void *)(guest_buf & PAGE_MASK), KM_USER0);
+ }
+ return req_size - size;
+}
+EXPORT_SYMBOL_GPL(kvm_read_guest);
+
+int kvm_write_guest(struct kvm_vcpu *vcpu,
+ gva_t addr,
+ unsigned long size,
+ void *data)
+{
+ unsigned char *host_buf = data;
+ unsigned long req_size = size;
+
+ while (size) {
+ hpa_t paddr;
+ unsigned now;
+ unsigned offset;
+ hva_t guest_buf;
+
+ paddr = gva_to_hpa(vcpu, addr);
+
+ if (is_error_hpa(paddr))
+ break;
+
+ guest_buf = (hva_t)kmap_atomic(
+ pfn_to_page(paddr >> PAGE_SHIFT), KM_USER0);
+ offset = addr & ~PAGE_MASK;
+ guest_buf |= offset;
+ now = min(size, PAGE_SIZE - offset);
+ memcpy((void*)guest_buf, host_buf, now);
+ host_buf += now;
+ addr += now;
+ size -= now;
+ kunmap_atomic((void *)(guest_buf & PAGE_MASK), KM_USER0);
+ }
+ return req_size - size;
+}
+EXPORT_SYMBOL_GPL(kvm_write_guest);
+
+static int vcpu_slot(struct kvm_vcpu *vcpu)
+{
+ return vcpu - vcpu->kvm->vcpus;
+}
+
+/*
+ * Switches to specified vcpu, until a matching vcpu_put()
+ */
+static struct kvm_vcpu *vcpu_load(struct kvm *kvm, int vcpu_slot)
+{
+ struct kvm_vcpu *vcpu = &kvm->vcpus[vcpu_slot];
+
+ mutex_lock(&vcpu->mutex);
+ if (unlikely(!vcpu->vmcs)) {
+ mutex_unlock(&vcpu->mutex);
+ return 0;
+ }
+ return kvm_arch_ops->vcpu_load(vcpu);
+}
+
+static void vcpu_put(struct kvm_vcpu *vcpu)
+{
+ kvm_arch_ops->vcpu_put(vcpu);
+ mutex_unlock(&vcpu->mutex);
+}
+
+static int kvm_dev_open(struct inode *inode, struct file *filp)
+{
+ struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
+ int i;
+
+ if (!kvm)
+ return -ENOMEM;
+
+ spin_lock_init(&kvm->lock);
+ INIT_LIST_HEAD(&kvm->active_mmu_pages);
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ struct kvm_vcpu *vcpu = &kvm->vcpus[i];
+
+ mutex_init(&vcpu->mutex);
+ vcpu->mmu.root_hpa = INVALID_PAGE;
+ INIT_LIST_HEAD(&vcpu->free_pages);
+ }
+ filp->private_data = kvm;
+ return 0;
+}
+
+/*
+ * Free any memory in @free but not in @dont.
+ */
+static void kvm_free_physmem_slot(struct kvm_memory_slot *free,
+ struct kvm_memory_slot *dont)
+{
+ int i;
+
+ if (!dont || free->phys_mem != dont->phys_mem)
+ if (free->phys_mem) {
+ for (i = 0; i < free->npages; ++i)
+ __free_page(free->phys_mem[i]);
+ vfree(free->phys_mem);
+ }
+
+ if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
+ vfree(free->dirty_bitmap);
+
+ free->phys_mem = 0;
+ free->npages = 0;
+ free->dirty_bitmap = 0;
+}
+
+static void kvm_free_physmem(struct kvm *kvm)
+{
+ int i;
+
+ for (i = 0; i < kvm->nmemslots; ++i)
+ kvm_free_physmem_slot(&kvm->memslots[i], 0);
+}
+
+static void kvm_free_vcpu(struct kvm_vcpu *vcpu)
+{
+ kvm_arch_ops->vcpu_free(vcpu);
+ kvm_mmu_destroy(vcpu);
+}
+
+static void kvm_free_vcpus(struct kvm *kvm)
+{
+ unsigned int i;
+
+ for (i = 0; i < KVM_MAX_VCPUS; ++i)
+ kvm_free_vcpu(&kvm->vcpus[i]);
+}
+
+static int kvm_dev_release(struct inode *inode, struct file *filp)
+{
+ struct kvm *kvm = filp->private_data;
+
+ kvm_free_vcpus(kvm);
+ kvm_free_physmem(kvm);
+ kfree(kvm);
+ return 0;
+}
+
+static void inject_gp(struct kvm_vcpu *vcpu)
+{
+ kvm_arch_ops->inject_gp(vcpu, 0);
+}
+
+static int pdptrs_have_reserved_bits_set(struct kvm_vcpu *vcpu,
+ unsigned long cr3)
+{
+ gfn_t pdpt_gfn = cr3 >> PAGE_SHIFT;
+ unsigned offset = (cr3 & (PAGE_SIZE-1)) >> 5;
+ int i;
+ u64 pdpte;
+ u64 *pdpt;
+ struct kvm_memory_slot *memslot;
+
+ spin_lock(&vcpu->kvm->lock);
+ memslot = gfn_to_memslot(vcpu->kvm, pdpt_gfn);
+ /* FIXME: !memslot - emulate? 0xff? */
+ pdpt = kmap_atomic(gfn_to_page(memslot, pdpt_gfn), KM_USER0);
+
+ for (i = 0; i < 4; ++i) {
+ pdpte = pdpt[offset + i];
+ if ((pdpte & 1) && (pdpte & 0xfffffff0000001e6ull))
+ break;
+ }
+
+ kunmap_atomic(pdpt, KM_USER0);
+ spin_unlock(&vcpu->kvm->lock);
+
+ return i != 4;
+}
+
+void set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
+{
+ if (cr0 & CR0_RESEVED_BITS) {
+ printk(KERN_DEBUG "set_cr0: 0x%lx #GP, reserved bits 0x%lx\n",
+ cr0, vcpu->cr0);
+ inject_gp(vcpu);
+ return;
+ }
+
+ if ((cr0 & CR0_NW_MASK) && !(cr0 & CR0_CD_MASK)) {
+ printk(KERN_DEBUG "set_cr0: #GP, CD == 0 && NW == 1\n");
+ inject_gp(vcpu);
+ return;
+ }
+
+ if ((cr0 & CR0_PG_MASK) && !(cr0 & CR0_PE_MASK)) {
+ printk(KERN_DEBUG "set_cr0: #GP, set PG flag "
+ "and a clear PE flag\n");
+ inject_gp(vcpu);
+ return;
+ }
+
+ if (!is_paging(vcpu) && (cr0 & CR0_PG_MASK)) {
+#ifdef CONFIG_X86_64
+ if ((vcpu->shadow_efer & EFER_LME)) {
+ int cs_db, cs_l;
+
+ if (!is_pae(vcpu)) {
+ printk(KERN_DEBUG "set_cr0: #GP, start paging "
+ "in long mode while PAE is disabled\n");
+ inject_gp(vcpu);
+ return;
+ }
+ kvm_arch_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
+ if (cs_l) {
+ printk(KERN_DEBUG "set_cr0: #GP, start paging "
+ "in long mode while CS.L == 1\n");
+ inject_gp(vcpu);
+ return;
+
+ }
+ } else
+#endif
+ if (is_pae(vcpu) &&
+ pdptrs_have_reserved_bits_set(vcpu, vcpu->cr3)) {
+ printk(KERN_DEBUG "set_cr0: #GP, pdptrs "
+ "reserved bits\n");
+ inject_gp(vcpu);
+ return;
+ }
+
+ }
+
+ kvm_arch_ops->set_cr0(vcpu, cr0);
+ vcpu->cr0 = cr0;
+
+ spin_lock(&vcpu->kvm->lock);
+ kvm_mmu_reset_context(vcpu);
+ spin_unlock(&vcpu->kvm->lock);
+ return;
+}
+EXPORT_SYMBOL_GPL(set_cr0);
+
+void lmsw(struct kvm_vcpu *vcpu, unsigned long msw)
+{
+ set_cr0(vcpu, (vcpu->cr0 & ~0x0ful) | (msw & 0x0f));
+}
+EXPORT_SYMBOL_GPL(lmsw);
+
+void set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
+{
+ if (cr4 & CR4_RESEVED_BITS) {
+ printk(KERN_DEBUG "set_cr4: #GP, reserved bits\n");
+ inject_gp(vcpu);
+ return;
+ }
+
+ if (kvm_arch_ops->is_long_mode(vcpu)) {
+ if (!(cr4 & CR4_PAE_MASK)) {
+ printk(KERN_DEBUG "set_cr4: #GP, clearing PAE while "
+ "in long mode\n");
+ inject_gp(vcpu);
+ return;
+ }
+ } else if (is_paging(vcpu) && !is_pae(vcpu) && (cr4 & CR4_PAE_MASK)
+ && pdptrs_have_reserved_bits_set(vcpu, vcpu->cr3)) {
+ printk(KERN_DEBUG "set_cr4: #GP, pdptrs reserved bits\n");
+ inject_gp(vcpu);
+ }
+
+ if (cr4 & CR4_VMXE_MASK) {
+ printk(KERN_DEBUG "set_cr4: #GP, setting VMXE\n");
+ inject_gp(vcpu);
+ return;
+ }
+ kvm_arch_ops->set_cr4(vcpu, cr4);
+ spin_lock(&vcpu->kvm->lock);
+ kvm_mmu_reset_context(vcpu);
+ spin_unlock(&vcpu->kvm->lock);
+}
+EXPORT_SYMBOL_GPL(set_cr4);
+
+void set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
+{
+ if (kvm_arch_ops->is_long_mode(vcpu)) {
+ if ( cr3 & CR3_L_MODE_RESEVED_BITS) {
+ printk(KERN_DEBUG "set_cr3: #GP, reserved bits\n");
+ inject_gp(vcpu);
+ return;
+ }
+ } else {
+ if (cr3 & CR3_RESEVED_BITS) {
+ printk(KERN_DEBUG "set_cr3: #GP, reserved bits\n");
+ inject_gp(vcpu);
+ return;
+ }
+ if (is_paging(vcpu) && is_pae(vcpu) &&
+ pdptrs_have_reserved_bits_set(vcpu, cr3)) {
+ printk(KERN_DEBUG "set_cr3: #GP, pdptrs "
+ "reserved bits\n");
+ inject_gp(vcpu);
+ return;
+ }
+ }
+
+ vcpu->cr3 = cr3;
+ spin_lock(&vcpu->kvm->lock);
+ vcpu->mmu.new_cr3(vcpu);
+ spin_unlock(&vcpu->kvm->lock);
+}
+EXPORT_SYMBOL_GPL(set_cr3);
+
+void set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
+{
+ if ( cr8 & CR8_RESEVED_BITS) {
+ printk(KERN_DEBUG "set_cr8: #GP, reserved bits 0x%lx\n", cr8);
+ inject_gp(vcpu);
+ return;
+ }
+ vcpu->cr8 = cr8;
+}
+EXPORT_SYMBOL_GPL(set_cr8);
+
+void fx_init(struct kvm_vcpu *vcpu)
+{
+ struct __attribute__ ((__packed__)) fx_image_s {
+ u16 control; //fcw
+ u16 status; //fsw
+ u16 tag; // ftw
+ u16 opcode; //fop
+ u64 ip; // fpu ip
+ u64 operand;// fpu dp
+ u32 mxcsr;
+ u32 mxcsr_mask;
+
+ } *fx_image;
+
+ fx_save(vcpu->host_fx_image);
+ fpu_init();
+ fx_save(vcpu->guest_fx_image);
+ fx_restore(vcpu->host_fx_image);
+
+ fx_image = (struct fx_image_s *)vcpu->guest_fx_image;
+ fx_image->mxcsr = 0x1f80;
+ memset(vcpu->guest_fx_image + sizeof(struct fx_image_s),
+ 0, FX_IMAGE_SIZE - sizeof(struct fx_image_s));
+}
+EXPORT_SYMBOL_GPL(fx_init);
+
+/*
+ * Creates some virtual cpus. Good luck creating more than one.
+ */
+static int kvm_dev_ioctl_create_vcpu(struct kvm *kvm, int n)
+{
+ int r;
+ struct kvm_vcpu *vcpu;
+
+ r = -EINVAL;
+ if (n < 0 || n >= KVM_MAX_VCPUS)
+ goto out;
+
+ vcpu = &kvm->vcpus[n];
+
+ mutex_lock(&vcpu->mutex);
+
+ if (vcpu->vmcs) {
+ mutex_unlock(&vcpu->mutex);
+ return -EEXIST;
+ }
+
+ vcpu->host_fx_image = (char*)ALIGN((hva_t)vcpu->fx_buf,
+ FX_IMAGE_ALIGN);
+ vcpu->guest_fx_image = vcpu->host_fx_image + FX_IMAGE_SIZE;
+
+ vcpu->cpu = -1; /* First load will set up TR */
+ vcpu->kvm = kvm;
+ r = kvm_arch_ops->vcpu_create(vcpu);
+ if (r < 0)
+ goto out_free_vcpus;
+
+ kvm_arch_ops->vcpu_load(vcpu);
+
+ r = kvm_arch_ops->vcpu_setup(vcpu);
+ if (r >= 0)
+ r = kvm_mmu_init(vcpu);
+
+ vcpu_put(vcpu);
+
+ if (r < 0)
+ goto out_free_vcpus;
+
+ return 0;
+
+out_free_vcpus:
+ kvm_free_vcpu(vcpu);
+ mutex_unlock(&vcpu->mutex);
+out:
+ return r;
+}
+
+/*
+ * Allocate some memory and give it an address in the guest physical address
+ * space.
+ *
+ * Discontiguous memory is allowed, mostly for framebuffers.
+ */
+static int kvm_dev_ioctl_set_memory_region(struct kvm *kvm,
+ struct kvm_memory_region *mem)
+{
+ int r;
+ gfn_t base_gfn;
+ unsigned long npages;
+ unsigned long i;
+ struct kvm_memory_slot *memslot;
+ struct kvm_memory_slot old, new;
+ int memory_config_version;
+
+ r = -EINVAL;
+ /* General sanity checks */
+ if (mem->memory_size & (PAGE_SIZE - 1))
+ goto out;
+ if (mem->guest_phys_addr & (PAGE_SIZE - 1))
+ goto out;
+ if (mem->slot >= KVM_MEMORY_SLOTS)
+ goto out;
+ if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)
+ goto out;
+
+ memslot = &kvm->memslots[mem->slot];
+ base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;
+ npages = mem->memory_size >> PAGE_SHIFT;
+
+ if (!npages)
+ mem->flags &= ~KVM_MEM_LOG_DIRTY_PAGES;
+
+raced:
+ spin_lock(&kvm->lock);
+
+ memory_config_version = kvm->memory_config_version;
+ new = old = *memslot;
+
+ new.base_gfn = base_gfn;
+ new.npages = npages;
+ new.flags = mem->flags;
+
+ /* Disallow changing a memory slot's size. */
+ r = -EINVAL;
+ if (npages && old.npages && npages != old.npages)
+ goto out_unlock;
+
+ /* Check for overlaps */
+ r = -EEXIST;
+ for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
+ struct kvm_memory_slot *s = &kvm->memslots[i];
+
+ if (s == memslot)
+ continue;
+ if (!((base_gfn + npages <= s->base_gfn) ||
+ (base_gfn >= s->base_gfn + s->npages)))
+ goto out_unlock;
+ }
+ /*
+ * Do memory allocations outside lock. memory_config_version will
+ * detect any races.
+ */
+ spin_unlock(&kvm->lock);
+
+ /* Deallocate if slot is being removed */
+ if (!npages)
+ new.phys_mem = 0;
+
+ /* Free page dirty bitmap if unneeded */
+ if (!(new.flags & KVM_MEM_LOG_DIRTY_PAGES))
+ new.dirty_bitmap = 0;
+
+ r = -ENOMEM;
+
+ /* Allocate if a slot is being created */
+ if (npages && !new.phys_mem) {
+ new.phys_mem = vmalloc(npages * sizeof(struct page *));
+
+ if (!new.phys_mem)
+ goto out_free;
+
+ memset(new.phys_mem, 0, npages * sizeof(struct page *));
+ for (i = 0; i < npages; ++i) {
+ new.phys_mem[i] = alloc_page(GFP_HIGHUSER
+ | __GFP_ZERO);
+ if (!new.phys_mem[i])
+ goto out_free;
+ }
+ }
+
+ /* Allocate page dirty bitmap if needed */
+ if ((new.flags & KVM_MEM_LOG_DIRTY_PAGES) && !new.dirty_bitmap) {
+ unsigned dirty_bytes = ALIGN(npages, BITS_PER_LONG) / 8;
+
+ new.dirty_bitmap = vmalloc(dirty_bytes);
+ if (!new.dirty_bitmap)
+ goto out_free;
+ memset(new.dirty_bitmap, 0, dirty_bytes);
+ }
+
+ spin_lock(&kvm->lock);
+
+ if (memory_config_version != kvm->memory_config_version) {
+ spin_unlock(&kvm->lock);
+ kvm_free_physmem_slot(&new, &old);
+ goto raced;
+ }
+
+ r = -EAGAIN;
+ if (kvm->busy)
+ goto out_unlock;
+
+ if (mem->slot >= kvm->nmemslots)
+ kvm->nmemslots = mem->slot + 1;
+
+ *memslot = new;
+ ++kvm->memory_config_version;
+
+ spin_unlock(&kvm->lock);
+
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ struct kvm_vcpu *vcpu;
+
+ vcpu = vcpu_load(kvm, i);
+ if (!vcpu)
+ continue;
+ kvm_mmu_reset_context(vcpu);
+ vcpu_put(vcpu);
+ }
+
+ kvm_free_physmem_slot(&old, &new);
+ return 0;
+
+out_unlock:
+ spin_unlock(&kvm->lock);
+out_free:
+ kvm_free_physmem_slot(&new, &old);
+out:
+ return r;
+}
+
+/*
+ * Get (and clear) the dirty memory log for a memory slot.
+ */
+static int kvm_dev_ioctl_get_dirty_log(struct kvm *kvm,
+ struct kvm_dirty_log *log)
+{
+ struct kvm_memory_slot *memslot;
+ int r, i;
+ int n;
+ unsigned long any = 0;
+
+ spin_lock(&kvm->lock);
+
+ /*
+ * Prevent changes to guest memory configuration even while the lock
+ * is not taken.
+ */
+ ++kvm->busy;
+ spin_unlock(&kvm->lock);
+ r = -EINVAL;
+ if (log->slot >= KVM_MEMORY_SLOTS)
+ goto out;
+
+ memslot = &kvm->memslots[log->slot];
+ r = -ENOENT;
+ if (!memslot->dirty_bitmap)
+ goto out;
+
+ n = ALIGN(memslot->npages, 8) / 8;
+
+ for (i = 0; !any && i < n; ++i)
+ any = memslot->dirty_bitmap[i];
+
+ r = -EFAULT;
+ if (copy_to_user(log->dirty_bitmap, memslot->dirty_bitmap, n))
+ goto out;
+
+
+ if (any) {
+ spin_lock(&kvm->lock);
+ kvm_mmu_slot_remove_write_access(kvm, log->slot);
+ spin_unlock(&kvm->lock);
+ memset(memslot->dirty_bitmap, 0, n);
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ struct kvm_vcpu *vcpu = vcpu_load(kvm, i);
+
+ if (!vcpu)
+ continue;
+ kvm_arch_ops->tlb_flush(vcpu);
+ vcpu_put(vcpu);
+ }
+ }
+
+ r = 0;
+
+out:
+ spin_lock(&kvm->lock);
+ --kvm->busy;
+ spin_unlock(&kvm->lock);
+ return r;
+}
+
+struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
+{
+ int i;
+
+ for (i = 0; i < kvm->nmemslots; ++i) {
+ struct kvm_memory_slot *memslot = &kvm->memslots[i];
+
+ if (gfn >= memslot->base_gfn
+ && gfn < memslot->base_gfn + memslot->npages)
+ return memslot;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gfn_to_memslot);
+
+void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
+{
+ int i;
+ struct kvm_memory_slot *memslot = 0;
+ unsigned long rel_gfn;
+
+ for (i = 0; i < kvm->nmemslots; ++i) {
+ memslot = &kvm->memslots[i];
+
+ if (gfn >= memslot->base_gfn
+ && gfn < memslot->base_gfn + memslot->npages) {
+
+ if (!memslot || !memslot->dirty_bitmap)
+ return;
+
+ rel_gfn = gfn - memslot->base_gfn;
+
+ /* avoid RMW */
+ if (!test_bit(rel_gfn, memslot->dirty_bitmap))
+ set_bit(rel_gfn, memslot->dirty_bitmap);
+ return;
+ }
+ }
+}
+
+static int emulator_read_std(unsigned long addr,
+ unsigned long *val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct kvm_vcpu *vcpu = ctxt->vcpu;
+ void *data = val;
+
+ while (bytes) {
+ gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);
+ unsigned offset = addr & (PAGE_SIZE-1);
+ unsigned tocopy = min(bytes, (unsigned)PAGE_SIZE - offset);
+ unsigned long pfn;
+ struct kvm_memory_slot *memslot;
+ void *page;
+
+ if (gpa == UNMAPPED_GVA)
+ return X86EMUL_PROPAGATE_FAULT;
+ pfn = gpa >> PAGE_SHIFT;
+ memslot = gfn_to_memslot(vcpu->kvm, pfn);
+ if (!memslot)
+ return X86EMUL_UNHANDLEABLE;
+ page = kmap_atomic(gfn_to_page(memslot, pfn), KM_USER0);
+
+ memcpy(data, page + offset, tocopy);
+
+ kunmap_atomic(page, KM_USER0);
+
+ bytes -= tocopy;
+ data += tocopy;
+ addr += tocopy;
+ }
+
+ return X86EMUL_CONTINUE;
+}
+
+static int emulator_write_std(unsigned long addr,
+ unsigned long val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt *ctxt)
+{
+ printk(KERN_ERR "emulator_write_std: addr %lx n %d\n",
+ addr, bytes);
+ return X86EMUL_UNHANDLEABLE;
+}
+
+static int emulator_read_emulated(unsigned long addr,
+ unsigned long *val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct kvm_vcpu *vcpu = ctxt->vcpu;
+
+ if (vcpu->mmio_read_completed) {
+ memcpy(val, vcpu->mmio_data, bytes);
+ vcpu->mmio_read_completed = 0;
+ return X86EMUL_CONTINUE;
+ } else if (emulator_read_std(addr, val, bytes, ctxt)
+ == X86EMUL_CONTINUE)
+ return X86EMUL_CONTINUE;
+ else {
+ gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);
+ if (gpa == UNMAPPED_GVA)
+ return vcpu_printf(vcpu, "not present\n"), X86EMUL_PROPAGATE_FAULT;
+ vcpu->mmio_needed = 1;
+ vcpu->mmio_phys_addr = gpa;
+ vcpu->mmio_size = bytes;
+ vcpu->mmio_is_write = 0;
+
+ return X86EMUL_UNHANDLEABLE;
+ }
+}
+
+static int emulator_write_emulated(unsigned long addr,
+ unsigned long val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct kvm_vcpu *vcpu = ctxt->vcpu;
+ gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);
+
+ if (gpa == UNMAPPED_GVA)
+ return X86EMUL_PROPAGATE_FAULT;
+
+ vcpu->mmio_needed = 1;
+ vcpu->mmio_phys_addr = gpa;
+ vcpu->mmio_size = bytes;
+ vcpu->mmio_is_write = 1;
+ memcpy(vcpu->mmio_data, &val, bytes);
+
+ return X86EMUL_CONTINUE;
+}
+
+static int emulator_cmpxchg_emulated(unsigned long addr,
+ unsigned long old,
+ unsigned long new,
+ unsigned int bytes,
+ struct x86_emulate_ctxt *ctxt)
+{
+ static int reported;
+
+ if (!reported) {
+ reported = 1;
+ printk(KERN_WARNING "kvm: emulating exchange as write\n");
+ }
+ return emulator_write_emulated(addr, new, bytes, ctxt);
+}
+
+static unsigned long get_segment_base(struct kvm_vcpu *vcpu, int seg)
+{
+ return kvm_arch_ops->get_segment_base(vcpu, seg);
+}
+
+int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address)
+{
+ spin_lock(&vcpu->kvm->lock);
+ vcpu->mmu.inval_page(vcpu, address);
+ spin_unlock(&vcpu->kvm->lock);
+ kvm_arch_ops->invlpg(vcpu, address);
+ return X86EMUL_CONTINUE;
+}
+
+int emulate_clts(struct kvm_vcpu *vcpu)
+{
+ unsigned long cr0 = vcpu->cr0;
+
+ cr0 &= ~CR0_TS_MASK;
+ kvm_arch_ops->set_cr0(vcpu, cr0);
+ return X86EMUL_CONTINUE;
+}
+
+int emulator_get_dr(struct x86_emulate_ctxt* ctxt, int dr, unsigned long *dest)
+{
+ struct kvm_vcpu *vcpu = ctxt->vcpu;
+
+ switch (dr) {
+ case 0 ... 3:
+ *dest = kvm_arch_ops->get_dr(vcpu, dr);
+ return X86EMUL_CONTINUE;
+ default:
+ printk(KERN_DEBUG "%s: unexpected dr %u\n",
+ __FUNCTION__, dr);
+ return X86EMUL_UNHANDLEABLE;
+ }
+}
+
+int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long value)
+{
+ unsigned long mask = (ctxt->mode == X86EMUL_MODE_PROT64) ? ~0ULL : ~0U;
+ int exception;
+
+ kvm_arch_ops->set_dr(ctxt->vcpu, dr, value & mask, &exception);
+ if (exception) {
+ /* FIXME: better handling */
+ return X86EMUL_UNHANDLEABLE;
+ }
+ return X86EMUL_CONTINUE;
+}
+
+static void report_emulation_failure(struct x86_emulate_ctxt *ctxt)
+{
+ static int reported;
+ u8 opcodes[4];
+ unsigned long rip = ctxt->vcpu->rip;
+ unsigned long rip_linear;
+
+ rip_linear = rip + get_segment_base(ctxt->vcpu, VCPU_SREG_CS);
+
+ if (reported)
+ return;
+
+ emulator_read_std(rip_linear, (void *)opcodes, 4, ctxt);
+
+ printk(KERN_ERR "emulation failed but !mmio_needed?"
+ " rip %lx %02x %02x %02x %02x\n",
+ rip, opcodes[0], opcodes[1], opcodes[2], opcodes[3]);
+ reported = 1;
+}
+
+struct x86_emulate_ops emulate_ops = {
+ .read_std = emulator_read_std,
+ .write_std = emulator_write_std,
+ .read_emulated = emulator_read_emulated,
+ .write_emulated = emulator_write_emulated,
+ .cmpxchg_emulated = emulator_cmpxchg_emulated,
+};
+
+int emulate_instruction(struct kvm_vcpu *vcpu,
+ struct kvm_run *run,
+ unsigned long cr2,
+ u16 error_code)
+{
+ struct x86_emulate_ctxt emulate_ctxt;
+ int r;
+ int cs_db, cs_l;
+
+ kvm_arch_ops->cache_regs(vcpu);
+
+ kvm_arch_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
+
+ emulate_ctxt.vcpu = vcpu;
+ emulate_ctxt.eflags = kvm_arch_ops->get_rflags(vcpu);
+ emulate_ctxt.cr2 = cr2;
+ emulate_ctxt.mode = (emulate_ctxt.eflags & X86_EFLAGS_VM)
+ ? X86EMUL_MODE_REAL : cs_l
+ ? X86EMUL_MODE_PROT64 : cs_db
+ ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;
+
+ if (emulate_ctxt.mode == X86EMUL_MODE_PROT64) {
+ emulate_ctxt.cs_base = 0;
+ emulate_ctxt.ds_base = 0;
+ emulate_ctxt.es_base = 0;
+ emulate_ctxt.ss_base = 0;
+ } else {
+ emulate_ctxt.cs_base = get_segment_base(vcpu, VCPU_SREG_CS);
+ emulate_ctxt.ds_base = get_segment_base(vcpu, VCPU_SREG_DS);
+ emulate_ctxt.es_base = get_segment_base(vcpu, VCPU_SREG_ES);
+ emulate_ctxt.ss_base = get_segment_base(vcpu, VCPU_SREG_SS);
+ }
+
+ emulate_ctxt.gs_base = get_segment_base(vcpu, VCPU_SREG_GS);
+ emulate_ctxt.fs_base = get_segment_base(vcpu, VCPU_SREG_FS);
+
+ vcpu->mmio_is_write = 0;
+ r = x86_emulate_memop(&emulate_ctxt, &emulate_ops);
+
+ if ((r || vcpu->mmio_is_write) && run) {
+ run->mmio.phys_addr = vcpu->mmio_phys_addr;
+ memcpy(run->mmio.data, vcpu->mmio_data, 8);
+ run->mmio.len = vcpu->mmio_size;
+ run->mmio.is_write = vcpu->mmio_is_write;
+ }
+
+ if (r) {
+ if (!vcpu->mmio_needed) {
+ report_emulation_failure(&emulate_ctxt);
+ return EMULATE_FAIL;
+ }
+ return EMULATE_DO_MMIO;
+ }
+
+ kvm_arch_ops->decache_regs(vcpu);
+ kvm_arch_ops->set_rflags(vcpu, emulate_ctxt.eflags);
+
+ if (vcpu->mmio_is_write)
+ return EMULATE_DO_MMIO;
+
+ return EMULATE_DONE;
+}
+EXPORT_SYMBOL_GPL(emulate_instruction);
+
+static u64 mk_cr_64(u64 curr_cr, u32 new_val)
+{
+ return (curr_cr & ~((1ULL << 32) - 1)) | new_val;
+}
+
+void realmode_lgdt(struct kvm_vcpu *vcpu, u16 limit, unsigned long base)
+{
+ struct descriptor_table dt = { limit, base };
+
+ kvm_arch_ops->set_gdt(vcpu, &dt);
+}
+
+void realmode_lidt(struct kvm_vcpu *vcpu, u16 limit, unsigned long base)
+{
+ struct descriptor_table dt = { limit, base };
+
+ kvm_arch_ops->set_idt(vcpu, &dt);
+}
+
+void realmode_lmsw(struct kvm_vcpu *vcpu, unsigned long msw,
+ unsigned long *rflags)
+{
+ lmsw(vcpu, msw);
+ *rflags = kvm_arch_ops->get_rflags(vcpu);
+}
+
+unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
+{
+ switch (cr) {
+ case 0:
+ return vcpu->cr0;
+ case 2:
+ return vcpu->cr2;
+ case 3:
+ return vcpu->cr3;
+ case 4:
+ return vcpu->cr4;
+ default:
+ vcpu_printf(vcpu, "%s: unexpected cr %u\n", __FUNCTION__, cr);
+ return 0;
+ }
+}
+
+void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val,
+ unsigned long *rflags)
+{
+ switch (cr) {
+ case 0:
+ set_cr0(vcpu, mk_cr_64(vcpu->cr0, val));
+ *rflags = kvm_arch_ops->get_rflags(vcpu);
+ break;
+ case 2:
+ vcpu->cr2 = val;
+ break;
+ case 3:
+ set_cr3(vcpu, val);
+ break;
+ case 4:
+ set_cr4(vcpu, mk_cr_64(vcpu->cr4, val));
+ break;
+ default:
+ vcpu_printf(vcpu, "%s: unexpected cr %u\n", __FUNCTION__, cr);
+ }
+}
+
+/*
+ * Reads an msr value (of 'msr_index') into 'pdata'.
+ * Returns 0 on success, non-0 otherwise.
+ * Assumes vcpu_load() was already called.
+ */
+static int get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
+{
+ return kvm_arch_ops->get_msr(vcpu, msr_index, pdata);
+}
+
+#ifdef CONFIG_X86_64
+
+void set_efer(struct kvm_vcpu *vcpu, u64 efer)
+{
+ if (efer & EFER_RESERVED_BITS) {
+ printk(KERN_DEBUG "set_efer: 0x%llx #GP, reserved bits\n",
+ efer);
+ inject_gp(vcpu);
+ return;
+ }
+
+ if (is_paging(vcpu)
+ && (vcpu->shadow_efer & EFER_LME) != (efer & EFER_LME)) {
+ printk(KERN_DEBUG "set_efer: #GP, change LME while paging\n");
+ inject_gp(vcpu);
+ return;
+ }
+
+ kvm_arch_ops->set_efer(vcpu, efer);
+
+ efer &= ~EFER_LMA;
+ efer |= vcpu->shadow_efer & EFER_LMA;
+
+ vcpu->shadow_efer = efer;
+}
+EXPORT_SYMBOL_GPL(set_efer);
+
+#endif
+
+/*
+ * Writes msr value into into the appropriate "register".
+ * Returns 0 on success, non-0 otherwise.
+ * Assumes vcpu_load() was already called.
+ */
+static int set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
+{
+ return kvm_arch_ops->set_msr(vcpu, msr_index, data);
+}
+
+void kvm_resched(struct kvm_vcpu *vcpu)
+{
+ vcpu_put(vcpu);
+ cond_resched();
+ /* Cannot fail - no vcpu unplug yet. */
+ vcpu_load(vcpu->kvm, vcpu_slot(vcpu));
+}
+EXPORT_SYMBOL_GPL(kvm_resched);
+
+void load_msrs(struct vmx_msr_entry *e, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ wrmsrl(e[i].index, e[i].data);
+}
+EXPORT_SYMBOL_GPL(load_msrs);
+
+void save_msrs(struct vmx_msr_entry *e, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ rdmsrl(e[i].index, e[i].data);
+}
+EXPORT_SYMBOL_GPL(save_msrs);
+
+static int kvm_dev_ioctl_run(struct kvm *kvm, struct kvm_run *kvm_run)
+{
+ struct kvm_vcpu *vcpu;
+ int r;
+
+ if (kvm_run->vcpu < 0 || kvm_run->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+
+ vcpu = vcpu_load(kvm, kvm_run->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ if (kvm_run->emulated) {
+ kvm_arch_ops->skip_emulated_instruction(vcpu);
+ kvm_run->emulated = 0;
+ }
+
+ if (kvm_run->mmio_completed) {
+ memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
+ vcpu->mmio_read_completed = 1;
+ }
+
+ vcpu->mmio_needed = 0;
+
+ r = kvm_arch_ops->run(vcpu, kvm_run);
+
+ vcpu_put(vcpu);
+ return r;
+}
+
+static int kvm_dev_ioctl_get_regs(struct kvm *kvm, struct kvm_regs *regs)
+{
+ struct kvm_vcpu *vcpu;
+
+ if (regs->vcpu < 0 || regs->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+
+ vcpu = vcpu_load(kvm, regs->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ kvm_arch_ops->cache_regs(vcpu);
+
+ regs->rax = vcpu->regs[VCPU_REGS_RAX];
+ regs->rbx = vcpu->regs[VCPU_REGS_RBX];
+ regs->rcx = vcpu->regs[VCPU_REGS_RCX];
+ regs->rdx = vcpu->regs[VCPU_REGS_RDX];
+ regs->rsi = vcpu->regs[VCPU_REGS_RSI];
+ regs->rdi = vcpu->regs[VCPU_REGS_RDI];
+ regs->rsp = vcpu->regs[VCPU_REGS_RSP];
+ regs->rbp = vcpu->regs[VCPU_REGS_RBP];
+#ifdef CONFIG_X86_64
+ regs->r8 = vcpu->regs[VCPU_REGS_R8];
+ regs->r9 = vcpu->regs[VCPU_REGS_R9];
+ regs->r10 = vcpu->regs[VCPU_REGS_R10];
+ regs->r11 = vcpu->regs[VCPU_REGS_R11];
+ regs->r12 = vcpu->regs[VCPU_REGS_R12];
+ regs->r13 = vcpu->regs[VCPU_REGS_R13];
+ regs->r14 = vcpu->regs[VCPU_REGS_R14];
+ regs->r15 = vcpu->regs[VCPU_REGS_R15];
+#endif
+
+ regs->rip = vcpu->rip;
+ regs->rflags = kvm_arch_ops->get_rflags(vcpu);
+
+ /*
+ * Don't leak debug flags in case they were set for guest debugging
+ */
+ if (vcpu->guest_debug.enabled && vcpu->guest_debug.singlestep)
+ regs->rflags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static int kvm_dev_ioctl_set_regs(struct kvm *kvm, struct kvm_regs *regs)
+{
+ struct kvm_vcpu *vcpu;
+
+ if (regs->vcpu < 0 || regs->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+
+ vcpu = vcpu_load(kvm, regs->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ vcpu->regs[VCPU_REGS_RAX] = regs->rax;
+ vcpu->regs[VCPU_REGS_RBX] = regs->rbx;
+ vcpu->regs[VCPU_REGS_RCX] = regs->rcx;
+ vcpu->regs[VCPU_REGS_RDX] = regs->rdx;
+ vcpu->regs[VCPU_REGS_RSI] = regs->rsi;
+ vcpu->regs[VCPU_REGS_RDI] = regs->rdi;
+ vcpu->regs[VCPU_REGS_RSP] = regs->rsp;
+ vcpu->regs[VCPU_REGS_RBP] = regs->rbp;
+#ifdef CONFIG_X86_64
+ vcpu->regs[VCPU_REGS_R8] = regs->r8;
+ vcpu->regs[VCPU_REGS_R9] = regs->r9;
+ vcpu->regs[VCPU_REGS_R10] = regs->r10;
+ vcpu->regs[VCPU_REGS_R11] = regs->r11;
+ vcpu->regs[VCPU_REGS_R12] = regs->r12;
+ vcpu->regs[VCPU_REGS_R13] = regs->r13;
+ vcpu->regs[VCPU_REGS_R14] = regs->r14;
+ vcpu->regs[VCPU_REGS_R15] = regs->r15;
+#endif
+
+ vcpu->rip = regs->rip;
+ kvm_arch_ops->set_rflags(vcpu, regs->rflags);
+
+ kvm_arch_ops->decache_regs(vcpu);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static void get_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ return kvm_arch_ops->get_segment(vcpu, var, seg);
+}
+
+static int kvm_dev_ioctl_get_sregs(struct kvm *kvm, struct kvm_sregs *sregs)
+{
+ struct kvm_vcpu *vcpu;
+ struct descriptor_table dt;
+
+ if (sregs->vcpu < 0 || sregs->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+ vcpu = vcpu_load(kvm, sregs->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ get_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
+ get_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
+ get_segment(vcpu, &sregs->es, VCPU_SREG_ES);
+ get_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
+ get_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
+ get_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
+
+ get_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
+ get_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
+
+ kvm_arch_ops->get_idt(vcpu, &dt);
+ sregs->idt.limit = dt.limit;
+ sregs->idt.base = dt.base;
+ kvm_arch_ops->get_gdt(vcpu, &dt);
+ sregs->gdt.limit = dt.limit;
+ sregs->gdt.base = dt.base;
+
+ sregs->cr0 = vcpu->cr0;
+ sregs->cr2 = vcpu->cr2;
+ sregs->cr3 = vcpu->cr3;
+ sregs->cr4 = vcpu->cr4;
+ sregs->cr8 = vcpu->cr8;
+ sregs->efer = vcpu->shadow_efer;
+ sregs->apic_base = vcpu->apic_base;
+
+ memcpy(sregs->interrupt_bitmap, vcpu->irq_pending,
+ sizeof sregs->interrupt_bitmap);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static void set_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ return kvm_arch_ops->set_segment(vcpu, var, seg);
+}
+
+static int kvm_dev_ioctl_set_sregs(struct kvm *kvm, struct kvm_sregs *sregs)
+{
+ struct kvm_vcpu *vcpu;
+ int mmu_reset_needed = 0;
+ int i;
+ struct descriptor_table dt;
+
+ if (sregs->vcpu < 0 || sregs->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+ vcpu = vcpu_load(kvm, sregs->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ set_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
+ set_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
+ set_segment(vcpu, &sregs->es, VCPU_SREG_ES);
+ set_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
+ set_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
+ set_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
+
+ set_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
+ set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
+
+ dt.limit = sregs->idt.limit;
+ dt.base = sregs->idt.base;
+ kvm_arch_ops->set_idt(vcpu, &dt);
+ dt.limit = sregs->gdt.limit;
+ dt.base = sregs->gdt.base;
+ kvm_arch_ops->set_gdt(vcpu, &dt);
+
+ vcpu->cr2 = sregs->cr2;
+ mmu_reset_needed |= vcpu->cr3 != sregs->cr3;
+ vcpu->cr3 = sregs->cr3;
+
+ vcpu->cr8 = sregs->cr8;
+
+ mmu_reset_needed |= vcpu->shadow_efer != sregs->efer;
+#ifdef CONFIG_X86_64
+ kvm_arch_ops->set_efer(vcpu, sregs->efer);
+#endif
+ vcpu->apic_base = sregs->apic_base;
+
+ mmu_reset_needed |= vcpu->cr0 != sregs->cr0;
+ kvm_arch_ops->set_cr0_no_modeswitch(vcpu, sregs->cr0);
+
+ mmu_reset_needed |= vcpu->cr4 != sregs->cr4;
+ kvm_arch_ops->set_cr4(vcpu, sregs->cr4);
+
+ if (mmu_reset_needed)
+ kvm_mmu_reset_context(vcpu);
+
+ memcpy(vcpu->irq_pending, sregs->interrupt_bitmap,
+ sizeof vcpu->irq_pending);
+ vcpu->irq_summary = 0;
+ for (i = 0; i < NR_IRQ_WORDS; ++i)
+ if (vcpu->irq_pending[i])
+ __set_bit(i, &vcpu->irq_summary);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+/*
+ * List of msr numbers which we expose to userspace through KVM_GET_MSRS
+ * and KVM_SET_MSRS, and KVM_GET_MSR_INDEX_LIST.
+ */
+static u32 msrs_to_save[] = {
+ MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
+ MSR_K6_STAR,
+#ifdef CONFIG_X86_64
+ MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
+#endif
+ MSR_IA32_TIME_STAMP_COUNTER,
+};
+
+
+/*
+ * Adapt set_msr() to msr_io()'s calling convention
+ */
+static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
+{
+ return set_msr(vcpu, index, *data);
+}
+
+/*
+ * Read or write a bunch of msrs. All parameters are kernel addresses.
+ *
+ * @return number of msrs set successfully.
+ */
+static int __msr_io(struct kvm *kvm, struct kvm_msrs *msrs,
+ struct kvm_msr_entry *entries,
+ int (*do_msr)(struct kvm_vcpu *vcpu,
+ unsigned index, u64 *data))
+{
+ struct kvm_vcpu *vcpu;
+ int i;
+
+ if (msrs->vcpu < 0 || msrs->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+
+ vcpu = vcpu_load(kvm, msrs->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ for (i = 0; i < msrs->nmsrs; ++i)
+ if (do_msr(vcpu, entries[i].index, &entries[i].data))
+ break;
+
+ vcpu_put(vcpu);
+
+ return i;
+}
+
+/*
+ * Read or write a bunch of msrs. Parameters are user addresses.
+ *
+ * @return number of msrs set successfully.
+ */
+static int msr_io(struct kvm *kvm, struct kvm_msrs __user *user_msrs,
+ int (*do_msr)(struct kvm_vcpu *vcpu,
+ unsigned index, u64 *data),
+ int writeback)
+{
+ struct kvm_msrs msrs;
+ struct kvm_msr_entry *entries;
+ int r, n;
+ unsigned size;
+
+ r = -EFAULT;
+ if (copy_from_user(&msrs, user_msrs, sizeof msrs))
+ goto out;
+
+ r = -E2BIG;
+ if (msrs.nmsrs >= MAX_IO_MSRS)
+ goto out;
+
+ r = -ENOMEM;
+ size = sizeof(struct kvm_msr_entry) * msrs.nmsrs;
+ entries = vmalloc(size);
+ if (!entries)
+ goto out;
+
+ r = -EFAULT;
+ if (copy_from_user(entries, user_msrs->entries, size))
+ goto out_free;
+
+ r = n = __msr_io(kvm, &msrs, entries, do_msr);
+ if (r < 0)
+ goto out_free;
+
+ r = -EFAULT;
+ if (writeback && copy_to_user(user_msrs->entries, entries, size))
+ goto out_free;
+
+ r = n;
+
+out_free:
+ vfree(entries);
+out:
+ return r;
+}
+
+/*
+ * Translate a guest virtual address to a guest physical address.
+ */
+static int kvm_dev_ioctl_translate(struct kvm *kvm, struct kvm_translation *tr)
+{
+ unsigned long vaddr = tr->linear_address;
+ struct kvm_vcpu *vcpu;
+ gpa_t gpa;
+
+ vcpu = vcpu_load(kvm, tr->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+ spin_lock(&kvm->lock);
+ gpa = vcpu->mmu.gva_to_gpa(vcpu, vaddr);
+ tr->physical_address = gpa;
+ tr->valid = gpa != UNMAPPED_GVA;
+ tr->writeable = 1;
+ tr->usermode = 0;
+ spin_unlock(&kvm->lock);
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static int kvm_dev_ioctl_interrupt(struct kvm *kvm, struct kvm_interrupt *irq)
+{
+ struct kvm_vcpu *vcpu;
+
+ if (irq->vcpu < 0 || irq->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+ if (irq->irq < 0 || irq->irq >= 256)
+ return -EINVAL;
+ vcpu = vcpu_load(kvm, irq->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ set_bit(irq->irq, vcpu->irq_pending);
+ set_bit(irq->irq / BITS_PER_LONG, &vcpu->irq_summary);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static int kvm_dev_ioctl_debug_guest(struct kvm *kvm,
+ struct kvm_debug_guest *dbg)
+{
+ struct kvm_vcpu *vcpu;
+ int r;
+
+ if (dbg->vcpu < 0 || dbg->vcpu >= KVM_MAX_VCPUS)
+ return -EINVAL;
+ vcpu = vcpu_load(kvm, dbg->vcpu);
+ if (!vcpu)
+ return -ENOENT;
+
+ r = kvm_arch_ops->set_guest_debug(vcpu, dbg);
+
+ vcpu_put(vcpu);
+
+ return r;
+}
+
+static long kvm_dev_ioctl(struct file *filp,
+ unsigned int ioctl, unsigned long arg)
+{
+ struct kvm *kvm = filp->private_data;
+ int r = -EINVAL;
+
+ switch (ioctl) {
+ case KVM_CREATE_VCPU: {
+ r = kvm_dev_ioctl_create_vcpu(kvm, arg);
+ if (r)
+ goto out;
+ break;
+ }
+ case KVM_RUN: {
+ struct kvm_run kvm_run;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_run, (void *)arg, sizeof kvm_run))
+ goto out;
+ r = kvm_dev_ioctl_run(kvm, &kvm_run);
+ if (r < 0)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user((void *)arg, &kvm_run, sizeof kvm_run))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_GET_REGS: {
+ struct kvm_regs kvm_regs;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_regs, (void *)arg, sizeof kvm_regs))
+ goto out;
+ r = kvm_dev_ioctl_get_regs(kvm, &kvm_regs);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user((void *)arg, &kvm_regs, sizeof kvm_regs))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_SET_REGS: {
+ struct kvm_regs kvm_regs;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_regs, (void *)arg, sizeof kvm_regs))
+ goto out;
+ r = kvm_dev_ioctl_set_regs(kvm, &kvm_regs);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_GET_SREGS: {
+ struct kvm_sregs kvm_sregs;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_sregs, (void *)arg, sizeof kvm_sregs))
+ goto out;
+ r = kvm_dev_ioctl_get_sregs(kvm, &kvm_sregs);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user((void *)arg, &kvm_sregs, sizeof kvm_sregs))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_SET_SREGS: {
+ struct kvm_sregs kvm_sregs;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_sregs, (void *)arg, sizeof kvm_sregs))
+ goto out;
+ r = kvm_dev_ioctl_set_sregs(kvm, &kvm_sregs);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_TRANSLATE: {
+ struct kvm_translation tr;
+
+ r = -EFAULT;
+ if (copy_from_user(&tr, (void *)arg, sizeof tr))
+ goto out;
+ r = kvm_dev_ioctl_translate(kvm, &tr);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user((void *)arg, &tr, sizeof tr))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_INTERRUPT: {
+ struct kvm_interrupt irq;
+
+ r = -EFAULT;
+ if (copy_from_user(&irq, (void *)arg, sizeof irq))
+ goto out;
+ r = kvm_dev_ioctl_interrupt(kvm, &irq);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_DEBUG_GUEST: {
+ struct kvm_debug_guest dbg;
+
+ r = -EFAULT;
+ if (copy_from_user(&dbg, (void *)arg, sizeof dbg))
+ goto out;
+ r = kvm_dev_ioctl_debug_guest(kvm, &dbg);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_SET_MEMORY_REGION: {
+ struct kvm_memory_region kvm_mem;
+
+ r = -EFAULT;
+ if (copy_from_user(&kvm_mem, (void *)arg, sizeof kvm_mem))
+ goto out;
+ r = kvm_dev_ioctl_set_memory_region(kvm, &kvm_mem);
+ if (r)
+ goto out;
+ break;
+ }
+ case KVM_GET_DIRTY_LOG: {
+ struct kvm_dirty_log log;
+
+ r = -EFAULT;
+ if (copy_from_user(&log, (void *)arg, sizeof log))
+ goto out;
+ r = kvm_dev_ioctl_get_dirty_log(kvm, &log);
+ if (r)
+ goto out;
+ break;
+ }
+ case KVM_GET_MSRS:
+ r = msr_io(kvm, (void __user *)arg, get_msr, 1);
+ break;
+ case KVM_SET_MSRS:
+ r = msr_io(kvm, (void __user *)arg, do_set_msr, 0);
+ break;
+ case KVM_GET_MSR_INDEX_LIST: {
+ struct kvm_msr_list __user *user_msr_list = (void __user *)arg;
+ struct kvm_msr_list msr_list;
+ unsigned n;
+
+ r = -EFAULT;
+ if (copy_from_user(&msr_list, user_msr_list, sizeof msr_list))
+ goto out;
+ n = msr_list.nmsrs;
+ msr_list.nmsrs = ARRAY_SIZE(msrs_to_save);
+ if (copy_to_user(user_msr_list, &msr_list, sizeof msr_list))
+ goto out;
+ r = -E2BIG;
+ if (n < ARRAY_SIZE(msrs_to_save))
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user(user_msr_list->indices, &msrs_to_save,
+ sizeof msrs_to_save))
+ goto out;
+ r = 0;
+ }
+ default:
+ ;
+ }
+out:
+ return r;
+}
+
+static struct page *kvm_dev_nopage(struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ struct kvm *kvm = vma->vm_file->private_data;
+ unsigned long pgoff;
+ struct kvm_memory_slot *slot;
+ struct page *page;
+
+ *type = VM_FAULT_MINOR;
+ pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ slot = gfn_to_memslot(kvm, pgoff);
+ if (!slot)
+ return NOPAGE_SIGBUS;
+ page = gfn_to_page(slot, pgoff);
+ if (!page)
+ return NOPAGE_SIGBUS;
+ get_page(page);
+ return page;
+}
+
+static struct vm_operations_struct kvm_dev_vm_ops = {
+ .nopage = kvm_dev_nopage,
+};
+
+static int kvm_dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ vma->vm_ops = &kvm_dev_vm_ops;
+ return 0;
+}
+
+static struct file_operations kvm_chardev_ops = {
+ .open = kvm_dev_open,
+ .release = kvm_dev_release,
+ .unlocked_ioctl = kvm_dev_ioctl,
+ .compat_ioctl = kvm_dev_ioctl,
+ .mmap = kvm_dev_mmap,
+};
+
+static struct miscdevice kvm_dev = {
+ MISC_DYNAMIC_MINOR,
+ "kvm",
+ &kvm_chardev_ops,
+};
+
+static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v)
+{
+ if (val == SYS_RESTART) {
+ /*
+ * Some (well, at least mine) BIOSes hang on reboot if
+ * in vmx root mode.
+ */
+ printk(KERN_INFO "kvm: exiting hardware virtualization\n");
+ on_each_cpu(kvm_arch_ops->hardware_disable, 0, 0, 1);
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block kvm_reboot_notifier = {
+ .notifier_call = kvm_reboot,
+ .priority = 0,
+};
+
+static __init void kvm_init_debug(void)
+{
+ struct kvm_stats_debugfs_item *p;
+
+ debugfs_dir = debugfs_create_dir("kvm", 0);
+ for (p = debugfs_entries; p->name; ++p)
+ p->dentry = debugfs_create_u32(p->name, 0444, debugfs_dir,
+ p->data);
+}
+
+static void kvm_exit_debug(void)
+{
+ struct kvm_stats_debugfs_item *p;
+
+ for (p = debugfs_entries; p->name; ++p)
+ debugfs_remove(p->dentry);
+ debugfs_remove(debugfs_dir);
+}
+
+hpa_t bad_page_address;
+
+int kvm_init_arch(struct kvm_arch_ops *ops, struct module *module)
+{
+ int r;
+
+ kvm_arch_ops = ops;
+
+ if (!kvm_arch_ops->cpu_has_kvm_support()) {
+ printk(KERN_ERR "kvm: no hardware support\n");
+ return -EOPNOTSUPP;
+ }
+ if (kvm_arch_ops->disabled_by_bios()) {
+ printk(KERN_ERR "kvm: disabled by bios\n");
+ return -EOPNOTSUPP;
+ }
+
+ r = kvm_arch_ops->hardware_setup();
+ if (r < 0)
+ return r;
+
+ on_each_cpu(kvm_arch_ops->hardware_enable, 0, 0, 1);
+ register_reboot_notifier(&kvm_reboot_notifier);
+
+ kvm_chardev_ops.owner = module;
+
+ r = misc_register(&kvm_dev);
+ if (r) {
+ printk (KERN_ERR "kvm: misc device register failed\n");
+ goto out_free;
+ }
+
+ return r;
+
+out_free:
+ unregister_reboot_notifier(&kvm_reboot_notifier);
+ on_each_cpu(kvm_arch_ops->hardware_disable, 0, 0, 1);
+ kvm_arch_ops->hardware_unsetup();
+ return r;
+}
+
+void kvm_exit_arch(void)
+{
+ misc_deregister(&kvm_dev);
+
+ unregister_reboot_notifier(&kvm_reboot_notifier);
+ on_each_cpu(kvm_arch_ops->hardware_disable, 0, 0, 1);
+ kvm_arch_ops->hardware_unsetup();
+}
+
+static __init int kvm_init(void)
+{
+ static struct page *bad_page;
+ int r = 0;
+
+ kvm_init_debug();
+
+ if ((bad_page = alloc_page(GFP_KERNEL)) == NULL) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ bad_page_address = page_to_pfn(bad_page) << PAGE_SHIFT;
+ memset(__va(bad_page_address), 0, PAGE_SIZE);
+
+ return r;
+
+out:
+ kvm_exit_debug();
+ return r;
+}
+
+static __exit void kvm_exit(void)
+{
+ kvm_exit_debug();
+ __free_page(pfn_to_page(bad_page_address >> PAGE_SHIFT));
+}
+
+module_init(kvm_init)
+module_exit(kvm_exit)
+
+EXPORT_SYMBOL_GPL(kvm_init_arch);
+EXPORT_SYMBOL_GPL(kvm_exit_arch);
diff --git a/drivers/kvm/kvm_svm.h b/drivers/kvm/kvm_svm.h
new file mode 100644
index 0000000..74cc862
--- /dev/null
+++ b/drivers/kvm/kvm_svm.h
@@ -0,0 +1,44 @@
+#ifndef __KVM_SVM_H
+#define __KVM_SVM_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/msr.h>
+
+#include "svm.h"
+#include "kvm.h"
+
+static const u32 host_save_msrs[] = {
+#ifdef CONFIG_X86_64
+ MSR_STAR, MSR_LSTAR, MSR_CSTAR, MSR_SYSCALL_MASK, MSR_KERNEL_GS_BASE,
+ MSR_FS_BASE, MSR_GS_BASE,
+#endif
+ MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
+ MSR_IA32_DEBUGCTLMSR, /*MSR_IA32_LASTBRANCHFROMIP,
+ MSR_IA32_LASTBRANCHTOIP, MSR_IA32_LASTINTFROMIP,MSR_IA32_LASTINTTOIP,*/
+};
+
+#define NR_HOST_SAVE_MSRS (sizeof(host_save_msrs) / sizeof(*host_save_msrs))
+#define NUM_DB_REGS 4
+
+struct vcpu_svm {
+ struct vmcb *vmcb;
+ unsigned long vmcb_pa;
+ struct svm_cpu_data *svm_data;
+ uint64_t asid_generation;
+
+ unsigned long cr0;
+ unsigned long cr4;
+ unsigned long db_regs[NUM_DB_REGS];
+
+ u64 next_rip;
+
+ u64 host_msrs[NR_HOST_SAVE_MSRS];
+ unsigned long host_cr2;
+ unsigned long host_db_regs[NUM_DB_REGS];
+ unsigned long host_dr6;
+ unsigned long host_dr7;
+};
+
+#endif
+
diff --git a/drivers/kvm/kvm_vmx.h b/drivers/kvm/kvm_vmx.h
new file mode 100644
index 0000000..d139f73
--- /dev/null
+++ b/drivers/kvm/kvm_vmx.h
@@ -0,0 +1,14 @@
+#ifndef __KVM_VMX_H
+#define __KVM_VMX_H
+
+#ifdef CONFIG_X86_64
+/*
+ * avoid save/load MSR_SYSCALL_MASK and MSR_LSTAR by std vt
+ * mechanism (cpu bug AA24)
+ */
+#define NR_BAD_MSRS 2
+#else
+#define NR_BAD_MSRS 0
+#endif
+
+#endif
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c
new file mode 100644
index 0000000..3d367cb
--- /dev/null
+++ b/drivers/kvm/mmu.c
@@ -0,0 +1,686 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables machines with Intel VT-x extensions to run virtual
+ * machines without emulation or binary translation.
+ *
+ * MMU support
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Yaniv Kamay <yaniv@qumranet.com>
+ * Avi Kivity <avi@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/page.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+
+#include "vmx.h"
+#include "kvm.h"
+
+#define pgprintk(x...) do { } while (0)
+
+#define ASSERT(x) \
+ if (!(x)) { \
+ printk(KERN_WARNING "assertion failed %s:%d: %s\n", \
+ __FILE__, __LINE__, #x); \
+ }
+
+#define PT64_ENT_PER_PAGE 512
+#define PT32_ENT_PER_PAGE 1024
+
+#define PT_WRITABLE_SHIFT 1
+
+#define PT_PRESENT_MASK (1ULL << 0)
+#define PT_WRITABLE_MASK (1ULL << PT_WRITABLE_SHIFT)
+#define PT_USER_MASK (1ULL << 2)
+#define PT_PWT_MASK (1ULL << 3)
+#define PT_PCD_MASK (1ULL << 4)
+#define PT_ACCESSED_MASK (1ULL << 5)
+#define PT_DIRTY_MASK (1ULL << 6)
+#define PT_PAGE_SIZE_MASK (1ULL << 7)
+#define PT_PAT_MASK (1ULL << 7)
+#define PT_GLOBAL_MASK (1ULL << 8)
+#define PT64_NX_MASK (1ULL << 63)
+
+#define PT_PAT_SHIFT 7
+#define PT_DIR_PAT_SHIFT 12
+#define PT_DIR_PAT_MASK (1ULL << PT_DIR_PAT_SHIFT)
+
+#define PT32_DIR_PSE36_SIZE 4
+#define PT32_DIR_PSE36_SHIFT 13
+#define PT32_DIR_PSE36_MASK (((1ULL << PT32_DIR_PSE36_SIZE) - 1) << PT32_DIR_PSE36_SHIFT)
+
+
+#define PT32_PTE_COPY_MASK \
+ (PT_PRESENT_MASK | PT_ACCESSED_MASK | PT_DIRTY_MASK | PT_GLOBAL_MASK)
+
+#define PT64_PTE_COPY_MASK (PT64_NX_MASK | PT32_PTE_COPY_MASK)
+
+#define PT_FIRST_AVAIL_BITS_SHIFT 9
+#define PT64_SECOND_AVAIL_BITS_SHIFT 52
+
+#define PT_SHADOW_PS_MARK (1ULL << PT_FIRST_AVAIL_BITS_SHIFT)
+#define PT_SHADOW_IO_MARK (1ULL << PT_FIRST_AVAIL_BITS_SHIFT)
+
+#define PT_SHADOW_WRITABLE_SHIFT (PT_FIRST_AVAIL_BITS_SHIFT + 1)
+#define PT_SHADOW_WRITABLE_MASK (1ULL << PT_SHADOW_WRITABLE_SHIFT)
+
+#define PT_SHADOW_USER_SHIFT (PT_SHADOW_WRITABLE_SHIFT + 1)
+#define PT_SHADOW_USER_MASK (1ULL << (PT_SHADOW_USER_SHIFT))
+
+#define PT_SHADOW_BITS_OFFSET (PT_SHADOW_WRITABLE_SHIFT - PT_WRITABLE_SHIFT)
+
+#define VALID_PAGE(x) ((x) != INVALID_PAGE)
+
+#define PT64_LEVEL_BITS 9
+
+#define PT64_LEVEL_SHIFT(level) \
+ ( PAGE_SHIFT + (level - 1) * PT64_LEVEL_BITS )
+
+#define PT64_LEVEL_MASK(level) \
+ (((1ULL << PT64_LEVEL_BITS) - 1) << PT64_LEVEL_SHIFT(level))
+
+#define PT64_INDEX(address, level)\
+ (((address) >> PT64_LEVEL_SHIFT(level)) & ((1 << PT64_LEVEL_BITS) - 1))
+
+
+#define PT32_LEVEL_BITS 10
+
+#define PT32_LEVEL_SHIFT(level) \
+ ( PAGE_SHIFT + (level - 1) * PT32_LEVEL_BITS )
+
+#define PT32_LEVEL_MASK(level) \
+ (((1ULL << PT32_LEVEL_BITS) - 1) << PT32_LEVEL_SHIFT(level))
+
+#define PT32_INDEX(address, level)\
+ (((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1))
+
+
+#define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & PAGE_MASK)
+#define PT64_DIR_BASE_ADDR_MASK \
+ (PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + PT64_LEVEL_BITS)) - 1))
+
+#define PT32_BASE_ADDR_MASK PAGE_MASK
+#define PT32_DIR_BASE_ADDR_MASK \
+ (PAGE_MASK & ~((1ULL << (PAGE_SHIFT + PT32_LEVEL_BITS)) - 1))
+
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+
+#define PT64_ROOT_LEVEL 4
+#define PT32_ROOT_LEVEL 2
+#define PT32E_ROOT_LEVEL 3
+
+#define PT_DIRECTORY_LEVEL 2
+#define PT_PAGE_TABLE_LEVEL 1
+
+static int is_write_protection(struct kvm_vcpu *vcpu)
+{
+ return vcpu->cr0 & CR0_WP_MASK;
+}
+
+static int is_cpuid_PSE36(void)
+{
+ return 1;
+}
+
+static int is_present_pte(unsigned long pte)
+{
+ return pte & PT_PRESENT_MASK;
+}
+
+static int is_writeble_pte(unsigned long pte)
+{
+ return pte & PT_WRITABLE_MASK;
+}
+
+static int is_io_pte(unsigned long pte)
+{
+ return pte & PT_SHADOW_IO_MARK;
+}
+
+static void kvm_mmu_free_page(struct kvm_vcpu *vcpu, hpa_t page_hpa)
+{
+ struct kvm_mmu_page *page_head = page_header(page_hpa);
+
+ list_del(&page_head->link);
+ page_head->page_hpa = page_hpa;
+ list_add(&page_head->link, &vcpu->free_pages);
+}
+
+static int is_empty_shadow_page(hpa_t page_hpa)
+{
+ u32 *pos;
+ u32 *end;
+ for (pos = __va(page_hpa), end = pos + PAGE_SIZE / sizeof(u32);
+ pos != end; pos++)
+ if (*pos != 0)
+ return 0;
+ return 1;
+}
+
+static hpa_t kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, u64 *parent_pte)
+{
+ struct kvm_mmu_page *page;
+
+ if (list_empty(&vcpu->free_pages))
+ return INVALID_PAGE;
+
+ page = list_entry(vcpu->free_pages.next, struct kvm_mmu_page, link);
+ list_del(&page->link);
+ list_add(&page->link, &vcpu->kvm->active_mmu_pages);
+ ASSERT(is_empty_shadow_page(page->page_hpa));
+ page->slot_bitmap = 0;
+ page->global = 1;
+ page->parent_pte = parent_pte;
+ return page->page_hpa;
+}
+
+static void page_header_update_slot(struct kvm *kvm, void *pte, gpa_t gpa)
+{
+ int slot = memslot_id(kvm, gfn_to_memslot(kvm, gpa >> PAGE_SHIFT));
+ struct kvm_mmu_page *page_head = page_header(__pa(pte));
+
+ __set_bit(slot, &page_head->slot_bitmap);
+}
+
+hpa_t safe_gpa_to_hpa(struct kvm_vcpu *vcpu, gpa_t gpa)
+{
+ hpa_t hpa = gpa_to_hpa(vcpu, gpa);
+
+ return is_error_hpa(hpa) ? bad_page_address | (gpa & ~PAGE_MASK): hpa;
+}
+
+hpa_t gpa_to_hpa(struct kvm_vcpu *vcpu, gpa_t gpa)
+{
+ struct kvm_memory_slot *slot;
+ struct page *page;
+
+ ASSERT((gpa & HPA_ERR_MASK) == 0);
+ slot = gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT);
+ if (!slot)
+ return gpa | HPA_ERR_MASK;
+ page = gfn_to_page(slot, gpa >> PAGE_SHIFT);
+ return ((hpa_t)page_to_pfn(page) << PAGE_SHIFT)
+ | (gpa & (PAGE_SIZE-1));
+}
+
+hpa_t gva_to_hpa(struct kvm_vcpu *vcpu, gva_t gva)
+{
+ gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, gva);
+
+ if (gpa == UNMAPPED_GVA)
+ return UNMAPPED_GVA;
+ return gpa_to_hpa(vcpu, gpa);
+}
+
+
+static void release_pt_page_64(struct kvm_vcpu *vcpu, hpa_t page_hpa,
+ int level)
+{
+ ASSERT(vcpu);
+ ASSERT(VALID_PAGE(page_hpa));
+ ASSERT(level <= PT64_ROOT_LEVEL && level > 0);
+
+ if (level == 1)
+ memset(__va(page_hpa), 0, PAGE_SIZE);
+ else {
+ u64 *pos;
+ u64 *end;
+
+ for (pos = __va(page_hpa), end = pos + PT64_ENT_PER_PAGE;
+ pos != end; pos++) {
+ u64 current_ent = *pos;
+
+ *pos = 0;
+ if (is_present_pte(current_ent))
+ release_pt_page_64(vcpu,
+ current_ent &
+ PT64_BASE_ADDR_MASK,
+ level - 1);
+ }
+ }
+ kvm_mmu_free_page(vcpu, page_hpa);
+}
+
+static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
+{
+}
+
+static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, hpa_t p)
+{
+ int level = PT32E_ROOT_LEVEL;
+ hpa_t table_addr = vcpu->mmu.root_hpa;
+
+ for (; ; level--) {
+ u32 index = PT64_INDEX(v, level);
+ u64 *table;
+
+ ASSERT(VALID_PAGE(table_addr));
+ table = __va(table_addr);
+
+ if (level == 1) {
+ mark_page_dirty(vcpu->kvm, v >> PAGE_SHIFT);
+ page_header_update_slot(vcpu->kvm, table, v);
+ table[index] = p | PT_PRESENT_MASK | PT_WRITABLE_MASK |
+ PT_USER_MASK;
+ return 0;
+ }
+
+ if (table[index] == 0) {
+ hpa_t new_table = kvm_mmu_alloc_page(vcpu,
+ &table[index]);
+
+ if (!VALID_PAGE(new_table)) {
+ pgprintk("nonpaging_map: ENOMEM\n");
+ return -ENOMEM;
+ }
+
+ if (level == PT32E_ROOT_LEVEL)
+ table[index] = new_table | PT_PRESENT_MASK;
+ else
+ table[index] = new_table | PT_PRESENT_MASK |
+ PT_WRITABLE_MASK | PT_USER_MASK;
+ }
+ table_addr = table[index] & PT64_BASE_ADDR_MASK;
+ }
+}
+
+static void nonpaging_flush(struct kvm_vcpu *vcpu)
+{
+ hpa_t root = vcpu->mmu.root_hpa;
+
+ ++kvm_stat.tlb_flush;
+ pgprintk("nonpaging_flush\n");
+ ASSERT(VALID_PAGE(root));
+ release_pt_page_64(vcpu, root, vcpu->mmu.shadow_root_level);
+ root = kvm_mmu_alloc_page(vcpu, NULL);
+ ASSERT(VALID_PAGE(root));
+ vcpu->mmu.root_hpa = root;
+ if (is_paging(vcpu))
+ root |= (vcpu->cr3 & (CR3_PCD_MASK | CR3_WPT_MASK));
+ kvm_arch_ops->set_cr3(vcpu, root);
+ kvm_arch_ops->tlb_flush(vcpu);
+}
+
+static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr)
+{
+ return vaddr;
+}
+
+static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva,
+ u32 error_code)
+{
+ int ret;
+ gpa_t addr = gva;
+
+ ASSERT(vcpu);
+ ASSERT(VALID_PAGE(vcpu->mmu.root_hpa));
+
+ for (;;) {
+ hpa_t paddr;
+
+ paddr = gpa_to_hpa(vcpu , addr & PT64_BASE_ADDR_MASK);
+
+ if (is_error_hpa(paddr))
+ return 1;
+
+ ret = nonpaging_map(vcpu, addr & PAGE_MASK, paddr);
+ if (ret) {
+ nonpaging_flush(vcpu);
+ continue;
+ }
+ break;
+ }
+ return ret;
+}
+
+static void nonpaging_inval_page(struct kvm_vcpu *vcpu, gva_t addr)
+{
+}
+
+static void nonpaging_free(struct kvm_vcpu *vcpu)
+{
+ hpa_t root;
+
+ ASSERT(vcpu);
+ root = vcpu->mmu.root_hpa;
+ if (VALID_PAGE(root))
+ release_pt_page_64(vcpu, root, vcpu->mmu.shadow_root_level);
+ vcpu->mmu.root_hpa = INVALID_PAGE;
+}
+
+static int nonpaging_init_context(struct kvm_vcpu *vcpu)
+{
+ struct kvm_mmu *context = &vcpu->mmu;
+
+ context->new_cr3 = nonpaging_new_cr3;
+ context->page_fault = nonpaging_page_fault;
+ context->inval_page = nonpaging_inval_page;
+ context->gva_to_gpa = nonpaging_gva_to_gpa;
+ context->free = nonpaging_free;
+ context->root_level = PT32E_ROOT_LEVEL;
+ context->shadow_root_level = PT32E_ROOT_LEVEL;
+ context->root_hpa = kvm_mmu_alloc_page(vcpu, NULL);
+ ASSERT(VALID_PAGE(context->root_hpa));
+ kvm_arch_ops->set_cr3(vcpu, context->root_hpa);
+ return 0;
+}
+
+
+static void kvm_mmu_flush_tlb(struct kvm_vcpu *vcpu)
+{
+ struct kvm_mmu_page *page, *npage;
+
+ list_for_each_entry_safe(page, npage, &vcpu->kvm->active_mmu_pages,
+ link) {
+ if (page->global)
+ continue;
+
+ if (!page->parent_pte)
+ continue;
+
+ *page->parent_pte = 0;
+ release_pt_page_64(vcpu, page->page_hpa, 1);
+ }
+ ++kvm_stat.tlb_flush;
+ kvm_arch_ops->tlb_flush(vcpu);
+}
+
+static void paging_new_cr3(struct kvm_vcpu *vcpu)
+{
+ kvm_mmu_flush_tlb(vcpu);
+}
+
+static void mark_pagetable_nonglobal(void *shadow_pte)
+{
+ page_header(__pa(shadow_pte))->global = 0;
+}
+
+static inline void set_pte_common(struct kvm_vcpu *vcpu,
+ u64 *shadow_pte,
+ gpa_t gaddr,
+ int dirty,
+ u64 access_bits)
+{
+ hpa_t paddr;
+
+ *shadow_pte |= access_bits << PT_SHADOW_BITS_OFFSET;
+ if (!dirty)
+ access_bits &= ~PT_WRITABLE_MASK;
+
+ if (access_bits & PT_WRITABLE_MASK)
+ mark_page_dirty(vcpu->kvm, gaddr >> PAGE_SHIFT);
+
+ *shadow_pte |= access_bits;
+
+ paddr = gpa_to_hpa(vcpu, gaddr & PT64_BASE_ADDR_MASK);
+
+ if (!(*shadow_pte & PT_GLOBAL_MASK))
+ mark_pagetable_nonglobal(shadow_pte);
+
+ if (is_error_hpa(paddr)) {
+ *shadow_pte |= gaddr;
+ *shadow_pte |= PT_SHADOW_IO_MARK;
+ *shadow_pte &= ~PT_PRESENT_MASK;
+ } else {
+ *shadow_pte |= paddr;
+ page_header_update_slot(vcpu->kvm, shadow_pte, gaddr);
+ }
+}
+
+static void inject_page_fault(struct kvm_vcpu *vcpu,
+ u64 addr,
+ u32 err_code)
+{
+ kvm_arch_ops->inject_page_fault(vcpu, addr, err_code);
+}
+
+static inline int fix_read_pf(u64 *shadow_ent)
+{
+ if ((*shadow_ent & PT_SHADOW_USER_MASK) &&
+ !(*shadow_ent & PT_USER_MASK)) {
+ /*
+ * If supervisor write protect is disabled, we shadow kernel
+ * pages as user pages so we can trap the write access.
+ */
+ *shadow_ent |= PT_USER_MASK;
+ *shadow_ent &= ~PT_WRITABLE_MASK;
+
+ return 1;
+
+ }
+ return 0;
+}
+
+static int may_access(u64 pte, int write, int user)
+{
+
+ if (user && !(pte & PT_USER_MASK))
+ return 0;
+ if (write && !(pte & PT_WRITABLE_MASK))
+ return 0;
+ return 1;
+}
+
+/*
+ * Remove a shadow pte.
+ */
+static void paging_inval_page(struct kvm_vcpu *vcpu, gva_t addr)
+{
+ hpa_t page_addr = vcpu->mmu.root_hpa;
+ int level = vcpu->mmu.shadow_root_level;
+
+ ++kvm_stat.invlpg;
+
+ for (; ; level--) {
+ u32 index = PT64_INDEX(addr, level);
+ u64 *table = __va(page_addr);
+
+ if (level == PT_PAGE_TABLE_LEVEL ) {
+ table[index] = 0;
+ return;
+ }
+
+ if (!is_present_pte(table[index]))
+ return;
+
+ page_addr = table[index] & PT64_BASE_ADDR_MASK;
+
+ if (level == PT_DIRECTORY_LEVEL &&
+ (table[index] & PT_SHADOW_PS_MARK)) {
+ table[index] = 0;
+ release_pt_page_64(vcpu, page_addr, PT_PAGE_TABLE_LEVEL);
+
+ kvm_arch_ops->tlb_flush(vcpu);
+ return;
+ }
+ }
+}
+
+static void paging_free(struct kvm_vcpu *vcpu)
+{
+ nonpaging_free(vcpu);
+}
+
+#define PTTYPE 64
+#include "paging_tmpl.h"
+#undef PTTYPE
+
+#define PTTYPE 32
+#include "paging_tmpl.h"
+#undef PTTYPE
+
+static int paging64_init_context(struct kvm_vcpu *vcpu)
+{
+ struct kvm_mmu *context = &vcpu->mmu;
+
+ ASSERT(is_pae(vcpu));
+ context->new_cr3 = paging_new_cr3;
+ context->page_fault = paging64_page_fault;
+ context->inval_page = paging_inval_page;
+ context->gva_to_gpa = paging64_gva_to_gpa;
+ context->free = paging_free;
+ context->root_level = PT64_ROOT_LEVEL;
+ context->shadow_root_level = PT64_ROOT_LEVEL;
+ context->root_hpa = kvm_mmu_alloc_page(vcpu, NULL);
+ ASSERT(VALID_PAGE(context->root_hpa));
+ kvm_arch_ops->set_cr3(vcpu, context->root_hpa |
+ (vcpu->cr3 & (CR3_PCD_MASK | CR3_WPT_MASK)));
+ return 0;
+}
+
+static int paging32_init_context(struct kvm_vcpu *vcpu)
+{
+ struct kvm_mmu *context = &vcpu->mmu;
+
+ context->new_cr3 = paging_new_cr3;
+ context->page_fault = paging32_page_fault;
+ context->inval_page = paging_inval_page;
+ context->gva_to_gpa = paging32_gva_to_gpa;
+ context->free = paging_free;
+ context->root_level = PT32_ROOT_LEVEL;
+ context->shadow_root_level = PT32E_ROOT_LEVEL;
+ context->root_hpa = kvm_mmu_alloc_page(vcpu, NULL);
+ ASSERT(VALID_PAGE(context->root_hpa));
+ kvm_arch_ops->set_cr3(vcpu, context->root_hpa |
+ (vcpu->cr3 & (CR3_PCD_MASK | CR3_WPT_MASK)));
+ return 0;
+}
+
+static int paging32E_init_context(struct kvm_vcpu *vcpu)
+{
+ int ret;
+
+ if ((ret = paging64_init_context(vcpu)))
+ return ret;
+
+ vcpu->mmu.root_level = PT32E_ROOT_LEVEL;
+ vcpu->mmu.shadow_root_level = PT32E_ROOT_LEVEL;
+ return 0;
+}
+
+static int init_kvm_mmu(struct kvm_vcpu *vcpu)
+{
+ ASSERT(vcpu);
+ ASSERT(!VALID_PAGE(vcpu->mmu.root_hpa));
+
+ if (!is_paging(vcpu))
+ return nonpaging_init_context(vcpu);
+ else if (kvm_arch_ops->is_long_mode(vcpu))
+ return paging64_init_context(vcpu);
+ else if (is_pae(vcpu))
+ return paging32E_init_context(vcpu);
+ else
+ return paging32_init_context(vcpu);
+}
+
+static void destroy_kvm_mmu(struct kvm_vcpu *vcpu)
+{
+ ASSERT(vcpu);
+ if (VALID_PAGE(vcpu->mmu.root_hpa)) {
+ vcpu->mmu.free(vcpu);
+ vcpu->mmu.root_hpa = INVALID_PAGE;
+ }
+}
+
+int kvm_mmu_reset_context(struct kvm_vcpu *vcpu)
+{
+ destroy_kvm_mmu(vcpu);
+ return init_kvm_mmu(vcpu);
+}
+
+static void free_mmu_pages(struct kvm_vcpu *vcpu)
+{
+ while (!list_empty(&vcpu->free_pages)) {
+ struct kvm_mmu_page *page;
+
+ page = list_entry(vcpu->free_pages.next,
+ struct kvm_mmu_page, link);
+ list_del(&page->link);
+ __free_page(pfn_to_page(page->page_hpa >> PAGE_SHIFT));
+ page->page_hpa = INVALID_PAGE;
+ }
+}
+
+static int alloc_mmu_pages(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ ASSERT(vcpu);
+
+ for (i = 0; i < KVM_NUM_MMU_PAGES; i++) {
+ struct page *page;
+ struct kvm_mmu_page *page_header = &vcpu->page_header_buf[i];
+
+ INIT_LIST_HEAD(&page_header->link);
+ if ((page = alloc_page(GFP_KVM_MMU)) == NULL)
+ goto error_1;
+ page->private = (unsigned long)page_header;
+ page_header->page_hpa = (hpa_t)page_to_pfn(page) << PAGE_SHIFT;
+ memset(__va(page_header->page_hpa), 0, PAGE_SIZE);
+ list_add(&page_header->link, &vcpu->free_pages);
+ }
+ return 0;
+
+error_1:
+ free_mmu_pages(vcpu);
+ return -ENOMEM;
+}
+
+int kvm_mmu_init(struct kvm_vcpu *vcpu)
+{
+ int r;
+
+ ASSERT(vcpu);
+ ASSERT(!VALID_PAGE(vcpu->mmu.root_hpa));
+ ASSERT(list_empty(&vcpu->free_pages));
+
+ if ((r = alloc_mmu_pages(vcpu)))
+ return r;
+
+ if ((r = init_kvm_mmu(vcpu))) {
+ free_mmu_pages(vcpu);
+ return r;
+ }
+ return 0;
+}
+
+void kvm_mmu_destroy(struct kvm_vcpu *vcpu)
+{
+ ASSERT(vcpu);
+
+ destroy_kvm_mmu(vcpu);
+ free_mmu_pages(vcpu);
+}
+
+void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot)
+{
+ struct kvm_mmu_page *page;
+
+ list_for_each_entry(page, &kvm->active_mmu_pages, link) {
+ int i;
+ u64 *pt;
+
+ if (!test_bit(slot, &page->slot_bitmap))
+ continue;
+
+ pt = __va(page->page_hpa);
+ for (i = 0; i < PT64_ENT_PER_PAGE; ++i)
+ /* avoid RMW */
+ if (pt[i] & PT_WRITABLE_MASK)
+ pt[i] &= ~PT_WRITABLE_MASK;
+
+ }
+}
diff --git a/drivers/kvm/paging_tmpl.h b/drivers/kvm/paging_tmpl.h
new file mode 100644
index 0000000..a9771b4
--- /dev/null
+++ b/drivers/kvm/paging_tmpl.h
@@ -0,0 +1,391 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables machines with Intel VT-x extensions to run virtual
+ * machines without emulation or binary translation.
+ *
+ * MMU support
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Yaniv Kamay <yaniv@qumranet.com>
+ * Avi Kivity <avi@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+/*
+ * We need the mmu code to access both 32-bit and 64-bit guest ptes,
+ * so the code in this file is compiled twice, once per pte size.
+ */
+
+#if PTTYPE == 64
+ #define pt_element_t u64
+ #define guest_walker guest_walker64
+ #define FNAME(name) paging##64_##name
+ #define PT_BASE_ADDR_MASK PT64_BASE_ADDR_MASK
+ #define PT_DIR_BASE_ADDR_MASK PT64_DIR_BASE_ADDR_MASK
+ #define PT_INDEX(addr, level) PT64_INDEX(addr, level)
+ #define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
+ #define PT_LEVEL_MASK(level) PT64_LEVEL_MASK(level)
+ #define PT_PTE_COPY_MASK PT64_PTE_COPY_MASK
+#elif PTTYPE == 32
+ #define pt_element_t u32
+ #define guest_walker guest_walker32
+ #define FNAME(name) paging##32_##name
+ #define PT_BASE_ADDR_MASK PT32_BASE_ADDR_MASK
+ #define PT_DIR_BASE_ADDR_MASK PT32_DIR_BASE_ADDR_MASK
+ #define PT_INDEX(addr, level) PT32_INDEX(addr, level)
+ #define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
+ #define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level)
+ #define PT_PTE_COPY_MASK PT32_PTE_COPY_MASK
+#else
+ #error Invalid PTTYPE value
+#endif
+
+/*
+ * The guest_walker structure emulates the behavior of the hardware page
+ * table walker.
+ */
+struct guest_walker {
+ int level;
+ pt_element_t *table;
+ pt_element_t inherited_ar;
+};
+
+static void FNAME(init_walker)(struct guest_walker *walker,
+ struct kvm_vcpu *vcpu)
+{
+ hpa_t hpa;
+ struct kvm_memory_slot *slot;
+
+ walker->level = vcpu->mmu.root_level;
+ slot = gfn_to_memslot(vcpu->kvm,
+ (vcpu->cr3 & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT);
+ hpa = safe_gpa_to_hpa(vcpu, vcpu->cr3 & PT64_BASE_ADDR_MASK);
+ walker->table = kmap_atomic(pfn_to_page(hpa >> PAGE_SHIFT), KM_USER0);
+
+ ASSERT((!kvm_arch_ops->is_long_mode(vcpu) && is_pae(vcpu)) ||
+ (vcpu->cr3 & ~(PAGE_MASK | CR3_FLAGS_MASK)) == 0);
+
+ walker->table = (pt_element_t *)( (unsigned long)walker->table |
+ (unsigned long)(vcpu->cr3 & ~(PAGE_MASK | CR3_FLAGS_MASK)) );
+ walker->inherited_ar = PT_USER_MASK | PT_WRITABLE_MASK;
+}
+
+static void FNAME(release_walker)(struct guest_walker *walker)
+{
+ kunmap_atomic(walker->table, KM_USER0);
+}
+
+static void FNAME(set_pte)(struct kvm_vcpu *vcpu, u64 guest_pte,
+ u64 *shadow_pte, u64 access_bits)
+{
+ ASSERT(*shadow_pte == 0);
+ access_bits &= guest_pte;
+ *shadow_pte = (guest_pte & PT_PTE_COPY_MASK);
+ set_pte_common(vcpu, shadow_pte, guest_pte & PT_BASE_ADDR_MASK,
+ guest_pte & PT_DIRTY_MASK, access_bits);
+}
+
+static void FNAME(set_pde)(struct kvm_vcpu *vcpu, u64 guest_pde,
+ u64 *shadow_pte, u64 access_bits,
+ int index)
+{
+ gpa_t gaddr;
+
+ ASSERT(*shadow_pte == 0);
+ access_bits &= guest_pde;
+ gaddr = (guest_pde & PT_DIR_BASE_ADDR_MASK) + PAGE_SIZE * index;
+ if (PTTYPE == 32 && is_cpuid_PSE36())
+ gaddr |= (guest_pde & PT32_DIR_PSE36_MASK) <<
+ (32 - PT32_DIR_PSE36_SHIFT);
+ *shadow_pte = guest_pde & PT_PTE_COPY_MASK;
+ set_pte_common(vcpu, shadow_pte, gaddr,
+ guest_pde & PT_DIRTY_MASK, access_bits);
+}
+
+/*
+ * Fetch a guest pte from a specific level in the paging hierarchy.
+ */
+static pt_element_t *FNAME(fetch_guest)(struct kvm_vcpu *vcpu,
+ struct guest_walker *walker,
+ int level,
+ gva_t addr)
+{
+
+ ASSERT(level > 0 && level <= walker->level);
+
+ for (;;) {
+ int index = PT_INDEX(addr, walker->level);
+ hpa_t paddr;
+
+ ASSERT(((unsigned long)walker->table & PAGE_MASK) ==
+ ((unsigned long)&walker->table[index] & PAGE_MASK));
+ if (level == walker->level ||
+ !is_present_pte(walker->table[index]) ||
+ (walker->level == PT_DIRECTORY_LEVEL &&
+ (walker->table[index] & PT_PAGE_SIZE_MASK) &&
+ (PTTYPE == 64 || is_pse(vcpu))))
+ return &walker->table[index];
+ if (walker->level != 3 || kvm_arch_ops->is_long_mode(vcpu))
+ walker->inherited_ar &= walker->table[index];
+ paddr = safe_gpa_to_hpa(vcpu, walker->table[index] & PT_BASE_ADDR_MASK);
+ kunmap_atomic(walker->table, KM_USER0);
+ walker->table = kmap_atomic(pfn_to_page(paddr >> PAGE_SHIFT),
+ KM_USER0);
+ --walker->level;
+ }
+}
+
+/*
+ * Fetch a shadow pte for a specific level in the paging hierarchy.
+ */
+static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
+ struct guest_walker *walker)
+{
+ hpa_t shadow_addr;
+ int level;
+ u64 *prev_shadow_ent = NULL;
+
+ shadow_addr = vcpu->mmu.root_hpa;
+ level = vcpu->mmu.shadow_root_level;
+
+ for (; ; level--) {
+ u32 index = SHADOW_PT_INDEX(addr, level);
+ u64 *shadow_ent = ((u64 *)__va(shadow_addr)) + index;
+ pt_element_t *guest_ent;
+ u64 shadow_pte;
+
+ if (is_present_pte(*shadow_ent) || is_io_pte(*shadow_ent)) {
+ if (level == PT_PAGE_TABLE_LEVEL)
+ return shadow_ent;
+ shadow_addr = *shadow_ent & PT64_BASE_ADDR_MASK;
+ prev_shadow_ent = shadow_ent;
+ continue;
+ }
+
+ if (PTTYPE == 32 && level > PT32_ROOT_LEVEL) {
+ ASSERT(level == PT32E_ROOT_LEVEL);
+ guest_ent = FNAME(fetch_guest)(vcpu, walker,
+ PT32_ROOT_LEVEL, addr);
+ } else
+ guest_ent = FNAME(fetch_guest)(vcpu, walker,
+ level, addr);
+
+ if (!is_present_pte(*guest_ent))
+ return NULL;
+
+ /* Don't set accessed bit on PAE PDPTRs */
+ if (vcpu->mmu.root_level != 3 || walker->level != 3)
+ *guest_ent |= PT_ACCESSED_MASK;
+
+ if (level == PT_PAGE_TABLE_LEVEL) {
+
+ if (walker->level == PT_DIRECTORY_LEVEL) {
+ if (prev_shadow_ent)
+ *prev_shadow_ent |= PT_SHADOW_PS_MARK;
+ FNAME(set_pde)(vcpu, *guest_ent, shadow_ent,
+ walker->inherited_ar,
+ PT_INDEX(addr, PT_PAGE_TABLE_LEVEL));
+ } else {
+ ASSERT(walker->level == PT_PAGE_TABLE_LEVEL);
+ FNAME(set_pte)(vcpu, *guest_ent, shadow_ent, walker->inherited_ar);
+ }
+ return shadow_ent;
+ }
+
+ shadow_addr = kvm_mmu_alloc_page(vcpu, shadow_ent);
+ if (!VALID_PAGE(shadow_addr))
+ return ERR_PTR(-ENOMEM);
+ shadow_pte = shadow_addr | PT_PRESENT_MASK;
+ if (vcpu->mmu.root_level > 3 || level != 3)
+ shadow_pte |= PT_ACCESSED_MASK
+ | PT_WRITABLE_MASK | PT_USER_MASK;
+ *shadow_ent = shadow_pte;
+ prev_shadow_ent = shadow_ent;
+ }
+}
+
+/*
+ * The guest faulted for write. We need to
+ *
+ * - check write permissions
+ * - update the guest pte dirty bit
+ * - update our own dirty page tracking structures
+ */
+static int FNAME(fix_write_pf)(struct kvm_vcpu *vcpu,
+ u64 *shadow_ent,
+ struct guest_walker *walker,
+ gva_t addr,
+ int user)
+{
+ pt_element_t *guest_ent;
+ int writable_shadow;
+ gfn_t gfn;
+
+ if (is_writeble_pte(*shadow_ent))
+ return 0;
+
+ writable_shadow = *shadow_ent & PT_SHADOW_WRITABLE_MASK;
+ if (user) {
+ /*
+ * User mode access. Fail if it's a kernel page or a read-only
+ * page.
+ */
+ if (!(*shadow_ent & PT_SHADOW_USER_MASK) || !writable_shadow)
+ return 0;
+ ASSERT(*shadow_ent & PT_USER_MASK);
+ } else
+ /*
+ * Kernel mode access. Fail if it's a read-only page and
+ * supervisor write protection is enabled.
+ */
+ if (!writable_shadow) {
+ if (is_write_protection(vcpu))
+ return 0;
+ *shadow_ent &= ~PT_USER_MASK;
+ }
+
+ guest_ent = FNAME(fetch_guest)(vcpu, walker, PT_PAGE_TABLE_LEVEL, addr);
+
+ if (!is_present_pte(*guest_ent)) {
+ *shadow_ent = 0;
+ return 0;
+ }
+
+ gfn = (*guest_ent & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
+ mark_page_dirty(vcpu->kvm, gfn);
+ *shadow_ent |= PT_WRITABLE_MASK;
+ *guest_ent |= PT_DIRTY_MASK;
+
+ return 1;
+}
+
+/*
+ * Page fault handler. There are several causes for a page fault:
+ * - there is no shadow pte for the guest pte
+ * - write access through a shadow pte marked read only so that we can set
+ * the dirty bit
+ * - write access to a shadow pte marked read only so we can update the page
+ * dirty bitmap, when userspace requests it
+ * - mmio access; in this case we will never install a present shadow pte
+ * - normal guest page fault due to the guest pte marked not present, not
+ * writable, or not executable
+ *
+ * Returns: 1 if we need to emulate the instruction, 0 otherwise
+ */
+static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
+ u32 error_code)
+{
+ int write_fault = error_code & PFERR_WRITE_MASK;
+ int pte_present = error_code & PFERR_PRESENT_MASK;
+ int user_fault = error_code & PFERR_USER_MASK;
+ struct guest_walker walker;
+ u64 *shadow_pte;
+ int fixed;
+
+ /*
+ * Look up the shadow pte for the faulting address.
+ */
+ for (;;) {
+ FNAME(init_walker)(&walker, vcpu);
+ shadow_pte = FNAME(fetch)(vcpu, addr, &walker);
+ if (IS_ERR(shadow_pte)) { /* must be -ENOMEM */
+ nonpaging_flush(vcpu);
+ FNAME(release_walker)(&walker);
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * The page is not mapped by the guest. Let the guest handle it.
+ */
+ if (!shadow_pte) {
+ inject_page_fault(vcpu, addr, error_code);
+ FNAME(release_walker)(&walker);
+ return 0;
+ }
+
+ /*
+ * Update the shadow pte.
+ */
+ if (write_fault)
+ fixed = FNAME(fix_write_pf)(vcpu, shadow_pte, &walker, addr,
+ user_fault);
+ else
+ fixed = fix_read_pf(shadow_pte);
+
+ FNAME(release_walker)(&walker);
+
+ /*
+ * mmio: emulate if accessible, otherwise its a guest fault.
+ */
+ if (is_io_pte(*shadow_pte)) {
+ if (may_access(*shadow_pte, write_fault, user_fault))
+ return 1;
+ pgprintk("%s: io work, no access\n", __FUNCTION__);
+ inject_page_fault(vcpu, addr,
+ error_code | PFERR_PRESENT_MASK);
+ return 0;
+ }
+
+ /*
+ * pte not present, guest page fault.
+ */
+ if (pte_present && !fixed) {
+ inject_page_fault(vcpu, addr, error_code);
+ return 0;
+ }
+
+ ++kvm_stat.pf_fixed;
+
+ return 0;
+}
+
+static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr)
+{
+ struct guest_walker walker;
+ pt_element_t guest_pte;
+ gpa_t gpa;
+
+ FNAME(init_walker)(&walker, vcpu);
+ guest_pte = *FNAME(fetch_guest)(vcpu, &walker, PT_PAGE_TABLE_LEVEL,
+ vaddr);
+ FNAME(release_walker)(&walker);
+
+ if (!is_present_pte(guest_pte))
+ return UNMAPPED_GVA;
+
+ if (walker.level == PT_DIRECTORY_LEVEL) {
+ ASSERT((guest_pte & PT_PAGE_SIZE_MASK));
+ ASSERT(PTTYPE == 64 || is_pse(vcpu));
+
+ gpa = (guest_pte & PT_DIR_BASE_ADDR_MASK) | (vaddr &
+ (PT_LEVEL_MASK(PT_PAGE_TABLE_LEVEL) | ~PAGE_MASK));
+
+ if (PTTYPE == 32 && is_cpuid_PSE36())
+ gpa |= (guest_pte & PT32_DIR_PSE36_MASK) <<
+ (32 - PT32_DIR_PSE36_SHIFT);
+ } else {
+ gpa = (guest_pte & PT_BASE_ADDR_MASK);
+ gpa |= (vaddr & ~PAGE_MASK);
+ }
+
+ return gpa;
+}
+
+#undef pt_element_t
+#undef guest_walker
+#undef FNAME
+#undef PT_BASE_ADDR_MASK
+#undef PT_INDEX
+#undef SHADOW_PT_INDEX
+#undef PT_LEVEL_MASK
+#undef PT_PTE_COPY_MASK
+#undef PT_NON_PTE_COPY_MASK
+#undef PT_DIR_BASE_ADDR_MASK
diff --git a/drivers/kvm/segment_descriptor.h b/drivers/kvm/segment_descriptor.h
new file mode 100644
index 0000000..71fdf45
--- /dev/null
+++ b/drivers/kvm/segment_descriptor.h
@@ -0,0 +1,17 @@
+struct segment_descriptor {
+ u16 limit_low;
+ u16 base_low;
+ u8 base_mid;
+ u8 type : 4;
+ u8 system : 1;
+ u8 dpl : 2;
+ u8 present : 1;
+ u8 limit_high : 4;
+ u8 avl : 1;
+ u8 long_mode : 1;
+ u8 default_op : 1;
+ u8 granularity : 1;
+ u8 base_high;
+} __attribute__((packed));
+
+
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c
new file mode 100644
index 0000000..0e6bc8c
--- /dev/null
+++ b/drivers/kvm/svm.c
@@ -0,0 +1,1641 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * AMD SVM support
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Yaniv Kamay <yaniv@qumranet.com>
+ * Avi Kivity <avi@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <asm/desc.h>
+
+#include "kvm_svm.h"
+#include "x86_emulate.h"
+
+MODULE_AUTHOR("Qumranet");
+MODULE_LICENSE("GPL");
+
+#define IOPM_ALLOC_ORDER 2
+#define MSRPM_ALLOC_ORDER 1
+
+#define DB_VECTOR 1
+#define UD_VECTOR 6
+#define GP_VECTOR 13
+
+#define DR7_GD_MASK (1 << 13)
+#define DR6_BD_MASK (1 << 13)
+#define CR4_DE_MASK (1UL << 3)
+
+#define SEG_TYPE_LDT 2
+#define SEG_TYPE_BUSY_TSS16 3
+
+#define KVM_EFER_LMA (1 << 10)
+#define KVM_EFER_LME (1 << 8)
+
+unsigned long iopm_base;
+unsigned long msrpm_base;
+
+struct kvm_ldttss_desc {
+ u16 limit0;
+ u16 base0;
+ unsigned base1 : 8, type : 5, dpl : 2, p : 1;
+ unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8;
+ u32 base3;
+ u32 zero1;
+} __attribute__((packed));
+
+struct svm_cpu_data {
+ int cpu;
+
+ uint64_t asid_generation;
+ uint32_t max_asid;
+ uint32_t next_asid;
+ struct kvm_ldttss_desc *tss_desc;
+
+ struct page *save_area;
+};
+
+static DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);
+
+struct svm_init_data {
+ int cpu;
+ int r;
+};
+
+static u32 msrpm_ranges[] = {0, 0xc0000000, 0xc0010000};
+
+#define NUM_MSR_MAPS (sizeof(msrpm_ranges) / sizeof(*msrpm_ranges))
+#define MSRS_RANGE_SIZE 2048
+#define MSRS_IN_RANGE (MSRS_RANGE_SIZE * 8 / 2)
+
+#define MAX_INST_SIZE 15
+
+static unsigned get_addr_size(struct kvm_vcpu *vcpu)
+{
+ struct vmcb_save_area *sa = &vcpu->svm->vmcb->save;
+ u16 cs_attrib;
+
+ if (!(sa->cr0 & CR0_PE_MASK) || (sa->rflags & X86_EFLAGS_VM))
+ return 2;
+
+ cs_attrib = sa->cs.attrib;
+
+ return (cs_attrib & SVM_SELECTOR_L_MASK) ? 8 :
+ (cs_attrib & SVM_SELECTOR_DB_MASK) ? 4 : 2;
+}
+
+static inline u8 pop_irq(struct kvm_vcpu *vcpu)
+{
+ int word_index = __ffs(vcpu->irq_summary);
+ int bit_index = __ffs(vcpu->irq_pending[word_index]);
+ int irq = word_index * BITS_PER_LONG + bit_index;
+
+ clear_bit(bit_index, &vcpu->irq_pending[word_index]);
+ if (!vcpu->irq_pending[word_index])
+ clear_bit(word_index, &vcpu->irq_summary);
+ return irq;
+}
+
+static inline void push_irq(struct kvm_vcpu *vcpu, u8 irq)
+{
+ set_bit(irq, vcpu->irq_pending);
+ set_bit(irq / BITS_PER_LONG, &vcpu->irq_summary);
+}
+
+static inline void clgi(void)
+{
+ asm volatile (SVM_CLGI);
+}
+
+static inline void stgi(void)
+{
+ asm volatile (SVM_STGI);
+}
+
+static inline void invlpga(unsigned long addr, u32 asid)
+{
+ asm volatile (SVM_INVLPGA :: "a"(addr), "c"(asid));
+}
+
+static inline unsigned long kvm_read_cr2(void)
+{
+ unsigned long cr2;
+
+ asm volatile ("mov %%cr2, %0" : "=r" (cr2));
+ return cr2;
+}
+
+static inline void kvm_write_cr2(unsigned long val)
+{
+ asm volatile ("mov %0, %%cr2" :: "r" (val));
+}
+
+static inline unsigned long read_dr6(void)
+{
+ unsigned long dr6;
+
+ asm volatile ("mov %%dr6, %0" : "=r" (dr6));
+ return dr6;
+}
+
+static inline void write_dr6(unsigned long val)
+{
+ asm volatile ("mov %0, %%dr6" :: "r" (val));
+}
+
+static inline unsigned long read_dr7(void)
+{
+ unsigned long dr7;
+
+ asm volatile ("mov %%dr7, %0" : "=r" (dr7));
+ return dr7;
+}
+
+static inline void write_dr7(unsigned long val)
+{
+ asm volatile ("mov %0, %%dr7" :: "r" (val));
+}
+
+static inline int svm_is_long_mode(struct kvm_vcpu *vcpu)
+{
+ return vcpu->svm->vmcb->save.efer & KVM_EFER_LMA;
+}
+
+static inline void force_new_asid(struct kvm_vcpu *vcpu)
+{
+ vcpu->svm->asid_generation--;
+}
+
+static inline void flush_guest_tlb(struct kvm_vcpu *vcpu)
+{
+ force_new_asid(vcpu);
+}
+
+static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
+{
+ if (!(efer & KVM_EFER_LMA))
+ efer &= ~KVM_EFER_LME;
+
+ vcpu->svm->vmcb->save.efer = efer | MSR_EFER_SVME_MASK;
+ vcpu->shadow_efer = efer;
+}
+
+static void svm_inject_gp(struct kvm_vcpu *vcpu, unsigned error_code)
+{
+ vcpu->svm->vmcb->control.event_inj = SVM_EVTINJ_VALID |
+ SVM_EVTINJ_VALID_ERR |
+ SVM_EVTINJ_TYPE_EXEPT |
+ GP_VECTOR;
+ vcpu->svm->vmcb->control.event_inj_err = error_code;
+}
+
+static void inject_ud(struct kvm_vcpu *vcpu)
+{
+ vcpu->svm->vmcb->control.event_inj = SVM_EVTINJ_VALID |
+ SVM_EVTINJ_TYPE_EXEPT |
+ UD_VECTOR;
+}
+
+static void inject_db(struct kvm_vcpu *vcpu)
+{
+ vcpu->svm->vmcb->control.event_inj = SVM_EVTINJ_VALID |
+ SVM_EVTINJ_TYPE_EXEPT |
+ DB_VECTOR;
+}
+
+static int is_page_fault(uint32_t info)
+{
+ info &= SVM_EVTINJ_VEC_MASK | SVM_EVTINJ_TYPE_MASK | SVM_EVTINJ_VALID;
+ return info == (PF_VECTOR | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_EXEPT);
+}
+
+static int is_external_interrupt(u32 info)
+{
+ info &= SVM_EVTINJ_TYPE_MASK | SVM_EVTINJ_VALID;
+ return info == (SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR);
+}
+
+static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
+{
+ if (!vcpu->svm->next_rip) {
+ printk(KERN_DEBUG "%s: NOP\n", __FUNCTION__);
+ return;
+ }
+ if (vcpu->svm->next_rip - vcpu->svm->vmcb->save.rip > 15) {
+ printk(KERN_ERR "%s: ip 0x%llx next 0x%llx\n",
+ __FUNCTION__,
+ vcpu->svm->vmcb->save.rip,
+ vcpu->svm->next_rip);
+ }
+
+ vcpu->rip = vcpu->svm->vmcb->save.rip = vcpu->svm->next_rip;
+ vcpu->svm->vmcb->control.int_state &= ~SVM_INTERRUPT_SHADOW_MASK;
+}
+
+static int has_svm(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ if (current_cpu_data.x86_vendor != X86_VENDOR_AMD) {
+ printk(KERN_INFO "has_svm: not amd\n");
+ return 0;
+ }
+
+ cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
+ if (eax < SVM_CPUID_FUNC) {
+ printk(KERN_INFO "has_svm: can't execute cpuid_8000000a\n");
+ return 0;
+ }
+
+ cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
+ if (!(ecx & (1 << SVM_CPUID_FEATURE_SHIFT))) {
+ printk(KERN_DEBUG "has_svm: svm not available\n");
+ return 0;
+ }
+ return 1;
+}
+
+static void svm_hardware_disable(void *garbage)
+{
+ struct svm_cpu_data *svm_data
+ = per_cpu(svm_data, raw_smp_processor_id());
+
+ if (svm_data) {
+ uint64_t efer;
+
+ wrmsrl(MSR_VM_HSAVE_PA, 0);
+ rdmsrl(MSR_EFER, efer);
+ wrmsrl(MSR_EFER, efer & ~MSR_EFER_SVME_MASK);
+ per_cpu(svm_data, raw_smp_processor_id()) = 0;
+ __free_page(svm_data->save_area);
+ kfree(svm_data);
+ }
+}
+
+static void svm_hardware_enable(void *garbage)
+{
+
+ struct svm_cpu_data *svm_data;
+ uint64_t efer;
+#ifdef CONFIG_X86_64
+ struct desc_ptr gdt_descr;
+#else
+ struct Xgt_desc_struct gdt_descr;
+#endif
+ struct desc_struct *gdt;
+ int me = raw_smp_processor_id();
+
+ if (!has_svm()) {
+ printk(KERN_ERR "svm_cpu_init: err EOPNOTSUPP on %d\n", me);
+ return;
+ }
+ svm_data = per_cpu(svm_data, me);
+
+ if (!svm_data) {
+ printk(KERN_ERR "svm_cpu_init: svm_data is NULL on %d\n",
+ me);
+ return;
+ }
+
+ svm_data->asid_generation = 1;
+ svm_data->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
+ svm_data->next_asid = svm_data->max_asid + 1;
+
+ asm volatile ( "sgdt %0" : "=m"(gdt_descr) );
+ gdt = (struct desc_struct *)gdt_descr.address;
+ svm_data->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);
+
+ rdmsrl(MSR_EFER, efer);
+ wrmsrl(MSR_EFER, efer | MSR_EFER_SVME_MASK);
+
+ wrmsrl(MSR_VM_HSAVE_PA,
+ page_to_pfn(svm_data->save_area) << PAGE_SHIFT);
+}
+
+static int svm_cpu_init(int cpu)
+{
+ struct svm_cpu_data *svm_data;
+ int r;
+
+ svm_data = kzalloc(sizeof(struct svm_cpu_data), GFP_KERNEL);
+ if (!svm_data)
+ return -ENOMEM;
+ svm_data->cpu = cpu;
+ svm_data->save_area = alloc_page(GFP_KERNEL);
+ r = -ENOMEM;
+ if (!svm_data->save_area)
+ goto err_1;
+
+ per_cpu(svm_data, cpu) = svm_data;
+
+ return 0;
+
+err_1:
+ kfree(svm_data);
+ return r;
+
+}
+
+static int set_msr_interception(u32 *msrpm, unsigned msr,
+ int read, int write)
+{
+ int i;
+
+ for (i = 0; i < NUM_MSR_MAPS; i++) {
+ if (msr >= msrpm_ranges[i] &&
+ msr < msrpm_ranges[i] + MSRS_IN_RANGE) {
+ u32 msr_offset = (i * MSRS_IN_RANGE + msr -
+ msrpm_ranges[i]) * 2;
+
+ u32 *base = msrpm + (msr_offset / 32);
+ u32 msr_shift = msr_offset % 32;
+ u32 mask = ((write) ? 0 : 2) | ((read) ? 0 : 1);
+ *base = (*base & ~(0x3 << msr_shift)) |
+ (mask << msr_shift);
+ return 1;
+ }
+ }
+ printk(KERN_DEBUG "%s: not found 0x%x\n", __FUNCTION__, msr);
+ return 0;
+}
+
+static __init int svm_hardware_setup(void)
+{
+ int cpu;
+ struct page *iopm_pages;
+ struct page *msrpm_pages;
+ void *msrpm_va;
+ int r;
+
+ kvm_emulator_want_group7_invlpg();
+
+ iopm_pages = alloc_pages(GFP_KERNEL, IOPM_ALLOC_ORDER);
+
+ if (!iopm_pages)
+ return -ENOMEM;
+ memset(page_address(iopm_pages), 0xff,
+ PAGE_SIZE * (1 << IOPM_ALLOC_ORDER));
+ iopm_base = page_to_pfn(iopm_pages) << PAGE_SHIFT;
+
+
+ msrpm_pages = alloc_pages(GFP_KERNEL, MSRPM_ALLOC_ORDER);
+
+ r = -ENOMEM;
+ if (!msrpm_pages)
+ goto err_1;
+
+ msrpm_va = page_address(msrpm_pages);
+ memset(msrpm_va, 0xff, PAGE_SIZE * (1 << MSRPM_ALLOC_ORDER));
+ msrpm_base = page_to_pfn(msrpm_pages) << PAGE_SHIFT;
+
+#ifdef CONFIG_X86_64
+ set_msr_interception(msrpm_va, MSR_GS_BASE, 1, 1);
+ set_msr_interception(msrpm_va, MSR_FS_BASE, 1, 1);
+ set_msr_interception(msrpm_va, MSR_KERNEL_GS_BASE, 1, 1);
+ set_msr_interception(msrpm_va, MSR_STAR, 1, 1);
+ set_msr_interception(msrpm_va, MSR_LSTAR, 1, 1);
+ set_msr_interception(msrpm_va, MSR_CSTAR, 1, 1);
+ set_msr_interception(msrpm_va, MSR_SYSCALL_MASK, 1, 1);
+#endif
+ set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_CS, 1, 1);
+ set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_ESP, 1, 1);
+ set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_EIP, 1, 1);
+
+ for_each_online_cpu(cpu) {
+ r = svm_cpu_init(cpu);
+ if (r)
+ goto err_2;
+ }
+ return 0;
+
+err_2:
+ __free_pages(msrpm_pages, MSRPM_ALLOC_ORDER);
+ msrpm_base = 0;
+err_1:
+ __free_pages(iopm_pages, IOPM_ALLOC_ORDER);
+ iopm_base = 0;
+ return r;
+}
+
+static __exit void svm_hardware_unsetup(void)
+{
+ __free_pages(pfn_to_page(msrpm_base >> PAGE_SHIFT), MSRPM_ALLOC_ORDER);
+ __free_pages(pfn_to_page(iopm_base >> PAGE_SHIFT), IOPM_ALLOC_ORDER);
+ iopm_base = msrpm_base = 0;
+}
+
+static void init_seg(struct vmcb_seg *seg)
+{
+ seg->selector = 0;
+ seg->attrib = SVM_SELECTOR_P_MASK | SVM_SELECTOR_S_MASK |
+ SVM_SELECTOR_WRITE_MASK; /* Read/Write Data Segment */
+ seg->limit = 0xffff;
+ seg->base = 0;
+}
+
+static void init_sys_seg(struct vmcb_seg *seg, uint32_t type)
+{
+ seg->selector = 0;
+ seg->attrib = SVM_SELECTOR_P_MASK | type;
+ seg->limit = 0xffff;
+ seg->base = 0;
+}
+
+static int svm_vcpu_setup(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
+
+static void init_vmcb(struct vmcb *vmcb)
+{
+ struct vmcb_control_area *control = &vmcb->control;
+ struct vmcb_save_area *save = &vmcb->save;
+ u64 tsc;
+
+ control->intercept_cr_read = INTERCEPT_CR0_MASK |
+ INTERCEPT_CR3_MASK |
+ INTERCEPT_CR4_MASK;
+
+ control->intercept_cr_write = INTERCEPT_CR0_MASK |
+ INTERCEPT_CR3_MASK |
+ INTERCEPT_CR4_MASK;
+
+ control->intercept_dr_read = INTERCEPT_DR0_MASK |
+ INTERCEPT_DR1_MASK |
+ INTERCEPT_DR2_MASK |
+ INTERCEPT_DR3_MASK;
+
+ control->intercept_dr_write = INTERCEPT_DR0_MASK |
+ INTERCEPT_DR1_MASK |
+ INTERCEPT_DR2_MASK |
+ INTERCEPT_DR3_MASK |
+ INTERCEPT_DR5_MASK |
+ INTERCEPT_DR7_MASK;
+
+ control->intercept_exceptions = 1 << PF_VECTOR;
+
+
+ control->intercept = (1ULL << INTERCEPT_INTR) |
+ (1ULL << INTERCEPT_NMI) |
+ /*
+ * selective cr0 intercept bug?
+ * 0: 0f 22 d8 mov %eax,%cr3
+ * 3: 0f 20 c0 mov %cr0,%eax
+ * 6: 0d 00 00 00 80 or $0x80000000,%eax
+ * b: 0f 22 c0 mov %eax,%cr0
+ * set cr3 ->interception
+ * get cr0 ->interception
+ * set cr0 -> no interception
+ */
+ /* (1ULL << INTERCEPT_SELECTIVE_CR0) | */
+ (1ULL << INTERCEPT_CPUID) |
+ (1ULL << INTERCEPT_HLT) |
+ (1ULL << INTERCEPT_INVLPG) |
+ (1ULL << INTERCEPT_INVLPGA) |
+ (1ULL << INTERCEPT_IOIO_PROT) |
+ (1ULL << INTERCEPT_MSR_PROT) |
+ (1ULL << INTERCEPT_TASK_SWITCH) |
+ (1ULL << INTERCEPT_VMRUN) |
+ (1ULL << INTERCEPT_VMMCALL) |
+ (1ULL << INTERCEPT_VMLOAD) |
+ (1ULL << INTERCEPT_VMSAVE) |
+ (1ULL << INTERCEPT_STGI) |
+ (1ULL << INTERCEPT_CLGI) |
+ (1ULL << INTERCEPT_SKINIT);
+
+ control->iopm_base_pa = iopm_base;
+ control->msrpm_base_pa = msrpm_base;
+ rdtscll(tsc);
+ control->tsc_offset = -tsc;
+ control->int_ctl = V_INTR_MASKING_MASK;
+
+ init_seg(&save->es);
+ init_seg(&save->ss);
+ init_seg(&save->ds);
+ init_seg(&save->fs);
+ init_seg(&save->gs);
+
+ save->cs.selector = 0xf000;
+ /* Executable/Readable Code Segment */
+ save->cs.attrib = SVM_SELECTOR_READ_MASK | SVM_SELECTOR_P_MASK |
+ SVM_SELECTOR_S_MASK | SVM_SELECTOR_CODE_MASK;
+ save->cs.limit = 0xffff;
+ save->cs.base = 0xffff0000;
+
+ save->gdtr.limit = 0xffff;
+ save->idtr.limit = 0xffff;
+
+ init_sys_seg(&save->ldtr, SEG_TYPE_LDT);
+ init_sys_seg(&save->tr, SEG_TYPE_BUSY_TSS16);
+
+ save->efer = MSR_EFER_SVME_MASK;
+
+ save->dr6 = 0xffff0ff0;
+ save->dr7 = 0x400;
+ save->rflags = 2;
+ save->rip = 0x0000fff0;
+
+ /*
+ * cr0 val on cpu init should be 0x60000010, we enable cpu
+ * cache by default. the orderly way is to enable cache in bios.
+ */
+ save->cr0 = 0x00000010 | CR0_PG_MASK;
+ save->cr4 = CR4_PAE_MASK;
+ /* rdx = ?? */
+}
+
+static int svm_create_vcpu(struct kvm_vcpu *vcpu)
+{
+ struct page *page;
+ int r;
+
+ r = -ENOMEM;
+ vcpu->svm = kzalloc(sizeof *vcpu->svm, GFP_KERNEL);
+ if (!vcpu->svm)
+ goto out1;
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ goto out2;
+
+ vcpu->svm->vmcb = page_address(page);
+ memset(vcpu->svm->vmcb, 0, PAGE_SIZE);
+ vcpu->svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
+ vcpu->svm->cr0 = 0x00000010;
+ vcpu->svm->asid_generation = 0;
+ memset(vcpu->svm->db_regs, 0, sizeof(vcpu->svm->db_regs));
+ init_vmcb(vcpu->svm->vmcb);
+
+ return 0;
+
+out2:
+ kfree(vcpu->svm);
+out1:
+ return r;
+}
+
+static void svm_free_vcpu(struct kvm_vcpu *vcpu)
+{
+ if (!vcpu->svm)
+ return;
+ if (vcpu->svm->vmcb)
+ __free_page(pfn_to_page(vcpu->svm->vmcb_pa >> PAGE_SHIFT));
+ kfree(vcpu->svm);
+}
+
+static struct kvm_vcpu *svm_vcpu_load(struct kvm_vcpu *vcpu)
+{
+ get_cpu();
+ return vcpu;
+}
+
+static void svm_vcpu_put(struct kvm_vcpu *vcpu)
+{
+ put_cpu();
+}
+
+static void svm_cache_regs(struct kvm_vcpu *vcpu)
+{
+ vcpu->regs[VCPU_REGS_RAX] = vcpu->svm->vmcb->save.rax;
+ vcpu->regs[VCPU_REGS_RSP] = vcpu->svm->vmcb->save.rsp;
+ vcpu->rip = vcpu->svm->vmcb->save.rip;
+}
+
+static void svm_decache_regs(struct kvm_vcpu *vcpu)
+{
+ vcpu->svm->vmcb->save.rax = vcpu->regs[VCPU_REGS_RAX];
+ vcpu->svm->vmcb->save.rsp = vcpu->regs[VCPU_REGS_RSP];
+ vcpu->svm->vmcb->save.rip = vcpu->rip;
+}
+
+static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
+{
+ return vcpu->svm->vmcb->save.rflags;
+}
+
+static void svm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
+{
+ vcpu->svm->vmcb->save.rflags = rflags;
+}
+
+static struct vmcb_seg *svm_seg(struct kvm_vcpu *vcpu, int seg)
+{
+ struct vmcb_save_area *save = &vcpu->svm->vmcb->save;
+
+ switch (seg) {
+ case VCPU_SREG_CS: return &save->cs;
+ case VCPU_SREG_DS: return &save->ds;
+ case VCPU_SREG_ES: return &save->es;
+ case VCPU_SREG_FS: return &save->fs;
+ case VCPU_SREG_GS: return &save->gs;
+ case VCPU_SREG_SS: return &save->ss;
+ case VCPU_SREG_TR: return &save->tr;
+ case VCPU_SREG_LDTR: return &save->ldtr;
+ }
+ BUG();
+ return 0;
+}
+
+static u64 svm_get_segment_base(struct kvm_vcpu *vcpu, int seg)
+{
+ struct vmcb_seg *s = svm_seg(vcpu, seg);
+
+ return s->base;
+}
+
+static void svm_get_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ struct vmcb_seg *s = svm_seg(vcpu, seg);
+
+ var->base = s->base;
+ var->limit = s->limit;
+ var->selector = s->selector;
+ var->type = s->attrib & SVM_SELECTOR_TYPE_MASK;
+ var->s = (s->attrib >> SVM_SELECTOR_S_SHIFT) & 1;
+ var->dpl = (s->attrib >> SVM_SELECTOR_DPL_SHIFT) & 3;
+ var->present = (s->attrib >> SVM_SELECTOR_P_SHIFT) & 1;
+ var->avl = (s->attrib >> SVM_SELECTOR_AVL_SHIFT) & 1;
+ var->l = (s->attrib >> SVM_SELECTOR_L_SHIFT) & 1;
+ var->db = (s->attrib >> SVM_SELECTOR_DB_SHIFT) & 1;
+ var->g = (s->attrib >> SVM_SELECTOR_G_SHIFT) & 1;
+ var->unusable = !var->present;
+}
+
+static void svm_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l)
+{
+ struct vmcb_seg *s = svm_seg(vcpu, VCPU_SREG_CS);
+
+ *db = (s->attrib >> SVM_SELECTOR_DB_SHIFT) & 1;
+ *l = (s->attrib >> SVM_SELECTOR_L_SHIFT) & 1;
+}
+
+static void svm_get_idt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ dt->limit = vcpu->svm->vmcb->save.ldtr.limit;
+ dt->base = vcpu->svm->vmcb->save.ldtr.base;
+}
+
+static void svm_set_idt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ vcpu->svm->vmcb->save.ldtr.limit = dt->limit;
+ vcpu->svm->vmcb->save.ldtr.base = dt->base ;
+}
+
+static void svm_get_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ dt->limit = vcpu->svm->vmcb->save.gdtr.limit;
+ dt->base = vcpu->svm->vmcb->save.gdtr.base;
+}
+
+static void svm_set_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ vcpu->svm->vmcb->save.gdtr.limit = dt->limit;
+ vcpu->svm->vmcb->save.gdtr.base = dt->base ;
+}
+
+static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
+{
+#ifdef CONFIG_X86_64
+ if (vcpu->shadow_efer & KVM_EFER_LME) {
+ if (!is_paging(vcpu) && (cr0 & CR0_PG_MASK)) {
+ vcpu->shadow_efer |= KVM_EFER_LMA;
+ vcpu->svm->vmcb->save.efer |= KVM_EFER_LMA | KVM_EFER_LME;
+ }
+
+ if (is_paging(vcpu) && !(cr0 & CR0_PG_MASK) ) {
+ vcpu->shadow_efer &= ~KVM_EFER_LMA;
+ vcpu->svm->vmcb->save.efer &= ~(KVM_EFER_LMA | KVM_EFER_LME);
+ }
+ }
+#endif
+ vcpu->svm->cr0 = cr0;
+ vcpu->svm->vmcb->save.cr0 = cr0 | CR0_PG_MASK;
+ vcpu->cr0 = cr0;
+}
+
+static void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
+{
+ vcpu->cr4 = cr4;
+ vcpu->svm->vmcb->save.cr4 = cr4 | CR4_PAE_MASK;
+}
+
+static void svm_set_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ struct vmcb_seg *s = svm_seg(vcpu, seg);
+
+ s->base = var->base;
+ s->limit = var->limit;
+ s->selector = var->selector;
+ if (var->unusable)
+ s->attrib = 0;
+ else {
+ s->attrib = (var->type & SVM_SELECTOR_TYPE_MASK);
+ s->attrib |= (var->s & 1) << SVM_SELECTOR_S_SHIFT;
+ s->attrib |= (var->dpl & 3) << SVM_SELECTOR_DPL_SHIFT;
+ s->attrib |= (var->present & 1) << SVM_SELECTOR_P_SHIFT;
+ s->attrib |= (var->avl & 1) << SVM_SELECTOR_AVL_SHIFT;
+ s->attrib |= (var->l & 1) << SVM_SELECTOR_L_SHIFT;
+ s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
+ s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
+ }
+ if (seg == VCPU_SREG_CS)
+ vcpu->svm->vmcb->save.cpl
+ = (vcpu->svm->vmcb->save.cs.attrib
+ >> SVM_SELECTOR_DPL_SHIFT) & 3;
+
+}
+
+/* FIXME:
+
+ vcpu->svm->vmcb->control.int_ctl &= ~V_TPR_MASK;
+ vcpu->svm->vmcb->control.int_ctl |= (sregs->cr8 & V_TPR_MASK);
+
+*/
+
+static int svm_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
+{
+ return -EOPNOTSUPP;
+}
+
+static void load_host_msrs(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ for ( i = 0; i < NR_HOST_SAVE_MSRS; i++)
+ wrmsrl(host_save_msrs[i], vcpu->svm->host_msrs[i]);
+}
+
+static void save_host_msrs(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ for ( i = 0; i < NR_HOST_SAVE_MSRS; i++)
+ rdmsrl(host_save_msrs[i], vcpu->svm->host_msrs[i]);
+}
+
+static void new_asid(struct kvm_vcpu *vcpu, struct svm_cpu_data *svm_data)
+{
+ if (svm_data->next_asid > svm_data->max_asid) {
+ ++svm_data->asid_generation;
+ svm_data->next_asid = 1;
+ vcpu->svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
+ }
+
+ vcpu->cpu = svm_data->cpu;
+ vcpu->svm->asid_generation = svm_data->asid_generation;
+ vcpu->svm->vmcb->control.asid = svm_data->next_asid++;
+}
+
+static void svm_invlpg(struct kvm_vcpu *vcpu, gva_t address)
+{
+ invlpga(address, vcpu->svm->vmcb->control.asid); // is needed?
+}
+
+static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
+{
+ return vcpu->svm->db_regs[dr];
+}
+
+static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
+ int *exception)
+{
+ *exception = 0;
+
+ if (vcpu->svm->vmcb->save.dr7 & DR7_GD_MASK) {
+ vcpu->svm->vmcb->save.dr7 &= ~DR7_GD_MASK;
+ vcpu->svm->vmcb->save.dr6 |= DR6_BD_MASK;
+ *exception = DB_VECTOR;
+ return;
+ }
+
+ switch (dr) {
+ case 0 ... 3:
+ vcpu->svm->db_regs[dr] = value;
+ return;
+ case 4 ... 5:
+ if (vcpu->cr4 & CR4_DE_MASK) {
+ *exception = UD_VECTOR;
+ return;
+ }
+ case 7: {
+ if (value & ~((1ULL << 32) - 1)) {
+ *exception = GP_VECTOR;
+ return;
+ }
+ vcpu->svm->vmcb->save.dr7 = value;
+ return;
+ }
+ default:
+ printk(KERN_DEBUG "%s: unexpected dr %u\n",
+ __FUNCTION__, dr);
+ *exception = UD_VECTOR;
+ return;
+ }
+}
+
+static int pf_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 exit_int_info = vcpu->svm->vmcb->control.exit_int_info;
+ u64 fault_address;
+ u32 error_code;
+ enum emulation_result er;
+
+ if (is_external_interrupt(exit_int_info))
+ push_irq(vcpu, exit_int_info & SVM_EVTINJ_VEC_MASK);
+
+ spin_lock(&vcpu->kvm->lock);
+
+ fault_address = vcpu->svm->vmcb->control.exit_info_2;
+ error_code = vcpu->svm->vmcb->control.exit_info_1;
+ if (!vcpu->mmu.page_fault(vcpu, fault_address, error_code)) {
+ spin_unlock(&vcpu->kvm->lock);
+ return 1;
+ }
+ er = emulate_instruction(vcpu, kvm_run, fault_address, error_code);
+ spin_unlock(&vcpu->kvm->lock);
+
+ switch (er) {
+ case EMULATE_DONE:
+ return 1;
+ case EMULATE_DO_MMIO:
+ ++kvm_stat.mmio_exits;
+ kvm_run->exit_reason = KVM_EXIT_MMIO;
+ return 0;
+ case EMULATE_FAIL:
+ vcpu_printf(vcpu, "%s: emulate fail\n", __FUNCTION__);
+ break;
+ default:
+ BUG();
+ }
+
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ return 0;
+}
+
+static int io_get_override(struct kvm_vcpu *vcpu,
+ struct vmcb_seg **seg,
+ int *addr_override)
+{
+ u8 inst[MAX_INST_SIZE];
+ unsigned ins_length;
+ gva_t rip;
+ int i;
+
+ rip = vcpu->svm->vmcb->save.rip;
+ ins_length = vcpu->svm->next_rip - rip;
+ rip += vcpu->svm->vmcb->save.cs.base;
+
+ if (ins_length > MAX_INST_SIZE)
+ printk(KERN_DEBUG
+ "%s: inst length err, cs base 0x%llx rip 0x%llx "
+ "next rip 0x%llx ins_length %u\n",
+ __FUNCTION__,
+ vcpu->svm->vmcb->save.cs.base,
+ vcpu->svm->vmcb->save.rip,
+ vcpu->svm->vmcb->control.exit_info_2,
+ ins_length);
+
+ if (kvm_read_guest(vcpu, rip, ins_length, inst) != ins_length)
+ /* #PF */
+ return 0;
+
+ *addr_override = 0;
+ *seg = 0;
+ for (i = 0; i < ins_length; i++)
+ switch (inst[i]) {
+ case 0xf0:
+ case 0xf2:
+ case 0xf3:
+ case 0x66:
+ continue;
+ case 0x67:
+ *addr_override = 1;
+ continue;
+ case 0x2e:
+ *seg = &vcpu->svm->vmcb->save.cs;
+ continue;
+ case 0x36:
+ *seg = &vcpu->svm->vmcb->save.ss;
+ continue;
+ case 0x3e:
+ *seg = &vcpu->svm->vmcb->save.ds;
+ continue;
+ case 0x26:
+ *seg = &vcpu->svm->vmcb->save.es;
+ continue;
+ case 0x64:
+ *seg = &vcpu->svm->vmcb->save.fs;
+ continue;
+ case 0x65:
+ *seg = &vcpu->svm->vmcb->save.gs;
+ continue;
+ default:
+ return 1;
+ }
+ printk(KERN_DEBUG "%s: unexpected\n", __FUNCTION__);
+ return 0;
+}
+
+static unsigned long io_adress(struct kvm_vcpu *vcpu, int ins, u64 *address)
+{
+ unsigned long addr_mask;
+ unsigned long *reg;
+ struct vmcb_seg *seg;
+ int addr_override;
+ struct vmcb_save_area *save_area = &vcpu->svm->vmcb->save;
+ u16 cs_attrib = save_area->cs.attrib;
+ unsigned addr_size = get_addr_size(vcpu);
+
+ if (!io_get_override(vcpu, &seg, &addr_override))
+ return 0;
+
+ if (addr_override)
+ addr_size = (addr_size == 2) ? 4: (addr_size >> 1);
+
+ if (ins) {
+ reg = &vcpu->regs[VCPU_REGS_RDI];
+ seg = &vcpu->svm->vmcb->save.es;
+ } else {
+ reg = &vcpu->regs[VCPU_REGS_RSI];
+ seg = (seg) ? seg : &vcpu->svm->vmcb->save.ds;
+ }
+
+ addr_mask = ~0ULL >> (64 - (addr_size * 8));
+
+ if ((cs_attrib & SVM_SELECTOR_L_MASK) &&
+ !(vcpu->svm->vmcb->save.rflags & X86_EFLAGS_VM)) {
+ *address = (*reg & addr_mask);
+ return addr_mask;
+ }
+
+ if (!(seg->attrib & SVM_SELECTOR_P_SHIFT)) {
+ svm_inject_gp(vcpu, 0);
+ return 0;
+ }
+
+ *address = (*reg & addr_mask) + seg->base;
+ return addr_mask;
+}
+
+static int io_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 io_info = vcpu->svm->vmcb->control.exit_info_1; //address size bug?
+ int _in = io_info & SVM_IOIO_TYPE_MASK;
+
+ ++kvm_stat.io_exits;
+
+ vcpu->svm->next_rip = vcpu->svm->vmcb->control.exit_info_2;
+
+ kvm_run->exit_reason = KVM_EXIT_IO;
+ kvm_run->io.port = io_info >> 16;
+ kvm_run->io.direction = (_in) ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT;
+ kvm_run->io.size = ((io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT);
+ kvm_run->io.string = (io_info & SVM_IOIO_STR_MASK) != 0;
+ kvm_run->io.rep = (io_info & SVM_IOIO_REP_MASK) != 0;
+
+ if (kvm_run->io.string) {
+ unsigned addr_mask;
+
+ addr_mask = io_adress(vcpu, _in, &kvm_run->io.address);
+ if (!addr_mask) {
+ printk(KERN_DEBUG "%s: get io address failed\n", __FUNCTION__);
+ return 1;
+ }
+
+ if (kvm_run->io.rep) {
+ kvm_run->io.count = vcpu->regs[VCPU_REGS_RCX] & addr_mask;
+ kvm_run->io.string_down = (vcpu->svm->vmcb->save.rflags
+ & X86_EFLAGS_DF) != 0;
+ }
+ } else {
+ kvm_run->io.value = vcpu->svm->vmcb->save.rax;
+ }
+ return 0;
+}
+
+
+static int nop_on_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ return 1;
+}
+
+static int halt_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ vcpu->svm->next_rip = vcpu->svm->vmcb->save.rip + 1;
+ skip_emulated_instruction(vcpu);
+ if (vcpu->irq_summary && (vcpu->svm->vmcb->save.rflags & X86_EFLAGS_IF))
+ return 1;
+
+ kvm_run->exit_reason = KVM_EXIT_HLT;
+ return 0;
+}
+
+static int invalid_op_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ inject_ud(vcpu);
+ return 1;
+}
+
+static int task_switch_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ printk(KERN_DEBUG "%s: task swiche is unsupported\n", __FUNCTION__);
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ return 0;
+}
+
+static int cpuid_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ vcpu->svm->next_rip = vcpu->svm->vmcb->save.rip + 2;
+ kvm_run->exit_reason = KVM_EXIT_CPUID;
+ return 0;
+}
+
+static int emulate_on_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ if (emulate_instruction(vcpu, 0, 0, 0) != EMULATE_DONE)
+ printk(KERN_ERR "%s: failed\n", __FUNCTION__);
+ return 1;
+}
+
+static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
+{
+ switch (ecx) {
+ case MSR_IA32_MC0_CTL:
+ case MSR_IA32_MCG_STATUS:
+ case MSR_IA32_MCG_CAP:
+ case MSR_IA32_MC0_MISC:
+ case MSR_IA32_MC0_MISC+4:
+ case MSR_IA32_MC0_MISC+8:
+ case MSR_IA32_MC0_MISC+12:
+ case MSR_IA32_MC0_MISC+16:
+ case MSR_IA32_UCODE_REV:
+ /* MTRR registers */
+ case 0xfe:
+ case 0x200 ... 0x2ff:
+ *data = 0;
+ break;
+ case MSR_IA32_TIME_STAMP_COUNTER: {
+ u64 tsc;
+
+ rdtscll(tsc);
+ *data = vcpu->svm->vmcb->control.tsc_offset + tsc;
+ break;
+ }
+ case MSR_EFER:
+ *data = vcpu->shadow_efer;
+ break;
+ case MSR_IA32_APICBASE:
+ *data = vcpu->apic_base;
+ break;
+#ifdef CONFIG_X86_64
+ case MSR_STAR:
+ *data = vcpu->svm->vmcb->save.star;
+ break;
+ case MSR_LSTAR:
+ *data = vcpu->svm->vmcb->save.lstar;
+ break;
+ case MSR_CSTAR:
+ *data = vcpu->svm->vmcb->save.cstar;
+ break;
+ case MSR_KERNEL_GS_BASE:
+ *data = vcpu->svm->vmcb->save.kernel_gs_base;
+ break;
+ case MSR_SYSCALL_MASK:
+ *data = vcpu->svm->vmcb->save.sfmask;
+ break;
+#endif
+ case MSR_IA32_SYSENTER_CS:
+ *data = vcpu->svm->vmcb->save.sysenter_cs;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ *data = vcpu->svm->vmcb->save.sysenter_eip;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ *data = vcpu->svm->vmcb->save.sysenter_esp;
+ break;
+ default:
+ printk(KERN_ERR "kvm: unhandled rdmsr: 0x%x\n", ecx);
+ return 1;
+ }
+ return 0;
+}
+
+static int rdmsr_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 ecx = vcpu->regs[VCPU_REGS_RCX];
+ u64 data;
+
+ if (svm_get_msr(vcpu, ecx, &data))
+ svm_inject_gp(vcpu, 0);
+ else {
+ vcpu->svm->vmcb->save.rax = data & 0xffffffff;
+ vcpu->regs[VCPU_REGS_RDX] = data >> 32;
+ vcpu->svm->next_rip = vcpu->svm->vmcb->save.rip + 2;
+ skip_emulated_instruction(vcpu);
+ }
+ return 1;
+}
+
+static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
+{
+ switch (ecx) {
+#ifdef CONFIG_X86_64
+ case MSR_EFER:
+ set_efer(vcpu, data);
+ break;
+#endif
+ case MSR_IA32_MC0_STATUS:
+ printk(KERN_WARNING "%s: MSR_IA32_MC0_STATUS 0x%llx, nop\n"
+ , __FUNCTION__, data);
+ break;
+ case MSR_IA32_TIME_STAMP_COUNTER: {
+ u64 tsc;
+
+ rdtscll(tsc);
+ vcpu->svm->vmcb->control.tsc_offset = data - tsc;
+ break;
+ }
+ case MSR_IA32_UCODE_REV:
+ case MSR_IA32_UCODE_WRITE:
+ case 0x200 ... 0x2ff: /* MTRRs */
+ break;
+ case MSR_IA32_APICBASE:
+ vcpu->apic_base = data;
+ break;
+#ifdef CONFIG_X86_64_
+ case MSR_STAR:
+ vcpu->svm->vmcb->save.star = data;
+ break;
+ case MSR_LSTAR:
+ vcpu->svm->vmcb->save.lstar = data;
+ break;
+ case MSR_CSTAR:
+ vcpu->svm->vmcb->save.cstar = data;
+ break;
+ case MSR_KERNEL_GS_BASE:
+ vcpu->svm->vmcb->save.kernel_gs_base = data;
+ break;
+ case MSR_SYSCALL_MASK:
+ vcpu->svm->vmcb->save.sfmask = data;
+ break;
+#endif
+ case MSR_IA32_SYSENTER_CS:
+ vcpu->svm->vmcb->save.sysenter_cs = data;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ vcpu->svm->vmcb->save.sysenter_eip = data;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ vcpu->svm->vmcb->save.sysenter_esp = data;
+ break;
+ default:
+ printk(KERN_ERR "kvm: unhandled wrmsr: %x\n", ecx);
+ return 1;
+ }
+ return 0;
+}
+
+static int wrmsr_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 ecx = vcpu->regs[VCPU_REGS_RCX];
+ u64 data = (vcpu->svm->vmcb->save.rax & -1u)
+ | ((u64)(vcpu->regs[VCPU_REGS_RDX] & -1u) << 32);
+ vcpu->svm->next_rip = vcpu->svm->vmcb->save.rip + 2;
+ if (svm_set_msr(vcpu, ecx, data))
+ svm_inject_gp(vcpu, 0);
+ else
+ skip_emulated_instruction(vcpu);
+ return 1;
+}
+
+static int msr_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ if (vcpu->svm->vmcb->control.exit_info_1)
+ return wrmsr_interception(vcpu, kvm_run);
+ else
+ return rdmsr_interception(vcpu, kvm_run);
+}
+
+static int (*svm_exit_handlers[])(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run) = {
+ [SVM_EXIT_READ_CR0] = emulate_on_interception,
+ [SVM_EXIT_READ_CR3] = emulate_on_interception,
+ [SVM_EXIT_READ_CR4] = emulate_on_interception,
+ /* for now: */
+ [SVM_EXIT_WRITE_CR0] = emulate_on_interception,
+ [SVM_EXIT_WRITE_CR3] = emulate_on_interception,
+ [SVM_EXIT_WRITE_CR4] = emulate_on_interception,
+ [SVM_EXIT_READ_DR0] = emulate_on_interception,
+ [SVM_EXIT_READ_DR1] = emulate_on_interception,
+ [SVM_EXIT_READ_DR2] = emulate_on_interception,
+ [SVM_EXIT_READ_DR3] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR0] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR1] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR2] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR3] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR5] = emulate_on_interception,
+ [SVM_EXIT_WRITE_DR7] = emulate_on_interception,
+ [SVM_EXIT_EXCP_BASE + PF_VECTOR] = pf_interception,
+ [SVM_EXIT_INTR] = nop_on_interception,
+ [SVM_EXIT_NMI] = nop_on_interception,
+ [SVM_EXIT_SMI] = nop_on_interception,
+ [SVM_EXIT_INIT] = nop_on_interception,
+ /* [SVM_EXIT_CR0_SEL_WRITE] = emulate_on_interception, */
+ [SVM_EXIT_CPUID] = cpuid_interception,
+ [SVM_EXIT_HLT] = halt_interception,
+ [SVM_EXIT_INVLPG] = emulate_on_interception,
+ [SVM_EXIT_INVLPGA] = invalid_op_interception,
+ [SVM_EXIT_IOIO] = io_interception,
+ [SVM_EXIT_MSR] = msr_interception,
+ [SVM_EXIT_TASK_SWITCH] = task_switch_interception,
+ [SVM_EXIT_VMRUN] = invalid_op_interception,
+ [SVM_EXIT_VMMCALL] = invalid_op_interception,
+ [SVM_EXIT_VMLOAD] = invalid_op_interception,
+ [SVM_EXIT_VMSAVE] = invalid_op_interception,
+ [SVM_EXIT_STGI] = invalid_op_interception,
+ [SVM_EXIT_CLGI] = invalid_op_interception,
+ [SVM_EXIT_SKINIT] = invalid_op_interception,
+};
+
+
+static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 exit_code = vcpu->svm->vmcb->control.exit_code;
+
+ kvm_run->exit_type = KVM_EXIT_TYPE_VM_EXIT;
+
+ if (is_external_interrupt(vcpu->svm->vmcb->control.exit_int_info) &&
+ exit_code != SVM_EXIT_EXCP_BASE + PF_VECTOR)
+ printk(KERN_ERR "%s: unexpected exit_ini_info 0x%x "
+ "exit_code 0x%x\n",
+ __FUNCTION__, vcpu->svm->vmcb->control.exit_int_info,
+ exit_code);
+
+ if (exit_code >= sizeof(svm_exit_handlers) / sizeof(*svm_exit_handlers)
+ || svm_exit_handlers[exit_code] == 0) {
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ printk(KERN_ERR "%s: 0x%x @ 0x%llx cr0 0x%lx rflags 0x%llx\n",
+ __FUNCTION__,
+ exit_code,
+ vcpu->svm->vmcb->save.rip,
+ vcpu->cr0,
+ vcpu->svm->vmcb->save.rflags);
+ return 0;
+ }
+
+ return svm_exit_handlers[exit_code](vcpu, kvm_run);
+}
+
+static void reload_tss(struct kvm_vcpu *vcpu)
+{
+ int cpu = raw_smp_processor_id();
+
+ struct svm_cpu_data *svm_data = per_cpu(svm_data, cpu);
+ svm_data->tss_desc->type = 9; //available 32/64-bit TSS
+ load_TR_desc();
+}
+
+static void pre_svm_run(struct kvm_vcpu *vcpu)
+{
+ int cpu = raw_smp_processor_id();
+
+ struct svm_cpu_data *svm_data = per_cpu(svm_data, cpu);
+
+ vcpu->svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
+ if (vcpu->cpu != cpu ||
+ vcpu->svm->asid_generation != svm_data->asid_generation)
+ new_asid(vcpu, svm_data);
+}
+
+
+static inline void kvm_try_inject_irq(struct kvm_vcpu *vcpu)
+{
+ struct vmcb_control_area *control;
+
+ if (!vcpu->irq_summary)
+ return;
+
+ control = &vcpu->svm->vmcb->control;
+
+ control->int_vector = pop_irq(vcpu);
+ control->int_ctl &= ~V_INTR_PRIO_MASK;
+ control->int_ctl |= V_IRQ_MASK |
+ ((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT);
+}
+
+static void kvm_reput_irq(struct kvm_vcpu *vcpu)
+{
+ struct vmcb_control_area *control = &vcpu->svm->vmcb->control;
+
+ if (control->int_ctl & V_IRQ_MASK) {
+ control->int_ctl &= ~V_IRQ_MASK;
+ push_irq(vcpu, control->int_vector);
+ }
+}
+
+static void save_db_regs(unsigned long *db_regs)
+{
+ asm volatile ("mov %%dr0, %0" : "=r"(db_regs[0]));
+ asm volatile ("mov %%dr1, %0" : "=r"(db_regs[1]));
+ asm volatile ("mov %%dr2, %0" : "=r"(db_regs[2]));
+ asm volatile ("mov %%dr3, %0" : "=r"(db_regs[3]));
+}
+
+static void load_db_regs(unsigned long *db_regs)
+{
+ asm volatile ("mov %0, %%dr0" : : "r"(db_regs[0]));
+ asm volatile ("mov %0, %%dr1" : : "r"(db_regs[1]));
+ asm volatile ("mov %0, %%dr2" : : "r"(db_regs[2]));
+ asm volatile ("mov %0, %%dr3" : : "r"(db_regs[3]));
+}
+
+static int svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u16 fs_selector;
+ u16 gs_selector;
+ u16 ldt_selector;
+
+again:
+ kvm_try_inject_irq(vcpu);
+
+ clgi();
+
+ pre_svm_run(vcpu);
+
+ save_host_msrs(vcpu);
+ fs_selector = read_fs();
+ gs_selector = read_gs();
+ ldt_selector = read_ldt();
+ vcpu->svm->host_cr2 = kvm_read_cr2();
+ vcpu->svm->host_dr6 = read_dr6();
+ vcpu->svm->host_dr7 = read_dr7();
+ vcpu->svm->vmcb->save.cr2 = vcpu->cr2;
+
+ if (vcpu->svm->vmcb->save.dr7 & 0xff) {
+ write_dr7(0);
+ save_db_regs(vcpu->svm->host_db_regs);
+ load_db_regs(vcpu->svm->db_regs);
+ }
+ asm volatile (
+#ifdef CONFIG_X86_64
+ "push %%rbx; push %%rcx; push %%rdx;"
+ "push %%rsi; push %%rdi; push %%rbp;"
+ "push %%r8; push %%r9; push %%r10; push %%r11;"
+ "push %%r12; push %%r13; push %%r14; push %%r15;"
+#else
+ "push %%ebx; push %%ecx; push %%edx;"
+ "push %%esi; push %%edi; push %%ebp;"
+#endif
+
+#ifdef CONFIG_X86_64
+ "mov %c[rbx](%[vcpu]), %%rbx \n\t"
+ "mov %c[rcx](%[vcpu]), %%rcx \n\t"
+ "mov %c[rdx](%[vcpu]), %%rdx \n\t"
+ "mov %c[rsi](%[vcpu]), %%rsi \n\t"
+ "mov %c[rdi](%[vcpu]), %%rdi \n\t"
+ "mov %c[rbp](%[vcpu]), %%rbp \n\t"
+ "mov %c[r8](%[vcpu]), %%r8 \n\t"
+ "mov %c[r9](%[vcpu]), %%r9 \n\t"
+ "mov %c[r10](%[vcpu]), %%r10 \n\t"
+ "mov %c[r11](%[vcpu]), %%r11 \n\t"
+ "mov %c[r12](%[vcpu]), %%r12 \n\t"
+ "mov %c[r13](%[vcpu]), %%r13 \n\t"
+ "mov %c[r14](%[vcpu]), %%r14 \n\t"
+ "mov %c[r15](%[vcpu]), %%r15 \n\t"
+#else
+ "mov %c[rbx](%[vcpu]), %%ebx \n\t"
+ "mov %c[rcx](%[vcpu]), %%ecx \n\t"
+ "mov %c[rdx](%[vcpu]), %%edx \n\t"
+ "mov %c[rsi](%[vcpu]), %%esi \n\t"
+ "mov %c[rdi](%[vcpu]), %%edi \n\t"
+ "mov %c[rbp](%[vcpu]), %%ebp \n\t"
+#endif
+
+#ifdef CONFIG_X86_64
+ /* Enter guest mode */
+ "push %%rax \n\t"
+ "mov %c[svm](%[vcpu]), %%rax \n\t"
+ "mov %c[vmcb](%%rax), %%rax \n\t"
+ SVM_VMLOAD "\n\t"
+ SVM_VMRUN "\n\t"
+ SVM_VMSAVE "\n\t"
+ "pop %%rax \n\t"
+#else
+ /* Enter guest mode */
+ "push %%eax \n\t"
+ "mov %c[svm](%[vcpu]), %%eax \n\t"
+ "mov %c[vmcb](%%eax), %%eax \n\t"
+ SVM_VMLOAD "\n\t"
+ SVM_VMRUN "\n\t"
+ SVM_VMSAVE "\n\t"
+ "pop %%eax \n\t"
+#endif
+
+ /* Save guest registers, load host registers */
+#ifdef CONFIG_X86_64
+ "mov %%rbx, %c[rbx](%[vcpu]) \n\t"
+ "mov %%rcx, %c[rcx](%[vcpu]) \n\t"
+ "mov %%rdx, %c[rdx](%[vcpu]) \n\t"
+ "mov %%rsi, %c[rsi](%[vcpu]) \n\t"
+ "mov %%rdi, %c[rdi](%[vcpu]) \n\t"
+ "mov %%rbp, %c[rbp](%[vcpu]) \n\t"
+ "mov %%r8, %c[r8](%[vcpu]) \n\t"
+ "mov %%r9, %c[r9](%[vcpu]) \n\t"
+ "mov %%r10, %c[r10](%[vcpu]) \n\t"
+ "mov %%r11, %c[r11](%[vcpu]) \n\t"
+ "mov %%r12, %c[r12](%[vcpu]) \n\t"
+ "mov %%r13, %c[r13](%[vcpu]) \n\t"
+ "mov %%r14, %c[r14](%[vcpu]) \n\t"
+ "mov %%r15, %c[r15](%[vcpu]) \n\t"
+
+ "pop %%r15; pop %%r14; pop %%r13; pop %%r12;"
+ "pop %%r11; pop %%r10; pop %%r9; pop %%r8;"
+ "pop %%rbp; pop %%rdi; pop %%rsi;"
+ "pop %%rdx; pop %%rcx; pop %%rbx; \n\t"
+#else
+ "mov %%ebx, %c[rbx](%[vcpu]) \n\t"
+ "mov %%ecx, %c[rcx](%[vcpu]) \n\t"
+ "mov %%edx, %c[rdx](%[vcpu]) \n\t"
+ "mov %%esi, %c[rsi](%[vcpu]) \n\t"
+ "mov %%edi, %c[rdi](%[vcpu]) \n\t"
+ "mov %%ebp, %c[rbp](%[vcpu]) \n\t"
+
+ "pop %%ebp; pop %%edi; pop %%esi;"
+ "pop %%edx; pop %%ecx; pop %%ebx; \n\t"
+#endif
+ :
+ : [vcpu]"a"(vcpu),
+ [svm]"i"(offsetof(struct kvm_vcpu, svm)),
+ [vmcb]"i"(offsetof(struct vcpu_svm, vmcb_pa)),
+ [rbx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RBX])),
+ [rcx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RCX])),
+ [rdx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RDX])),
+ [rsi]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RSI])),
+ [rdi]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RDI])),
+ [rbp]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RBP]))
+#ifdef CONFIG_X86_64
+ ,[r8 ]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R8 ])),
+ [r9 ]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R9 ])),
+ [r10]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R10])),
+ [r11]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R11])),
+ [r12]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R12])),
+ [r13]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R13])),
+ [r14]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R14])),
+ [r15]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R15]))
+#endif
+ : "cc", "memory" );
+
+ if ((vcpu->svm->vmcb->save.dr7 & 0xff))
+ load_db_regs(vcpu->svm->host_db_regs);
+
+ vcpu->cr2 = vcpu->svm->vmcb->save.cr2;
+
+ write_dr6(vcpu->svm->host_dr6);
+ write_dr7(vcpu->svm->host_dr7);
+ kvm_write_cr2(vcpu->svm->host_cr2);
+
+ load_fs(fs_selector);
+ load_gs(gs_selector);
+ load_ldt(ldt_selector);
+ load_host_msrs(vcpu);
+
+ reload_tss(vcpu);
+
+ stgi();
+
+ kvm_reput_irq(vcpu);
+
+ vcpu->svm->next_rip = 0;
+
+ if (vcpu->svm->vmcb->control.exit_code == SVM_EXIT_ERR) {
+ kvm_run->exit_type = KVM_EXIT_TYPE_FAIL_ENTRY;
+ kvm_run->exit_reason = vcpu->svm->vmcb->control.exit_code;
+ return 0;
+ }
+
+ if (handle_exit(vcpu, kvm_run)) {
+ if (signal_pending(current)) {
+ ++kvm_stat.signal_exits;
+ return -EINTR;
+ }
+ kvm_resched(vcpu);
+ goto again;
+ }
+ return 0;
+}
+
+static void svm_flush_tlb(struct kvm_vcpu *vcpu)
+{
+ force_new_asid(vcpu);
+}
+
+static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
+{
+ vcpu->svm->vmcb->save.cr3 = root;
+ force_new_asid(vcpu);
+}
+
+static void svm_inject_page_fault(struct kvm_vcpu *vcpu,
+ unsigned long addr,
+ uint32_t err_code)
+{
+ uint32_t exit_int_info = vcpu->svm->vmcb->control.exit_int_info;
+
+ ++kvm_stat.pf_guest;
+
+ if (is_page_fault(exit_int_info)) {
+
+ vcpu->svm->vmcb->control.event_inj_err = 0;
+ vcpu->svm->vmcb->control.event_inj = SVM_EVTINJ_VALID |
+ SVM_EVTINJ_VALID_ERR |
+ SVM_EVTINJ_TYPE_EXEPT |
+ DF_VECTOR;
+ return;
+ }
+ vcpu->cr2 = addr;
+ vcpu->svm->vmcb->save.cr2 = addr;
+ vcpu->svm->vmcb->control.event_inj = SVM_EVTINJ_VALID |
+ SVM_EVTINJ_VALID_ERR |
+ SVM_EVTINJ_TYPE_EXEPT |
+ PF_VECTOR;
+ vcpu->svm->vmcb->control.event_inj_err = err_code;
+}
+
+
+static int is_disabled(void)
+{
+ return 0;
+}
+
+static struct kvm_arch_ops svm_arch_ops = {
+ .cpu_has_kvm_support = has_svm,
+ .disabled_by_bios = is_disabled,
+ .hardware_setup = svm_hardware_setup,
+ .hardware_unsetup = svm_hardware_unsetup,
+ .hardware_enable = svm_hardware_enable,
+ .hardware_disable = svm_hardware_disable,
+
+ .vcpu_create = svm_create_vcpu,
+ .vcpu_free = svm_free_vcpu,
+
+ .vcpu_load = svm_vcpu_load,
+ .vcpu_put = svm_vcpu_put,
+
+ .set_guest_debug = svm_guest_debug,
+ .get_msr = svm_get_msr,
+ .set_msr = svm_set_msr,
+ .get_segment_base = svm_get_segment_base,
+ .get_segment = svm_get_segment,
+ .set_segment = svm_set_segment,
+ .is_long_mode = svm_is_long_mode,
+ .get_cs_db_l_bits = svm_get_cs_db_l_bits,
+ .set_cr0 = svm_set_cr0,
+ .set_cr0_no_modeswitch = svm_set_cr0,
+ .set_cr3 = svm_set_cr3,
+ .set_cr4 = svm_set_cr4,
+ .set_efer = svm_set_efer,
+ .get_idt = svm_get_idt,
+ .set_idt = svm_set_idt,
+ .get_gdt = svm_get_gdt,
+ .set_gdt = svm_set_gdt,
+ .get_dr = svm_get_dr,
+ .set_dr = svm_set_dr,
+ .cache_regs = svm_cache_regs,
+ .decache_regs = svm_decache_regs,
+ .get_rflags = svm_get_rflags,
+ .set_rflags = svm_set_rflags,
+
+ .invlpg = svm_invlpg,
+ .tlb_flush = svm_flush_tlb,
+ .inject_page_fault = svm_inject_page_fault,
+
+ .inject_gp = svm_inject_gp,
+
+ .run = svm_vcpu_run,
+ .skip_emulated_instruction = skip_emulated_instruction,
+ .vcpu_setup = svm_vcpu_setup,
+};
+
+static int __init svm_init(void)
+{
+ return kvm_init_arch(&svm_arch_ops, THIS_MODULE);
+}
+
+static void __exit svm_exit(void)
+{
+ kvm_exit_arch();
+}
+
+module_init(svm_init)
+module_exit(svm_exit)
diff --git a/drivers/kvm/svm.h b/drivers/kvm/svm.h
new file mode 100644
index 0000000..df731c3
--- /dev/null
+++ b/drivers/kvm/svm.h
@@ -0,0 +1,315 @@
+#ifndef __SVM_H
+#define __SVM_H
+
+enum {
+ INTERCEPT_INTR,
+ INTERCEPT_NMI,
+ INTERCEPT_SMI,
+ INTERCEPT_INIT,
+ INTERCEPT_VINTR,
+ INTERCEPT_SELECTIVE_CR0,
+ INTERCEPT_STORE_IDTR,
+ INTERCEPT_STORE_GDTR,
+ INTERCEPT_STORE_LDTR,
+ INTERCEPT_STORE_TR,
+ INTERCEPT_LOAD_IDTR,
+ INTERCEPT_LOAD_GDTR,
+ INTERCEPT_LOAD_LDTR,
+ INTERCEPT_LOAD_TR,
+ INTERCEPT_RDTSC,
+ INTERCEPT_RDPMC,
+ INTERCEPT_PUSHF,
+ INTERCEPT_POPF,
+ INTERCEPT_CPUID,
+ INTERCEPT_RSM,
+ INTERCEPT_IRET,
+ INTERCEPT_INTn,
+ INTERCEPT_INVD,
+ INTERCEPT_PAUSE,
+ INTERCEPT_HLT,
+ INTERCEPT_INVLPG,
+ INTERCEPT_INVLPGA,
+ INTERCEPT_IOIO_PROT,
+ INTERCEPT_MSR_PROT,
+ INTERCEPT_TASK_SWITCH,
+ INTERCEPT_FERR_FREEZE,
+ INTERCEPT_SHUTDOWN,
+ INTERCEPT_VMRUN,
+ INTERCEPT_VMMCALL,
+ INTERCEPT_VMLOAD,
+ INTERCEPT_VMSAVE,
+ INTERCEPT_STGI,
+ INTERCEPT_CLGI,
+ INTERCEPT_SKINIT,
+ INTERCEPT_RDTSCP,
+ INTERCEPT_ICEBP,
+ INTERCEPT_WBINVD,
+};
+
+
+struct __attribute__ ((__packed__)) vmcb_control_area {
+ u16 intercept_cr_read;
+ u16 intercept_cr_write;
+ u16 intercept_dr_read;
+ u16 intercept_dr_write;
+ u32 intercept_exceptions;
+ u64 intercept;
+ u8 reserved_1[44];
+ u64 iopm_base_pa;
+ u64 msrpm_base_pa;
+ u64 tsc_offset;
+ u32 asid;
+ u8 tlb_ctl;
+ u8 reserved_2[3];
+ u32 int_ctl;
+ u32 int_vector;
+ u32 int_state;
+ u8 reserved_3[4];
+ u32 exit_code;
+ u32 exit_code_hi;
+ u64 exit_info_1;
+ u64 exit_info_2;
+ u32 exit_int_info;
+ u32 exit_int_info_err;
+ u64 nested_ctl;
+ u8 reserved_4[16];
+ u32 event_inj;
+ u32 event_inj_err;
+ u64 nested_cr3;
+ u64 lbr_ctl;
+ u8 reserved_5[832];
+};
+
+
+#define TLB_CONTROL_DO_NOTHING 0
+#define TLB_CONTROL_FLUSH_ALL_ASID 1
+
+#define V_TPR_MASK 0x0f
+
+#define V_IRQ_SHIFT 8
+#define V_IRQ_MASK (1 << V_IRQ_SHIFT)
+
+#define V_INTR_PRIO_SHIFT 16
+#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT)
+
+#define V_IGN_TPR_SHIFT 20
+#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT)
+
+#define V_INTR_MASKING_SHIFT 24
+#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
+
+#define SVM_INTERRUPT_SHADOW_MASK 1
+
+#define SVM_IOIO_STR_SHIFT 2
+#define SVM_IOIO_REP_SHIFT 3
+#define SVM_IOIO_SIZE_SHIFT 4
+#define SVM_IOIO_ASIZE_SHIFT 7
+
+#define SVM_IOIO_TYPE_MASK 1
+#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT)
+#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT)
+#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
+#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
+
+struct __attribute__ ((__packed__)) vmcb_seg {
+ u16 selector;
+ u16 attrib;
+ u32 limit;
+ u64 base;
+};
+
+struct __attribute__ ((__packed__)) vmcb_save_area {
+ struct vmcb_seg es;
+ struct vmcb_seg cs;
+ struct vmcb_seg ss;
+ struct vmcb_seg ds;
+ struct vmcb_seg fs;
+ struct vmcb_seg gs;
+ struct vmcb_seg gdtr;
+ struct vmcb_seg ldtr;
+ struct vmcb_seg idtr;
+ struct vmcb_seg tr;
+ u8 reserved_1[43];
+ u8 cpl;
+ u8 reserved_2[4];
+ u64 efer;
+ u8 reserved_3[112];
+ u64 cr4;
+ u64 cr3;
+ u64 cr0;
+ u64 dr7;
+ u64 dr6;
+ u64 rflags;
+ u64 rip;
+ u8 reserved_4[88];
+ u64 rsp;
+ u8 reserved_5[24];
+ u64 rax;
+ u64 star;
+ u64 lstar;
+ u64 cstar;
+ u64 sfmask;
+ u64 kernel_gs_base;
+ u64 sysenter_cs;
+ u64 sysenter_esp;
+ u64 sysenter_eip;
+ u64 cr2;
+ u8 reserved_6[32];
+ u64 g_pat;
+ u64 dbgctl;
+ u64 br_from;
+ u64 br_to;
+ u64 last_excp_from;
+ u64 last_excp_to;
+};
+
+struct __attribute__ ((__packed__)) vmcb {
+ struct vmcb_control_area control;
+ struct vmcb_save_area save;
+};
+
+#define SVM_CPUID_FEATURE_SHIFT 2
+#define SVM_CPUID_FUNC 0x8000000a
+
+#define MSR_EFER_SVME_MASK (1ULL << 12)
+#define MSR_VM_HSAVE_PA 0xc0010117ULL
+
+#define SVM_SELECTOR_S_SHIFT 4
+#define SVM_SELECTOR_DPL_SHIFT 5
+#define SVM_SELECTOR_P_SHIFT 7
+#define SVM_SELECTOR_AVL_SHIFT 8
+#define SVM_SELECTOR_L_SHIFT 9
+#define SVM_SELECTOR_DB_SHIFT 10
+#define SVM_SELECTOR_G_SHIFT 11
+
+#define SVM_SELECTOR_TYPE_MASK (0xf)
+#define SVM_SELECTOR_S_MASK (1 << SVM_SELECTOR_S_SHIFT)
+#define SVM_SELECTOR_DPL_MASK (3 << SVM_SELECTOR_DPL_SHIFT)
+#define SVM_SELECTOR_P_MASK (1 << SVM_SELECTOR_P_SHIFT)
+#define SVM_SELECTOR_AVL_MASK (1 << SVM_SELECTOR_AVL_SHIFT)
+#define SVM_SELECTOR_L_MASK (1 << SVM_SELECTOR_L_SHIFT)
+#define SVM_SELECTOR_DB_MASK (1 << SVM_SELECTOR_DB_SHIFT)
+#define SVM_SELECTOR_G_MASK (1 << SVM_SELECTOR_G_SHIFT)
+
+#define SVM_SELECTOR_WRITE_MASK (1 << 1)
+#define SVM_SELECTOR_READ_MASK SVM_SELECTOR_WRITE_MASK
+#define SVM_SELECTOR_CODE_MASK (1 << 3)
+
+#define INTERCEPT_CR0_MASK 1
+#define INTERCEPT_CR3_MASK (1 << 3)
+#define INTERCEPT_CR4_MASK (1 << 4)
+
+#define INTERCEPT_DR0_MASK 1
+#define INTERCEPT_DR1_MASK (1 << 1)
+#define INTERCEPT_DR2_MASK (1 << 2)
+#define INTERCEPT_DR3_MASK (1 << 3)
+#define INTERCEPT_DR4_MASK (1 << 4)
+#define INTERCEPT_DR5_MASK (1 << 5)
+#define INTERCEPT_DR6_MASK (1 << 6)
+#define INTERCEPT_DR7_MASK (1 << 7)
+
+#define SVM_EVTINJ_VEC_MASK 0xff
+
+#define SVM_EVTINJ_TYPE_SHIFT 8
+#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_VALID (1 << 31)
+#define SVM_EVTINJ_VALID_ERR (1 << 11)
+
+#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK
+
+#define SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR
+#define SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI
+#define SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT
+#define SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT
+
+#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID
+#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR
+
+#define SVM_EXIT_READ_CR0 0x000
+#define SVM_EXIT_READ_CR3 0x003
+#define SVM_EXIT_READ_CR4 0x004
+#define SVM_EXIT_READ_CR8 0x008
+#define SVM_EXIT_WRITE_CR0 0x010
+#define SVM_EXIT_WRITE_CR3 0x013
+#define SVM_EXIT_WRITE_CR4 0x014
+#define SVM_EXIT_WRITE_CR8 0x018
+#define SVM_EXIT_READ_DR0 0x020
+#define SVM_EXIT_READ_DR1 0x021
+#define SVM_EXIT_READ_DR2 0x022
+#define SVM_EXIT_READ_DR3 0x023
+#define SVM_EXIT_READ_DR4 0x024
+#define SVM_EXIT_READ_DR5 0x025
+#define SVM_EXIT_READ_DR6 0x026
+#define SVM_EXIT_READ_DR7 0x027
+#define SVM_EXIT_WRITE_DR0 0x030
+#define SVM_EXIT_WRITE_DR1 0x031
+#define SVM_EXIT_WRITE_DR2 0x032
+#define SVM_EXIT_WRITE_DR3 0x033
+#define SVM_EXIT_WRITE_DR4 0x034
+#define SVM_EXIT_WRITE_DR5 0x035
+#define SVM_EXIT_WRITE_DR6 0x036
+#define SVM_EXIT_WRITE_DR7 0x037
+#define SVM_EXIT_EXCP_BASE 0x040
+#define SVM_EXIT_INTR 0x060
+#define SVM_EXIT_NMI 0x061
+#define SVM_EXIT_SMI 0x062
+#define SVM_EXIT_INIT 0x063
+#define SVM_EXIT_VINTR 0x064
+#define SVM_EXIT_CR0_SEL_WRITE 0x065
+#define SVM_EXIT_IDTR_READ 0x066
+#define SVM_EXIT_GDTR_READ 0x067
+#define SVM_EXIT_LDTR_READ 0x068
+#define SVM_EXIT_TR_READ 0x069
+#define SVM_EXIT_IDTR_WRITE 0x06a
+#define SVM_EXIT_GDTR_WRITE 0x06b
+#define SVM_EXIT_LDTR_WRITE 0x06c
+#define SVM_EXIT_TR_WRITE 0x06d
+#define SVM_EXIT_RDTSC 0x06e
+#define SVM_EXIT_RDPMC 0x06f
+#define SVM_EXIT_PUSHF 0x070
+#define SVM_EXIT_POPF 0x071
+#define SVM_EXIT_CPUID 0x072
+#define SVM_EXIT_RSM 0x073
+#define SVM_EXIT_IRET 0x074
+#define SVM_EXIT_SWINT 0x075
+#define SVM_EXIT_INVD 0x076
+#define SVM_EXIT_PAUSE 0x077
+#define SVM_EXIT_HLT 0x078
+#define SVM_EXIT_INVLPG 0x079
+#define SVM_EXIT_INVLPGA 0x07a
+#define SVM_EXIT_IOIO 0x07b
+#define SVM_EXIT_MSR 0x07c
+#define SVM_EXIT_TASK_SWITCH 0x07d
+#define SVM_EXIT_FERR_FREEZE 0x07e
+#define SVM_EXIT_SHUTDOWN 0x07f
+#define SVM_EXIT_VMRUN 0x080
+#define SVM_EXIT_VMMCALL 0x081
+#define SVM_EXIT_VMLOAD 0x082
+#define SVM_EXIT_VMSAVE 0x083
+#define SVM_EXIT_STGI 0x084
+#define SVM_EXIT_CLGI 0x085
+#define SVM_EXIT_SKINIT 0x086
+#define SVM_EXIT_RDTSCP 0x087
+#define SVM_EXIT_ICEBP 0x088
+#define SVM_EXIT_WBINVD 0x089
+#define SVM_EXIT_NPF 0x400
+
+#define SVM_EXIT_ERR -1
+
+#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) // TS and MP
+
+#define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda"
+#define SVM_VMRUN ".byte 0x0f, 0x01, 0xd8"
+#define SVM_VMSAVE ".byte 0x0f, 0x01, 0xdb"
+#define SVM_CLGI ".byte 0x0f, 0x01, 0xdd"
+#define SVM_STGI ".byte 0x0f, 0x01, 0xdc"
+#define SVM_INVLPGA ".byte 0x0f, 0x01, 0xdf"
+
+#endif
+
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c
new file mode 100644
index 0000000..f0f0b1a
--- /dev/null
+++ b/drivers/kvm/vmx.c
@@ -0,0 +1,2014 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables machines with Intel VT-x extensions to run virtual
+ * machines without emulation or binary translation.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "kvm.h"
+#include "vmx.h"
+#include "kvm_vmx.h"
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/io.h>
+#include <asm/desc.h>
+
+#include "segment_descriptor.h"
+
+#define MSR_IA32_FEATURE_CONTROL 0x03a
+
+MODULE_AUTHOR("Qumranet");
+MODULE_LICENSE("GPL");
+
+static DEFINE_PER_CPU(struct vmcs *, vmxarea);
+static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
+
+#ifdef CONFIG_X86_64
+#define HOST_IS_64 1
+#else
+#define HOST_IS_64 0
+#endif
+
+static struct vmcs_descriptor {
+ int size;
+ int order;
+ u32 revision_id;
+} vmcs_descriptor;
+
+#define VMX_SEGMENT_FIELD(seg) \
+ [VCPU_SREG_##seg] = { \
+ .selector = GUEST_##seg##_SELECTOR, \
+ .base = GUEST_##seg##_BASE, \
+ .limit = GUEST_##seg##_LIMIT, \
+ .ar_bytes = GUEST_##seg##_AR_BYTES, \
+ }
+
+static struct kvm_vmx_segment_field {
+ unsigned selector;
+ unsigned base;
+ unsigned limit;
+ unsigned ar_bytes;
+} kvm_vmx_segment_fields[] = {
+ VMX_SEGMENT_FIELD(CS),
+ VMX_SEGMENT_FIELD(DS),
+ VMX_SEGMENT_FIELD(ES),
+ VMX_SEGMENT_FIELD(FS),
+ VMX_SEGMENT_FIELD(GS),
+ VMX_SEGMENT_FIELD(SS),
+ VMX_SEGMENT_FIELD(TR),
+ VMX_SEGMENT_FIELD(LDTR),
+};
+
+static const u32 vmx_msr_index[] = {
+#ifdef CONFIG_X86_64
+ MSR_SYSCALL_MASK, MSR_LSTAR, MSR_CSTAR, MSR_KERNEL_GS_BASE,
+#endif
+ MSR_EFER, MSR_K6_STAR,
+};
+#define NR_VMX_MSR (sizeof(vmx_msr_index) / sizeof(*vmx_msr_index))
+
+static inline int is_page_fault(u32 intr_info)
+{
+ return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK |
+ INTR_INFO_VALID_MASK)) ==
+ (INTR_TYPE_EXCEPTION | PF_VECTOR | INTR_INFO_VALID_MASK);
+}
+
+static inline int is_external_interrupt(u32 intr_info)
+{
+ return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK))
+ == (INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK);
+}
+
+static struct vmx_msr_entry *find_msr_entry(struct kvm_vcpu *vcpu, u32 msr)
+{
+ int i;
+
+ for (i = 0; i < vcpu->nmsrs; ++i)
+ if (vcpu->guest_msrs[i].index == msr)
+ return &vcpu->guest_msrs[i];
+ return 0;
+}
+
+static void vmcs_clear(struct vmcs *vmcs)
+{
+ u64 phys_addr = __pa(vmcs);
+ u8 error;
+
+ asm volatile (ASM_VMX_VMCLEAR_RAX "; setna %0"
+ : "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
+ : "cc", "memory");
+ if (error)
+ printk(KERN_ERR "kvm: vmclear fail: %p/%llx\n",
+ vmcs, phys_addr);
+}
+
+static void __vcpu_clear(void *arg)
+{
+ struct kvm_vcpu *vcpu = arg;
+ int cpu = smp_processor_id();
+
+ if (vcpu->cpu == cpu)
+ vmcs_clear(vcpu->vmcs);
+ if (per_cpu(current_vmcs, cpu) == vcpu->vmcs)
+ per_cpu(current_vmcs, cpu) = NULL;
+}
+
+static unsigned long vmcs_readl(unsigned long field)
+{
+ unsigned long value;
+
+ asm volatile (ASM_VMX_VMREAD_RDX_RAX
+ : "=a"(value) : "d"(field) : "cc");
+ return value;
+}
+
+static u16 vmcs_read16(unsigned long field)
+{
+ return vmcs_readl(field);
+}
+
+static u32 vmcs_read32(unsigned long field)
+{
+ return vmcs_readl(field);
+}
+
+static u64 vmcs_read64(unsigned long field)
+{
+#ifdef CONFIG_X86_64
+ return vmcs_readl(field);
+#else
+ return vmcs_readl(field) | ((u64)vmcs_readl(field+1) << 32);
+#endif
+}
+
+static void vmcs_writel(unsigned long field, unsigned long value)
+{
+ u8 error;
+
+ asm volatile (ASM_VMX_VMWRITE_RAX_RDX "; setna %0"
+ : "=q"(error) : "a"(value), "d"(field) : "cc" );
+ if (error)
+ printk(KERN_ERR "vmwrite error: reg %lx value %lx (err %d)\n",
+ field, value, vmcs_read32(VM_INSTRUCTION_ERROR));
+}
+
+static void vmcs_write16(unsigned long field, u16 value)
+{
+ vmcs_writel(field, value);
+}
+
+static void vmcs_write32(unsigned long field, u32 value)
+{
+ vmcs_writel(field, value);
+}
+
+static void vmcs_write64(unsigned long field, u64 value)
+{
+#ifdef CONFIG_X86_64
+ vmcs_writel(field, value);
+#else
+ vmcs_writel(field, value);
+ asm volatile ("");
+ vmcs_writel(field+1, value >> 32);
+#endif
+}
+
+/*
+ * Switches to specified vcpu, until a matching vcpu_put(), but assumes
+ * vcpu mutex is already taken.
+ */
+static struct kvm_vcpu *vmx_vcpu_load(struct kvm_vcpu *vcpu)
+{
+ u64 phys_addr = __pa(vcpu->vmcs);
+ int cpu;
+
+ cpu = get_cpu();
+
+ if (vcpu->cpu != cpu) {
+ smp_call_function(__vcpu_clear, vcpu, 0, 1);
+ vcpu->launched = 0;
+ }
+
+ if (per_cpu(current_vmcs, cpu) != vcpu->vmcs) {
+ u8 error;
+
+ per_cpu(current_vmcs, cpu) = vcpu->vmcs;
+ asm volatile (ASM_VMX_VMPTRLD_RAX "; setna %0"
+ : "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
+ : "cc");
+ if (error)
+ printk(KERN_ERR "kvm: vmptrld %p/%llx fail\n",
+ vcpu->vmcs, phys_addr);
+ }
+
+ if (vcpu->cpu != cpu) {
+ struct descriptor_table dt;
+ unsigned long sysenter_esp;
+
+ vcpu->cpu = cpu;
+ /*
+ * Linux uses per-cpu TSS and GDT, so set these when switching
+ * processors.
+ */
+ vmcs_writel(HOST_TR_BASE, read_tr_base()); /* 22.2.4 */
+ get_gdt(&dt);
+ vmcs_writel(HOST_GDTR_BASE, dt.base); /* 22.2.4 */
+
+ rdmsrl(MSR_IA32_SYSENTER_ESP, sysenter_esp);
+ vmcs_writel(HOST_IA32_SYSENTER_ESP, sysenter_esp); /* 22.2.3 */
+ }
+ return vcpu;
+}
+
+static void vmx_vcpu_put(struct kvm_vcpu *vcpu)
+{
+ put_cpu();
+}
+
+static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu)
+{
+ return vmcs_readl(GUEST_RFLAGS);
+}
+
+static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
+{
+ vmcs_writel(GUEST_RFLAGS, rflags);
+}
+
+static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
+{
+ unsigned long rip;
+ u32 interruptibility;
+
+ rip = vmcs_readl(GUEST_RIP);
+ rip += vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
+ vmcs_writel(GUEST_RIP, rip);
+
+ /*
+ * We emulated an instruction, so temporary interrupt blocking
+ * should be removed, if set.
+ */
+ interruptibility = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO);
+ if (interruptibility & 3)
+ vmcs_write32(GUEST_INTERRUPTIBILITY_INFO,
+ interruptibility & ~3);
+}
+
+static void vmx_inject_gp(struct kvm_vcpu *vcpu, unsigned error_code)
+{
+ printk(KERN_DEBUG "inject_general_protection: rip 0x%lx\n",
+ vmcs_readl(GUEST_RIP));
+ vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ GP_VECTOR |
+ INTR_TYPE_EXCEPTION |
+ INTR_INFO_DELIEVER_CODE_MASK |
+ INTR_INFO_VALID_MASK);
+}
+
+/*
+ * reads and returns guest's timestamp counter "register"
+ * guest_tsc = host_tsc + tsc_offset -- 21.3
+ */
+static u64 guest_read_tsc(void)
+{
+ u64 host_tsc, tsc_offset;
+
+ rdtscll(host_tsc);
+ tsc_offset = vmcs_read64(TSC_OFFSET);
+ return host_tsc + tsc_offset;
+}
+
+/*
+ * writes 'guest_tsc' into guest's timestamp counter "register"
+ * guest_tsc = host_tsc + tsc_offset ==> tsc_offset = guest_tsc - host_tsc
+ */
+static void guest_write_tsc(u64 guest_tsc)
+{
+ u64 host_tsc;
+
+ rdtscll(host_tsc);
+ vmcs_write64(TSC_OFFSET, guest_tsc - host_tsc);
+}
+
+static void reload_tss(void)
+{
+#ifndef CONFIG_X86_64
+
+ /*
+ * VT restores TR but not its size. Useless.
+ */
+ struct descriptor_table gdt;
+ struct segment_descriptor *descs;
+
+ get_gdt(&gdt);
+ descs = (void *)gdt.base;
+ descs[GDT_ENTRY_TSS].type = 9; /* available TSS */
+ load_TR_desc();
+#endif
+}
+
+/*
+ * Reads an msr value (of 'msr_index') into 'pdata'.
+ * Returns 0 on success, non-0 otherwise.
+ * Assumes vcpu_load() was already called.
+ */
+static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
+{
+ u64 data;
+ struct vmx_msr_entry *msr;
+
+ if (!pdata) {
+ printk(KERN_ERR "BUG: get_msr called with NULL pdata\n");
+ return -EINVAL;
+ }
+
+ switch (msr_index) {
+#ifdef CONFIG_X86_64
+ case MSR_FS_BASE:
+ data = vmcs_readl(GUEST_FS_BASE);
+ break;
+ case MSR_GS_BASE:
+ data = vmcs_readl(GUEST_GS_BASE);
+ break;
+ case MSR_EFER:
+ data = vcpu->shadow_efer;
+ break;
+#endif
+ case MSR_IA32_TIME_STAMP_COUNTER:
+ data = guest_read_tsc();
+ break;
+ case MSR_IA32_SYSENTER_CS:
+ data = vmcs_read32(GUEST_SYSENTER_CS);
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ data = vmcs_read32(GUEST_SYSENTER_EIP);
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ data = vmcs_read32(GUEST_SYSENTER_ESP);
+ break;
+ case MSR_IA32_MC0_CTL:
+ case MSR_IA32_MCG_STATUS:
+ case MSR_IA32_MCG_CAP:
+ case MSR_IA32_MC0_MISC:
+ case MSR_IA32_MC0_MISC+4:
+ case MSR_IA32_MC0_MISC+8:
+ case MSR_IA32_MC0_MISC+12:
+ case MSR_IA32_MC0_MISC+16:
+ case MSR_IA32_UCODE_REV:
+ /* MTRR registers */
+ case 0xfe:
+ case 0x200 ... 0x2ff:
+ data = 0;
+ break;
+ case MSR_IA32_APICBASE:
+ data = vcpu->apic_base;
+ break;
+ default:
+ msr = find_msr_entry(vcpu, msr_index);
+ if (!msr) {
+ printk(KERN_ERR "kvm: unhandled rdmsr: %x\n", msr_index);
+ return 1;
+ }
+ data = msr->data;
+ break;
+ }
+
+ *pdata = data;
+ return 0;
+}
+
+/*
+ * Writes msr value into into the appropriate "register".
+ * Returns 0 on success, non-0 otherwise.
+ * Assumes vcpu_load() was already called.
+ */
+static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
+{
+ struct vmx_msr_entry *msr;
+ switch (msr_index) {
+#ifdef CONFIG_X86_64
+ case MSR_FS_BASE:
+ vmcs_writel(GUEST_FS_BASE, data);
+ break;
+ case MSR_GS_BASE:
+ vmcs_writel(GUEST_GS_BASE, data);
+ break;
+#endif
+ case MSR_IA32_SYSENTER_CS:
+ vmcs_write32(GUEST_SYSENTER_CS, data);
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ vmcs_write32(GUEST_SYSENTER_EIP, data);
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ vmcs_write32(GUEST_SYSENTER_ESP, data);
+ break;
+#ifdef __x86_64
+ case MSR_EFER:
+ set_efer(vcpu, data);
+ break;
+ case MSR_IA32_MC0_STATUS:
+ printk(KERN_WARNING "%s: MSR_IA32_MC0_STATUS 0x%llx, nop\n"
+ , __FUNCTION__, data);
+ break;
+#endif
+ case MSR_IA32_TIME_STAMP_COUNTER: {
+ guest_write_tsc(data);
+ break;
+ }
+ case MSR_IA32_UCODE_REV:
+ case MSR_IA32_UCODE_WRITE:
+ case 0x200 ... 0x2ff: /* MTRRs */
+ break;
+ case MSR_IA32_APICBASE:
+ vcpu->apic_base = data;
+ break;
+ default:
+ msr = find_msr_entry(vcpu, msr_index);
+ if (!msr) {
+ printk(KERN_ERR "kvm: unhandled wrmsr: 0x%x\n", msr_index);
+ return 1;
+ }
+ msr->data = data;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Sync the rsp and rip registers into the vcpu structure. This allows
+ * registers to be accessed by indexing vcpu->regs.
+ */
+static void vcpu_load_rsp_rip(struct kvm_vcpu *vcpu)
+{
+ vcpu->regs[VCPU_REGS_RSP] = vmcs_readl(GUEST_RSP);
+ vcpu->rip = vmcs_readl(GUEST_RIP);
+}
+
+/*
+ * Syncs rsp and rip back into the vmcs. Should be called after possible
+ * modification.
+ */
+static void vcpu_put_rsp_rip(struct kvm_vcpu *vcpu)
+{
+ vmcs_writel(GUEST_RSP, vcpu->regs[VCPU_REGS_RSP]);
+ vmcs_writel(GUEST_RIP, vcpu->rip);
+}
+
+static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
+{
+ unsigned long dr7 = 0x400;
+ u32 exception_bitmap;
+ int old_singlestep;
+
+ exception_bitmap = vmcs_read32(EXCEPTION_BITMAP);
+ old_singlestep = vcpu->guest_debug.singlestep;
+
+ vcpu->guest_debug.enabled = dbg->enabled;
+ if (vcpu->guest_debug.enabled) {
+ int i;
+
+ dr7 |= 0x200; /* exact */
+ for (i = 0; i < 4; ++i) {
+ if (!dbg->breakpoints[i].enabled)
+ continue;
+ vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address;
+ dr7 |= 2 << (i*2); /* global enable */
+ dr7 |= 0 << (i*4+16); /* execution breakpoint */
+ }
+
+ exception_bitmap |= (1u << 1); /* Trap debug exceptions */
+
+ vcpu->guest_debug.singlestep = dbg->singlestep;
+ } else {
+ exception_bitmap &= ~(1u << 1); /* Ignore debug exceptions */
+ vcpu->guest_debug.singlestep = 0;
+ }
+
+ if (old_singlestep && !vcpu->guest_debug.singlestep) {
+ unsigned long flags;
+
+ flags = vmcs_readl(GUEST_RFLAGS);
+ flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
+ vmcs_writel(GUEST_RFLAGS, flags);
+ }
+
+ vmcs_write32(EXCEPTION_BITMAP, exception_bitmap);
+ vmcs_writel(GUEST_DR7, dr7);
+
+ return 0;
+}
+
+static __init int cpu_has_kvm_support(void)
+{
+ unsigned long ecx = cpuid_ecx(1);
+ return test_bit(5, &ecx); /* CPUID.1:ECX.VMX[bit 5] -> VT */
+}
+
+static __init int vmx_disabled_by_bios(void)
+{
+ u64 msr;
+
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, msr);
+ return (msr & 5) == 1; /* locked but not enabled */
+}
+
+static __init void hardware_enable(void *garbage)
+{
+ int cpu = raw_smp_processor_id();
+ u64 phys_addr = __pa(per_cpu(vmxarea, cpu));
+ u64 old;
+
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, old);
+ if ((old & 5) != 5)
+ /* enable and lock */
+ wrmsrl(MSR_IA32_FEATURE_CONTROL, old | 5);
+ write_cr4(read_cr4() | CR4_VMXE); /* FIXME: not cpu hotplug safe */
+ asm volatile (ASM_VMX_VMXON_RAX : : "a"(&phys_addr), "m"(phys_addr)
+ : "memory", "cc");
+}
+
+static void hardware_disable(void *garbage)
+{
+ asm volatile (ASM_VMX_VMXOFF : : : "cc");
+}
+
+static __init void setup_vmcs_descriptor(void)
+{
+ u32 vmx_msr_low, vmx_msr_high;
+
+ rdmsr(MSR_IA32_VMX_BASIC_MSR, vmx_msr_low, vmx_msr_high);
+ vmcs_descriptor.size = vmx_msr_high & 0x1fff;
+ vmcs_descriptor.order = get_order(vmcs_descriptor.size);
+ vmcs_descriptor.revision_id = vmx_msr_low;
+};
+
+static struct vmcs *alloc_vmcs_cpu(int cpu)
+{
+ int node = cpu_to_node(cpu);
+ struct page *pages;
+ struct vmcs *vmcs;
+
+ pages = alloc_pages_node(node, GFP_KERNEL, vmcs_descriptor.order);
+ if (!pages)
+ return NULL;
+ vmcs = page_address(pages);
+ memset(vmcs, 0, vmcs_descriptor.size);
+ vmcs->revision_id = vmcs_descriptor.revision_id; /* vmcs revision id */
+ return vmcs;
+}
+
+static struct vmcs *alloc_vmcs(void)
+{
+ return alloc_vmcs_cpu(smp_processor_id());
+}
+
+static void free_vmcs(struct vmcs *vmcs)
+{
+ free_pages((unsigned long)vmcs, vmcs_descriptor.order);
+}
+
+static __exit void free_kvm_area(void)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu)
+ free_vmcs(per_cpu(vmxarea, cpu));
+}
+
+extern struct vmcs *alloc_vmcs_cpu(int cpu);
+
+static __init int alloc_kvm_area(void)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ struct vmcs *vmcs;
+
+ vmcs = alloc_vmcs_cpu(cpu);
+ if (!vmcs) {
+ free_kvm_area();
+ return -ENOMEM;
+ }
+
+ per_cpu(vmxarea, cpu) = vmcs;
+ }
+ return 0;
+}
+
+static __init int hardware_setup(void)
+{
+ setup_vmcs_descriptor();
+ return alloc_kvm_area();
+}
+
+static __exit void hardware_unsetup(void)
+{
+ free_kvm_area();
+}
+
+static void update_exception_bitmap(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->rmode.active)
+ vmcs_write32(EXCEPTION_BITMAP, ~0);
+ else
+ vmcs_write32(EXCEPTION_BITMAP, 1 << PF_VECTOR);
+}
+
+static void fix_pmode_dataseg(int seg, struct kvm_save_segment *save)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+
+ if (vmcs_readl(sf->base) == save->base) {
+ vmcs_write16(sf->selector, save->selector);
+ vmcs_writel(sf->base, save->base);
+ vmcs_write32(sf->limit, save->limit);
+ vmcs_write32(sf->ar_bytes, save->ar);
+ } else {
+ u32 dpl = (vmcs_read16(sf->selector) & SELECTOR_RPL_MASK)
+ << AR_DPL_SHIFT;
+ vmcs_write32(sf->ar_bytes, 0x93 | dpl);
+ }
+}
+
+static void enter_pmode(struct kvm_vcpu *vcpu)
+{
+ unsigned long flags;
+
+ vcpu->rmode.active = 0;
+
+ vmcs_writel(GUEST_TR_BASE, vcpu->rmode.tr.base);
+ vmcs_write32(GUEST_TR_LIMIT, vcpu->rmode.tr.limit);
+ vmcs_write32(GUEST_TR_AR_BYTES, vcpu->rmode.tr.ar);
+
+ flags = vmcs_readl(GUEST_RFLAGS);
+ flags &= ~(IOPL_MASK | X86_EFLAGS_VM);
+ flags |= (vcpu->rmode.save_iopl << IOPL_SHIFT);
+ vmcs_writel(GUEST_RFLAGS, flags);
+
+ vmcs_writel(GUEST_CR4, (vmcs_readl(GUEST_CR4) & ~CR4_VME_MASK) |
+ (vmcs_readl(CR4_READ_SHADOW) & CR4_VME_MASK));
+
+ update_exception_bitmap(vcpu);
+
+ fix_pmode_dataseg(VCPU_SREG_ES, &vcpu->rmode.es);
+ fix_pmode_dataseg(VCPU_SREG_DS, &vcpu->rmode.ds);
+ fix_pmode_dataseg(VCPU_SREG_GS, &vcpu->rmode.gs);
+ fix_pmode_dataseg(VCPU_SREG_FS, &vcpu->rmode.fs);
+
+ vmcs_write16(GUEST_SS_SELECTOR, 0);
+ vmcs_write32(GUEST_SS_AR_BYTES, 0x93);
+
+ vmcs_write16(GUEST_CS_SELECTOR,
+ vmcs_read16(GUEST_CS_SELECTOR) & ~SELECTOR_RPL_MASK);
+ vmcs_write32(GUEST_CS_AR_BYTES, 0x9b);
+}
+
+static int rmode_tss_base(struct kvm* kvm)
+{
+ gfn_t base_gfn = kvm->memslots[0].base_gfn + kvm->memslots[0].npages - 3;
+ return base_gfn << PAGE_SHIFT;
+}
+
+static void fix_rmode_seg(int seg, struct kvm_save_segment *save)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+
+ save->selector = vmcs_read16(sf->selector);
+ save->base = vmcs_readl(sf->base);
+ save->limit = vmcs_read32(sf->limit);
+ save->ar = vmcs_read32(sf->ar_bytes);
+ vmcs_write16(sf->selector, vmcs_readl(sf->base) >> 4);
+ vmcs_write32(sf->limit, 0xffff);
+ vmcs_write32(sf->ar_bytes, 0xf3);
+}
+
+static void enter_rmode(struct kvm_vcpu *vcpu)
+{
+ unsigned long flags;
+
+ vcpu->rmode.active = 1;
+
+ vcpu->rmode.tr.base = vmcs_readl(GUEST_TR_BASE);
+ vmcs_writel(GUEST_TR_BASE, rmode_tss_base(vcpu->kvm));
+
+ vcpu->rmode.tr.limit = vmcs_read32(GUEST_TR_LIMIT);
+ vmcs_write32(GUEST_TR_LIMIT, RMODE_TSS_SIZE - 1);
+
+ vcpu->rmode.tr.ar = vmcs_read32(GUEST_TR_AR_BYTES);
+ vmcs_write32(GUEST_TR_AR_BYTES, 0x008b);
+
+ flags = vmcs_readl(GUEST_RFLAGS);
+ vcpu->rmode.save_iopl = (flags & IOPL_MASK) >> IOPL_SHIFT;
+
+ flags |= IOPL_MASK | X86_EFLAGS_VM;
+
+ vmcs_writel(GUEST_RFLAGS, flags);
+ vmcs_writel(GUEST_CR4, vmcs_readl(GUEST_CR4) | CR4_VME_MASK);
+ update_exception_bitmap(vcpu);
+
+ vmcs_write16(GUEST_SS_SELECTOR, vmcs_readl(GUEST_SS_BASE) >> 4);
+ vmcs_write32(GUEST_SS_LIMIT, 0xffff);
+ vmcs_write32(GUEST_SS_AR_BYTES, 0xf3);
+
+ vmcs_write32(GUEST_CS_AR_BYTES, 0xf3);
+ vmcs_write16(GUEST_CS_SELECTOR, vmcs_readl(GUEST_CS_BASE) >> 4);
+
+ fix_rmode_seg(VCPU_SREG_ES, &vcpu->rmode.es);
+ fix_rmode_seg(VCPU_SREG_DS, &vcpu->rmode.ds);
+ fix_rmode_seg(VCPU_SREG_GS, &vcpu->rmode.gs);
+ fix_rmode_seg(VCPU_SREG_FS, &vcpu->rmode.fs);
+}
+
+#ifdef CONFIG_X86_64
+
+static void enter_lmode(struct kvm_vcpu *vcpu)
+{
+ u32 guest_tr_ar;
+
+ guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES);
+ if ((guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) {
+ printk(KERN_DEBUG "%s: tss fixup for long mode. \n",
+ __FUNCTION__);
+ vmcs_write32(GUEST_TR_AR_BYTES,
+ (guest_tr_ar & ~AR_TYPE_MASK)
+ | AR_TYPE_BUSY_64_TSS);
+ }
+
+ vcpu->shadow_efer |= EFER_LMA;
+
+ find_msr_entry(vcpu, MSR_EFER)->data |= EFER_LMA | EFER_LME;
+ vmcs_write32(VM_ENTRY_CONTROLS,
+ vmcs_read32(VM_ENTRY_CONTROLS)
+ | VM_ENTRY_CONTROLS_IA32E_MASK);
+}
+
+static void exit_lmode(struct kvm_vcpu *vcpu)
+{
+ vcpu->shadow_efer &= ~EFER_LMA;
+
+ vmcs_write32(VM_ENTRY_CONTROLS,
+ vmcs_read32(VM_ENTRY_CONTROLS)
+ & ~VM_ENTRY_CONTROLS_IA32E_MASK);
+}
+
+#endif
+
+static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
+{
+ if (vcpu->rmode.active && (cr0 & CR0_PE_MASK))
+ enter_pmode(vcpu);
+
+ if (!vcpu->rmode.active && !(cr0 & CR0_PE_MASK))
+ enter_rmode(vcpu);
+
+#ifdef CONFIG_X86_64
+ if (vcpu->shadow_efer & EFER_LME) {
+ if (!is_paging(vcpu) && (cr0 & CR0_PG_MASK))
+ enter_lmode(vcpu);
+ if (is_paging(vcpu) && !(cr0 & CR0_PG_MASK))
+ exit_lmode(vcpu);
+ }
+#endif
+
+ vmcs_writel(CR0_READ_SHADOW, cr0);
+ vmcs_writel(GUEST_CR0,
+ (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON);
+ vcpu->cr0 = cr0;
+}
+
+/*
+ * Used when restoring the VM to avoid corrupting segment registers
+ */
+static void vmx_set_cr0_no_modeswitch(struct kvm_vcpu *vcpu, unsigned long cr0)
+{
+ vcpu->rmode.active = ((cr0 & CR0_PE_MASK) == 0);
+ update_exception_bitmap(vcpu);
+ vmcs_writel(CR0_READ_SHADOW, cr0);
+ vmcs_writel(GUEST_CR0,
+ (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON);
+ vcpu->cr0 = cr0;
+}
+
+static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
+{
+ vmcs_writel(GUEST_CR3, cr3);
+}
+
+static void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
+{
+ vmcs_writel(CR4_READ_SHADOW, cr4);
+ vmcs_writel(GUEST_CR4, cr4 | (vcpu->rmode.active ?
+ KVM_RMODE_VM_CR4_ALWAYS_ON : KVM_PMODE_VM_CR4_ALWAYS_ON));
+ vcpu->cr4 = cr4;
+}
+
+#ifdef CONFIG_X86_64
+
+static void vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
+{
+ struct vmx_msr_entry *msr = find_msr_entry(vcpu, MSR_EFER);
+
+ vcpu->shadow_efer = efer;
+ if (efer & EFER_LMA) {
+ vmcs_write32(VM_ENTRY_CONTROLS,
+ vmcs_read32(VM_ENTRY_CONTROLS) |
+ VM_ENTRY_CONTROLS_IA32E_MASK);
+ msr->data = efer;
+
+ } else {
+ vmcs_write32(VM_ENTRY_CONTROLS,
+ vmcs_read32(VM_ENTRY_CONTROLS) &
+ ~VM_ENTRY_CONTROLS_IA32E_MASK);
+
+ msr->data = efer & ~EFER_LME;
+ }
+}
+
+#endif
+
+static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+
+ return vmcs_readl(sf->base);
+}
+
+static void vmx_get_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+ u32 ar;
+
+ var->base = vmcs_readl(sf->base);
+ var->limit = vmcs_read32(sf->limit);
+ var->selector = vmcs_read16(sf->selector);
+ ar = vmcs_read32(sf->ar_bytes);
+ if (ar & AR_UNUSABLE_MASK)
+ ar = 0;
+ var->type = ar & 15;
+ var->s = (ar >> 4) & 1;
+ var->dpl = (ar >> 5) & 3;
+ var->present = (ar >> 7) & 1;
+ var->avl = (ar >> 12) & 1;
+ var->l = (ar >> 13) & 1;
+ var->db = (ar >> 14) & 1;
+ var->g = (ar >> 15) & 1;
+ var->unusable = (ar >> 16) & 1;
+}
+
+static void vmx_set_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+ u32 ar;
+
+ vmcs_writel(sf->base, var->base);
+ vmcs_write32(sf->limit, var->limit);
+ vmcs_write16(sf->selector, var->selector);
+ if (var->unusable)
+ ar = 1 << 16;
+ else {
+ ar = var->type & 15;
+ ar |= (var->s & 1) << 4;
+ ar |= (var->dpl & 3) << 5;
+ ar |= (var->present & 1) << 7;
+ ar |= (var->avl & 1) << 12;
+ ar |= (var->l & 1) << 13;
+ ar |= (var->db & 1) << 14;
+ ar |= (var->g & 1) << 15;
+ }
+ if (ar == 0) /* a 0 value means unusable */
+ ar = AR_UNUSABLE_MASK;
+ vmcs_write32(sf->ar_bytes, ar);
+}
+
+static int vmx_is_long_mode(struct kvm_vcpu *vcpu)
+{
+ return vmcs_read32(VM_ENTRY_CONTROLS) & VM_ENTRY_CONTROLS_IA32E_MASK;
+}
+
+static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l)
+{
+ u32 ar = vmcs_read32(GUEST_CS_AR_BYTES);
+
+ *db = (ar >> 14) & 1;
+ *l = (ar >> 13) & 1;
+}
+
+static void vmx_get_idt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ dt->limit = vmcs_read32(GUEST_IDTR_LIMIT);
+ dt->base = vmcs_readl(GUEST_IDTR_BASE);
+}
+
+static void vmx_set_idt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ vmcs_write32(GUEST_IDTR_LIMIT, dt->limit);
+ vmcs_writel(GUEST_IDTR_BASE, dt->base);
+}
+
+static void vmx_get_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ dt->limit = vmcs_read32(GUEST_GDTR_LIMIT);
+ dt->base = vmcs_readl(GUEST_GDTR_BASE);
+}
+
+static void vmx_set_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
+{
+ vmcs_write32(GUEST_GDTR_LIMIT, dt->limit);
+ vmcs_writel(GUEST_GDTR_BASE, dt->base);
+}
+
+static int init_rmode_tss(struct kvm* kvm)
+{
+ struct page *p1, *p2, *p3;
+ gfn_t fn = rmode_tss_base(kvm) >> PAGE_SHIFT;
+ char *page;
+
+ p1 = _gfn_to_page(kvm, fn++);
+ p2 = _gfn_to_page(kvm, fn++);
+ p3 = _gfn_to_page(kvm, fn);
+
+ if (!p1 || !p2 || !p3) {
+ kvm_printf(kvm,"%s: gfn_to_page failed\n", __FUNCTION__);
+ return 0;
+ }
+
+ page = kmap_atomic(p1, KM_USER0);
+ memset(page, 0, PAGE_SIZE);
+ *(u16*)(page + 0x66) = TSS_BASE_SIZE + TSS_REDIRECTION_SIZE;
+ kunmap_atomic(page, KM_USER0);
+
+ page = kmap_atomic(p2, KM_USER0);
+ memset(page, 0, PAGE_SIZE);
+ kunmap_atomic(page, KM_USER0);
+
+ page = kmap_atomic(p3, KM_USER0);
+ memset(page, 0, PAGE_SIZE);
+ *(page + RMODE_TSS_SIZE - 2 * PAGE_SIZE - 1) = ~0;
+ kunmap_atomic(page, KM_USER0);
+
+ return 1;
+}
+
+static void vmcs_write32_fixedbits(u32 msr, u32 vmcs_field, u32 val)
+{
+ u32 msr_high, msr_low;
+
+ rdmsr(msr, msr_low, msr_high);
+
+ val &= msr_high;
+ val |= msr_low;
+ vmcs_write32(vmcs_field, val);
+}
+
+static void seg_setup(int seg)
+{
+ struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+
+ vmcs_write16(sf->selector, 0);
+ vmcs_writel(sf->base, 0);
+ vmcs_write32(sf->limit, 0xffff);
+ vmcs_write32(sf->ar_bytes, 0x93);
+}
+
+/*
+ * Sets up the vmcs for emulated real mode.
+ */
+static int vmx_vcpu_setup(struct kvm_vcpu *vcpu)
+{
+ u32 host_sysenter_cs;
+ u32 junk;
+ unsigned long a;
+ struct descriptor_table dt;
+ int i;
+ int ret = 0;
+ int nr_good_msrs;
+ extern asmlinkage void kvm_vmx_return(void);
+
+ if (!init_rmode_tss(vcpu->kvm)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(vcpu->regs, 0, sizeof(vcpu->regs));
+ vcpu->regs[VCPU_REGS_RDX] = get_rdx_init_val();
+ vcpu->cr8 = 0;
+ vcpu->apic_base = 0xfee00000 |
+ /*for vcpu 0*/ MSR_IA32_APICBASE_BSP |
+ MSR_IA32_APICBASE_ENABLE;
+
+ fx_init(vcpu);
+
+ /*
+ * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode
+ * insists on having GUEST_CS_BASE == GUEST_CS_SELECTOR << 4. Sigh.
+ */
+ vmcs_write16(GUEST_CS_SELECTOR, 0xf000);
+ vmcs_writel(GUEST_CS_BASE, 0x000f0000);
+ vmcs_write32(GUEST_CS_LIMIT, 0xffff);
+ vmcs_write32(GUEST_CS_AR_BYTES, 0x9b);
+
+ seg_setup(VCPU_SREG_DS);
+ seg_setup(VCPU_SREG_ES);
+ seg_setup(VCPU_SREG_FS);
+ seg_setup(VCPU_SREG_GS);
+ seg_setup(VCPU_SREG_SS);
+
+ vmcs_write16(GUEST_TR_SELECTOR, 0);
+ vmcs_writel(GUEST_TR_BASE, 0);
+ vmcs_write32(GUEST_TR_LIMIT, 0xffff);
+ vmcs_write32(GUEST_TR_AR_BYTES, 0x008b);
+
+ vmcs_write16(GUEST_LDTR_SELECTOR, 0);
+ vmcs_writel(GUEST_LDTR_BASE, 0);
+ vmcs_write32(GUEST_LDTR_LIMIT, 0xffff);
+ vmcs_write32(GUEST_LDTR_AR_BYTES, 0x00082);
+
+ vmcs_write32(GUEST_SYSENTER_CS, 0);
+ vmcs_writel(GUEST_SYSENTER_ESP, 0);
+ vmcs_writel(GUEST_SYSENTER_EIP, 0);
+
+ vmcs_writel(GUEST_RFLAGS, 0x02);
+ vmcs_writel(GUEST_RIP, 0xfff0);
+ vmcs_writel(GUEST_RSP, 0);
+
+ vmcs_writel(GUEST_CR3, 0);
+
+ //todo: dr0 = dr1 = dr2 = dr3 = 0; dr6 = 0xffff0ff0
+ vmcs_writel(GUEST_DR7, 0x400);
+
+ vmcs_writel(GUEST_GDTR_BASE, 0);
+ vmcs_write32(GUEST_GDTR_LIMIT, 0xffff);
+
+ vmcs_writel(GUEST_IDTR_BASE, 0);
+ vmcs_write32(GUEST_IDTR_LIMIT, 0xffff);
+
+ vmcs_write32(GUEST_ACTIVITY_STATE, 0);
+ vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
+ vmcs_write32(GUEST_PENDING_DBG_EXCEPTIONS, 0);
+
+ /* I/O */
+ vmcs_write64(IO_BITMAP_A, 0);
+ vmcs_write64(IO_BITMAP_B, 0);
+
+ guest_write_tsc(0);
+
+ vmcs_write64(VMCS_LINK_POINTER, -1ull); /* 22.3.1.5 */
+
+ /* Special registers */
+ vmcs_write64(GUEST_IA32_DEBUGCTL, 0);
+
+ /* Control */
+ vmcs_write32_fixedbits(MSR_IA32_VMX_PINBASED_CTLS_MSR,
+ PIN_BASED_VM_EXEC_CONTROL,
+ PIN_BASED_EXT_INTR_MASK /* 20.6.1 */
+ | PIN_BASED_NMI_EXITING /* 20.6.1 */
+ );
+ vmcs_write32_fixedbits(MSR_IA32_VMX_PROCBASED_CTLS_MSR,
+ CPU_BASED_VM_EXEC_CONTROL,
+ CPU_BASED_HLT_EXITING /* 20.6.2 */
+ | CPU_BASED_CR8_LOAD_EXITING /* 20.6.2 */
+ | CPU_BASED_CR8_STORE_EXITING /* 20.6.2 */
+ | CPU_BASED_UNCOND_IO_EXITING /* 20.6.2 */
+ | CPU_BASED_INVDPG_EXITING
+ | CPU_BASED_MOV_DR_EXITING
+ | CPU_BASED_USE_TSC_OFFSETING /* 21.3 */
+ );
+
+ vmcs_write32(EXCEPTION_BITMAP, 1 << PF_VECTOR);
+ vmcs_write32(PAGE_FAULT_ERROR_CODE_MASK, 0);
+ vmcs_write32(PAGE_FAULT_ERROR_CODE_MATCH, 0);
+ vmcs_write32(CR3_TARGET_COUNT, 0); /* 22.2.1 */
+
+ vmcs_writel(HOST_CR0, read_cr0()); /* 22.2.3 */
+ vmcs_writel(HOST_CR4, read_cr4()); /* 22.2.3, 22.2.5 */
+ vmcs_writel(HOST_CR3, read_cr3()); /* 22.2.3 FIXME: shadow tables */
+
+ vmcs_write16(HOST_CS_SELECTOR, __KERNEL_CS); /* 22.2.4 */
+ vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS); /* 22.2.4 */
+ vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS); /* 22.2.4 */
+ vmcs_write16(HOST_FS_SELECTOR, read_fs()); /* 22.2.4 */
+ vmcs_write16(HOST_GS_SELECTOR, read_gs()); /* 22.2.4 */
+ vmcs_write16(HOST_SS_SELECTOR, __KERNEL_DS); /* 22.2.4 */
+#ifdef CONFIG_X86_64
+ rdmsrl(MSR_FS_BASE, a);
+ vmcs_writel(HOST_FS_BASE, a); /* 22.2.4 */
+ rdmsrl(MSR_GS_BASE, a);
+ vmcs_writel(HOST_GS_BASE, a); /* 22.2.4 */
+#else
+ vmcs_writel(HOST_FS_BASE, 0); /* 22.2.4 */
+ vmcs_writel(HOST_GS_BASE, 0); /* 22.2.4 */
+#endif
+
+ vmcs_write16(HOST_TR_SELECTOR, GDT_ENTRY_TSS*8); /* 22.2.4 */
+
+ get_idt(&dt);
+ vmcs_writel(HOST_IDTR_BASE, dt.base); /* 22.2.4 */
+
+
+ vmcs_writel(HOST_RIP, (unsigned long)kvm_vmx_return); /* 22.2.5 */
+
+ rdmsr(MSR_IA32_SYSENTER_CS, host_sysenter_cs, junk);
+ vmcs_write32(HOST_IA32_SYSENTER_CS, host_sysenter_cs);
+ rdmsrl(MSR_IA32_SYSENTER_ESP, a);
+ vmcs_writel(HOST_IA32_SYSENTER_ESP, a); /* 22.2.3 */
+ rdmsrl(MSR_IA32_SYSENTER_EIP, a);
+ vmcs_writel(HOST_IA32_SYSENTER_EIP, a); /* 22.2.3 */
+
+ ret = -ENOMEM;
+ vcpu->guest_msrs = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!vcpu->guest_msrs)
+ goto out;
+ vcpu->host_msrs = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!vcpu->host_msrs)
+ goto out_free_guest_msrs;
+
+ for (i = 0; i < NR_VMX_MSR; ++i) {
+ u32 index = vmx_msr_index[i];
+ u32 data_low, data_high;
+ u64 data;
+ int j = vcpu->nmsrs;
+
+ if (rdmsr_safe(index, &data_low, &data_high) < 0)
+ continue;
+ data = data_low | ((u64)data_high << 32);
+ vcpu->host_msrs[j].index = index;
+ vcpu->host_msrs[j].reserved = 0;
+ vcpu->host_msrs[j].data = data;
+ vcpu->guest_msrs[j] = vcpu->host_msrs[j];
+ ++vcpu->nmsrs;
+ }
+ printk(KERN_DEBUG "kvm: msrs: %d\n", vcpu->nmsrs);
+
+ nr_good_msrs = vcpu->nmsrs - NR_BAD_MSRS;
+ vmcs_writel(VM_ENTRY_MSR_LOAD_ADDR,
+ virt_to_phys(vcpu->guest_msrs + NR_BAD_MSRS));
+ vmcs_writel(VM_EXIT_MSR_STORE_ADDR,
+ virt_to_phys(vcpu->guest_msrs + NR_BAD_MSRS));
+ vmcs_writel(VM_EXIT_MSR_LOAD_ADDR,
+ virt_to_phys(vcpu->host_msrs + NR_BAD_MSRS));
+ vmcs_write32_fixedbits(MSR_IA32_VMX_EXIT_CTLS_MSR, VM_EXIT_CONTROLS,
+ (HOST_IS_64 << 9)); /* 22.2,1, 20.7.1 */
+ vmcs_write32(VM_EXIT_MSR_STORE_COUNT, nr_good_msrs); /* 22.2.2 */
+ vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, nr_good_msrs); /* 22.2.2 */
+ vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, nr_good_msrs); /* 22.2.2 */
+
+
+ /* 22.2.1, 20.8.1 */
+ vmcs_write32_fixedbits(MSR_IA32_VMX_ENTRY_CTLS_MSR,
+ VM_ENTRY_CONTROLS, 0);
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); /* 22.2.1 */
+
+#ifdef CONFIG_X86_64
+ vmcs_writel(VIRTUAL_APIC_PAGE_ADDR, 0);
+ vmcs_writel(TPR_THRESHOLD, 0);
+#endif
+
+ vmcs_writel(CR0_GUEST_HOST_MASK, KVM_GUEST_CR0_MASK);
+ vmcs_writel(CR4_GUEST_HOST_MASK, KVM_GUEST_CR4_MASK);
+
+ vcpu->cr0 = 0x60000010;
+ vmx_set_cr0(vcpu, vcpu->cr0); // enter rmode
+ vmx_set_cr4(vcpu, 0);
+#ifdef CONFIG_X86_64
+ vmx_set_efer(vcpu, 0);
+#endif
+
+ return 0;
+
+out_free_guest_msrs:
+ kfree(vcpu->guest_msrs);
+out:
+ return ret;
+}
+
+static void inject_rmode_irq(struct kvm_vcpu *vcpu, int irq)
+{
+ u16 ent[2];
+ u16 cs;
+ u16 ip;
+ unsigned long flags;
+ unsigned long ss_base = vmcs_readl(GUEST_SS_BASE);
+ u16 sp = vmcs_readl(GUEST_RSP);
+ u32 ss_limit = vmcs_read32(GUEST_SS_LIMIT);
+
+ if (sp > ss_limit || sp - 6 > sp) {
+ vcpu_printf(vcpu, "%s: #SS, rsp 0x%lx ss 0x%lx limit 0x%x\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_RSP),
+ vmcs_readl(GUEST_SS_BASE),
+ vmcs_read32(GUEST_SS_LIMIT));
+ return;
+ }
+
+ if (kvm_read_guest(vcpu, irq * sizeof(ent), sizeof(ent), &ent) !=
+ sizeof(ent)) {
+ vcpu_printf(vcpu, "%s: read guest err\n", __FUNCTION__);
+ return;
+ }
+
+ flags = vmcs_readl(GUEST_RFLAGS);
+ cs = vmcs_readl(GUEST_CS_BASE) >> 4;
+ ip = vmcs_readl(GUEST_RIP);
+
+
+ if (kvm_write_guest(vcpu, ss_base + sp - 2, 2, &flags) != 2 ||
+ kvm_write_guest(vcpu, ss_base + sp - 4, 2, &cs) != 2 ||
+ kvm_write_guest(vcpu, ss_base + sp - 6, 2, &ip) != 2) {
+ vcpu_printf(vcpu, "%s: write guest err\n", __FUNCTION__);
+ return;
+ }
+
+ vmcs_writel(GUEST_RFLAGS, flags &
+ ~( X86_EFLAGS_IF | X86_EFLAGS_AC | X86_EFLAGS_TF));
+ vmcs_write16(GUEST_CS_SELECTOR, ent[1]) ;
+ vmcs_writel(GUEST_CS_BASE, ent[1] << 4);
+ vmcs_writel(GUEST_RIP, ent[0]);
+ vmcs_writel(GUEST_RSP, (vmcs_readl(GUEST_RSP) & ~0xffff) | (sp - 6));
+}
+
+static void kvm_do_inject_irq(struct kvm_vcpu *vcpu)
+{
+ int word_index = __ffs(vcpu->irq_summary);
+ int bit_index = __ffs(vcpu->irq_pending[word_index]);
+ int irq = word_index * BITS_PER_LONG + bit_index;
+
+ clear_bit(bit_index, &vcpu->irq_pending[word_index]);
+ if (!vcpu->irq_pending[word_index])
+ clear_bit(word_index, &vcpu->irq_summary);
+
+ if (vcpu->rmode.active) {
+ inject_rmode_irq(vcpu, irq);
+ return;
+ }
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK);
+}
+
+static void kvm_try_inject_irq(struct kvm_vcpu *vcpu)
+{
+ if ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF)
+ && (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0)
+ /*
+ * Interrupts enabled, and not blocked by sti or mov ss. Good.
+ */
+ kvm_do_inject_irq(vcpu);
+ else
+ /*
+ * Interrupts blocked. Wait for unblock.
+ */
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL,
+ vmcs_read32(CPU_BASED_VM_EXEC_CONTROL)
+ | CPU_BASED_VIRTUAL_INTR_PENDING);
+}
+
+static void kvm_guest_debug_pre(struct kvm_vcpu *vcpu)
+{
+ struct kvm_guest_debug *dbg = &vcpu->guest_debug;
+
+ set_debugreg(dbg->bp[0], 0);
+ set_debugreg(dbg->bp[1], 1);
+ set_debugreg(dbg->bp[2], 2);
+ set_debugreg(dbg->bp[3], 3);
+
+ if (dbg->singlestep) {
+ unsigned long flags;
+
+ flags = vmcs_readl(GUEST_RFLAGS);
+ flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
+ vmcs_writel(GUEST_RFLAGS, flags);
+ }
+}
+
+static int handle_rmode_exception(struct kvm_vcpu *vcpu,
+ int vec, u32 err_code)
+{
+ if (!vcpu->rmode.active)
+ return 0;
+
+ if (vec == GP_VECTOR && err_code == 0)
+ if (emulate_instruction(vcpu, NULL, 0, 0) == EMULATE_DONE)
+ return 1;
+ return 0;
+}
+
+static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 intr_info, error_code;
+ unsigned long cr2, rip;
+ u32 vect_info;
+ enum emulation_result er;
+
+ vect_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+ intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
+
+ if ((vect_info & VECTORING_INFO_VALID_MASK) &&
+ !is_page_fault(intr_info)) {
+ printk(KERN_ERR "%s: unexpected, vectoring info 0x%x "
+ "intr info 0x%x\n", __FUNCTION__, vect_info, intr_info);
+ }
+
+ if (is_external_interrupt(vect_info)) {
+ int irq = vect_info & VECTORING_INFO_VECTOR_MASK;
+ set_bit(irq, vcpu->irq_pending);
+ set_bit(irq / BITS_PER_LONG, &vcpu->irq_summary);
+ }
+
+ if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200) { /* nmi */
+ asm ("int $2");
+ return 1;
+ }
+ error_code = 0;
+ rip = vmcs_readl(GUEST_RIP);
+ if (intr_info & INTR_INFO_DELIEVER_CODE_MASK)
+ error_code = vmcs_read32(VM_EXIT_INTR_ERROR_CODE);
+ if (is_page_fault(intr_info)) {
+ cr2 = vmcs_readl(EXIT_QUALIFICATION);
+
+ spin_lock(&vcpu->kvm->lock);
+ if (!vcpu->mmu.page_fault(vcpu, cr2, error_code)) {
+ spin_unlock(&vcpu->kvm->lock);
+ return 1;
+ }
+
+ er = emulate_instruction(vcpu, kvm_run, cr2, error_code);
+ spin_unlock(&vcpu->kvm->lock);
+
+ switch (er) {
+ case EMULATE_DONE:
+ return 1;
+ case EMULATE_DO_MMIO:
+ ++kvm_stat.mmio_exits;
+ kvm_run->exit_reason = KVM_EXIT_MMIO;
+ return 0;
+ case EMULATE_FAIL:
+ vcpu_printf(vcpu, "%s: emulate fail\n", __FUNCTION__);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (vcpu->rmode.active &&
+ handle_rmode_exception(vcpu, intr_info & INTR_INFO_VECTOR_MASK,
+ error_code))
+ return 1;
+
+ if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) == (INTR_TYPE_EXCEPTION | 1)) {
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ return 0;
+ }
+ kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
+ kvm_run->ex.exception = intr_info & INTR_INFO_VECTOR_MASK;
+ kvm_run->ex.error_code = error_code;
+ return 0;
+}
+
+static int handle_external_interrupt(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run)
+{
+ ++kvm_stat.irq_exits;
+ return 1;
+}
+
+
+static int get_io_count(struct kvm_vcpu *vcpu, u64 *count)
+{
+ u64 inst;
+ gva_t rip;
+ int countr_size;
+ int i, n;
+
+ if ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_VM)) {
+ countr_size = 2;
+ } else {
+ u32 cs_ar = vmcs_read32(GUEST_CS_AR_BYTES);
+
+ countr_size = (cs_ar & AR_L_MASK) ? 8:
+ (cs_ar & AR_DB_MASK) ? 4: 2;
+ }
+
+ rip = vmcs_readl(GUEST_RIP);
+ if (countr_size != 8)
+ rip += vmcs_readl(GUEST_CS_BASE);
+
+ n = kvm_read_guest(vcpu, rip, sizeof(inst), &inst);
+
+ for (i = 0; i < n; i++) {
+ switch (((u8*)&inst)[i]) {
+ case 0xf0:
+ case 0xf2:
+ case 0xf3:
+ case 0x2e:
+ case 0x36:
+ case 0x3e:
+ case 0x26:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ break;
+ case 0x67:
+ countr_size = (countr_size == 2) ? 4: (countr_size >> 1);
+ default:
+ goto done;
+ }
+ }
+ return 0;
+done:
+ countr_size *= 8;
+ *count = vcpu->regs[VCPU_REGS_RCX] & (~0ULL >> (64 - countr_size));
+ return 1;
+}
+
+static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u64 exit_qualification;
+
+ ++kvm_stat.io_exits;
+ exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
+ kvm_run->exit_reason = KVM_EXIT_IO;
+ if (exit_qualification & 8)
+ kvm_run->io.direction = KVM_EXIT_IO_IN;
+ else
+ kvm_run->io.direction = KVM_EXIT_IO_OUT;
+ kvm_run->io.size = (exit_qualification & 7) + 1;
+ kvm_run->io.string = (exit_qualification & 16) != 0;
+ kvm_run->io.string_down
+ = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0;
+ kvm_run->io.rep = (exit_qualification & 32) != 0;
+ kvm_run->io.port = exit_qualification >> 16;
+ if (kvm_run->io.string) {
+ if (!get_io_count(vcpu, &kvm_run->io.count))
+ return 1;
+ kvm_run->io.address = vmcs_readl(GUEST_LINEAR_ADDRESS);
+ } else
+ kvm_run->io.value = vcpu->regs[VCPU_REGS_RAX]; /* rax */
+ return 0;
+}
+
+static int handle_invlpg(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u64 address = vmcs_read64(EXIT_QUALIFICATION);
+ int instruction_length = vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
+ spin_lock(&vcpu->kvm->lock);
+ vcpu->mmu.inval_page(vcpu, address);
+ spin_unlock(&vcpu->kvm->lock);
+ vmcs_writel(GUEST_RIP, vmcs_readl(GUEST_RIP) + instruction_length);
+ return 1;
+}
+
+static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u64 exit_qualification;
+ int cr;
+ int reg;
+
+ exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
+ cr = exit_qualification & 15;
+ reg = (exit_qualification >> 8) & 15;
+ switch ((exit_qualification >> 4) & 3) {
+ case 0: /* mov to cr */
+ switch (cr) {
+ case 0:
+ vcpu_load_rsp_rip(vcpu);
+ set_cr0(vcpu, vcpu->regs[reg]);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ case 3:
+ vcpu_load_rsp_rip(vcpu);
+ set_cr3(vcpu, vcpu->regs[reg]);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ case 4:
+ vcpu_load_rsp_rip(vcpu);
+ set_cr4(vcpu, vcpu->regs[reg]);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ case 8:
+ vcpu_load_rsp_rip(vcpu);
+ set_cr8(vcpu, vcpu->regs[reg]);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ };
+ break;
+ case 1: /*mov from cr*/
+ switch (cr) {
+ case 3:
+ vcpu_load_rsp_rip(vcpu);
+ vcpu->regs[reg] = vcpu->cr3;
+ vcpu_put_rsp_rip(vcpu);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ case 8:
+ printk(KERN_DEBUG "handle_cr: read CR8 "
+ "cpu erratum AA15\n");
+ vcpu_load_rsp_rip(vcpu);
+ vcpu->regs[reg] = vcpu->cr8;
+ vcpu_put_rsp_rip(vcpu);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ }
+ break;
+ case 3: /* lmsw */
+ lmsw(vcpu, (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f);
+
+ skip_emulated_instruction(vcpu);
+ return 1;
+ default:
+ break;
+ }
+ kvm_run->exit_reason = 0;
+ printk(KERN_ERR "kvm: unhandled control register: op %d cr %d\n",
+ (int)(exit_qualification >> 4) & 3, cr);
+ return 0;
+}
+
+static int handle_dr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u64 exit_qualification;
+ unsigned long val;
+ int dr, reg;
+
+ /*
+ * FIXME: this code assumes the host is debugging the guest.
+ * need to deal with guest debugging itself too.
+ */
+ exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
+ dr = exit_qualification & 7;
+ reg = (exit_qualification >> 8) & 15;
+ vcpu_load_rsp_rip(vcpu);
+ if (exit_qualification & 16) {
+ /* mov from dr */
+ switch (dr) {
+ case 6:
+ val = 0xffff0ff0;
+ break;
+ case 7:
+ val = 0x400;
+ break;
+ default:
+ val = 0;
+ }
+ vcpu->regs[reg] = val;
+ } else {
+ /* mov to dr */
+ }
+ vcpu_put_rsp_rip(vcpu);
+ skip_emulated_instruction(vcpu);
+ return 1;
+}
+
+static int handle_cpuid(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ kvm_run->exit_reason = KVM_EXIT_CPUID;
+ return 0;
+}
+
+static int handle_rdmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 ecx = vcpu->regs[VCPU_REGS_RCX];
+ u64 data;
+
+ if (vmx_get_msr(vcpu, ecx, &data)) {
+ vmx_inject_gp(vcpu, 0);
+ return 1;
+ }
+
+ /* FIXME: handling of bits 32:63 of rax, rdx */
+ vcpu->regs[VCPU_REGS_RAX] = data & -1u;
+ vcpu->regs[VCPU_REGS_RDX] = (data >> 32) & -1u;
+ skip_emulated_instruction(vcpu);
+ return 1;
+}
+
+static int handle_wrmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 ecx = vcpu->regs[VCPU_REGS_RCX];
+ u64 data = (vcpu->regs[VCPU_REGS_RAX] & -1u)
+ | ((u64)(vcpu->regs[VCPU_REGS_RDX] & -1u) << 32);
+
+ if (vmx_set_msr(vcpu, ecx, data) != 0) {
+ vmx_inject_gp(vcpu, 0);
+ return 1;
+ }
+
+ skip_emulated_instruction(vcpu);
+ return 1;
+}
+
+static int handle_interrupt_window(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run)
+{
+ /* Turn off interrupt window reporting. */
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL,
+ vmcs_read32(CPU_BASED_VM_EXEC_CONTROL)
+ & ~CPU_BASED_VIRTUAL_INTR_PENDING);
+ return 1;
+}
+
+static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ skip_emulated_instruction(vcpu);
+ if (vcpu->irq_summary && (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF))
+ return 1;
+
+ kvm_run->exit_reason = KVM_EXIT_HLT;
+ return 0;
+}
+
+/*
+ * The exit handlers return 1 if the exit was handled fully and guest execution
+ * may resume. Otherwise they set the kvm_run parameter to indicate what needs
+ * to be done to userspace and return 0.
+ */
+static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run) = {
+ [EXIT_REASON_EXCEPTION_NMI] = handle_exception,
+ [EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt,
+ [EXIT_REASON_IO_INSTRUCTION] = handle_io,
+ [EXIT_REASON_INVLPG] = handle_invlpg,
+ [EXIT_REASON_CR_ACCESS] = handle_cr,
+ [EXIT_REASON_DR_ACCESS] = handle_dr,
+ [EXIT_REASON_CPUID] = handle_cpuid,
+ [EXIT_REASON_MSR_READ] = handle_rdmsr,
+ [EXIT_REASON_MSR_WRITE] = handle_wrmsr,
+ [EXIT_REASON_PENDING_INTERRUPT] = handle_interrupt_window,
+ [EXIT_REASON_HLT] = handle_halt,
+};
+
+static const int kvm_vmx_max_exit_handlers =
+ sizeof(kvm_vmx_exit_handlers) / sizeof(*kvm_vmx_exit_handlers);
+
+/*
+ * The guest has exited. See if we can fix it or if we need userspace
+ * assistance.
+ */
+static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
+{
+ u32 vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+ u32 exit_reason = vmcs_read32(VM_EXIT_REASON);
+
+ if ( (vectoring_info & VECTORING_INFO_VALID_MASK) &&
+ exit_reason != EXIT_REASON_EXCEPTION_NMI )
+ printk(KERN_WARNING "%s: unexpected, valid vectoring info and "
+ "exit reason is 0x%x\n", __FUNCTION__, exit_reason);
+ kvm_run->instruction_length = vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
+ if (exit_reason < kvm_vmx_max_exit_handlers
+ && kvm_vmx_exit_handlers[exit_reason])
+ return kvm_vmx_exit_handlers[exit_reason](vcpu, kvm_run);
+ else {
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ kvm_run->hw.hardware_exit_reason = exit_reason;
+ }
+ return 0;
+}
+
+static int vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u8 fail;
+ u16 fs_sel, gs_sel, ldt_sel;
+ int fs_gs_ldt_reload_needed;
+
+again:
+ /*
+ * Set host fs and gs selectors. Unfortunately, 22.2.3 does not
+ * allow segment selectors with cpl > 0 or ti == 1.
+ */
+ fs_sel = read_fs();
+ gs_sel = read_gs();
+ ldt_sel = read_ldt();
+ fs_gs_ldt_reload_needed = (fs_sel & 7) | (gs_sel & 7) | ldt_sel;
+ if (!fs_gs_ldt_reload_needed) {
+ vmcs_write16(HOST_FS_SELECTOR, fs_sel);
+ vmcs_write16(HOST_GS_SELECTOR, gs_sel);
+ } else {
+ vmcs_write16(HOST_FS_SELECTOR, 0);
+ vmcs_write16(HOST_GS_SELECTOR, 0);
+ }
+
+#ifdef CONFIG_X86_64
+ vmcs_writel(HOST_FS_BASE, read_msr(MSR_FS_BASE));
+ vmcs_writel(HOST_GS_BASE, read_msr(MSR_GS_BASE));
+#else
+ vmcs_writel(HOST_FS_BASE, segment_base(fs_sel));
+ vmcs_writel(HOST_GS_BASE, segment_base(gs_sel));
+#endif
+
+ if (vcpu->irq_summary &&
+ !(vmcs_read32(VM_ENTRY_INTR_INFO_FIELD) & INTR_INFO_VALID_MASK))
+ kvm_try_inject_irq(vcpu);
+
+ if (vcpu->guest_debug.enabled)
+ kvm_guest_debug_pre(vcpu);
+
+ fx_save(vcpu->host_fx_image);
+ fx_restore(vcpu->guest_fx_image);
+
+ save_msrs(vcpu->host_msrs, vcpu->nmsrs);
+ load_msrs(vcpu->guest_msrs, NR_BAD_MSRS);
+
+ asm (
+ /* Store host registers */
+ "pushf \n\t"
+#ifdef CONFIG_X86_64
+ "push %%rax; push %%rbx; push %%rdx;"
+ "push %%rsi; push %%rdi; push %%rbp;"
+ "push %%r8; push %%r9; push %%r10; push %%r11;"
+ "push %%r12; push %%r13; push %%r14; push %%r15;"
+ "push %%rcx \n\t"
+ ASM_VMX_VMWRITE_RSP_RDX "\n\t"
+#else
+ "pusha; push %%ecx \n\t"
+ ASM_VMX_VMWRITE_RSP_RDX "\n\t"
+#endif
+ /* Check if vmlaunch of vmresume is needed */
+ "cmp $0, %1 \n\t"
+ /* Load guest registers. Don't clobber flags. */
+#ifdef CONFIG_X86_64
+ "mov %c[cr2](%3), %%rax \n\t"
+ "mov %%rax, %%cr2 \n\t"
+ "mov %c[rax](%3), %%rax \n\t"
+ "mov %c[rbx](%3), %%rbx \n\t"
+ "mov %c[rdx](%3), %%rdx \n\t"
+ "mov %c[rsi](%3), %%rsi \n\t"
+ "mov %c[rdi](%3), %%rdi \n\t"
+ "mov %c[rbp](%3), %%rbp \n\t"
+ "mov %c[r8](%3), %%r8 \n\t"
+ "mov %c[r9](%3), %%r9 \n\t"
+ "mov %c[r10](%3), %%r10 \n\t"
+ "mov %c[r11](%3), %%r11 \n\t"
+ "mov %c[r12](%3), %%r12 \n\t"
+ "mov %c[r13](%3), %%r13 \n\t"
+ "mov %c[r14](%3), %%r14 \n\t"
+ "mov %c[r15](%3), %%r15 \n\t"
+ "mov %c[rcx](%3), %%rcx \n\t" /* kills %3 (rcx) */
+#else
+ "mov %c[cr2](%3), %%eax \n\t"
+ "mov %%eax, %%cr2 \n\t"
+ "mov %c[rax](%3), %%eax \n\t"
+ "mov %c[rbx](%3), %%ebx \n\t"
+ "mov %c[rdx](%3), %%edx \n\t"
+ "mov %c[rsi](%3), %%esi \n\t"
+ "mov %c[rdi](%3), %%edi \n\t"
+ "mov %c[rbp](%3), %%ebp \n\t"
+ "mov %c[rcx](%3), %%ecx \n\t" /* kills %3 (ecx) */
+#endif
+ /* Enter guest mode */
+ "jne launched \n\t"
+ ASM_VMX_VMLAUNCH "\n\t"
+ "jmp kvm_vmx_return \n\t"
+ "launched: " ASM_VMX_VMRESUME "\n\t"
+ ".globl kvm_vmx_return \n\t"
+ "kvm_vmx_return: "
+ /* Save guest registers, load host registers, keep flags */
+#ifdef CONFIG_X86_64
+ "xchg %3, 0(%%rsp) \n\t"
+ "mov %%rax, %c[rax](%3) \n\t"
+ "mov %%rbx, %c[rbx](%3) \n\t"
+ "pushq 0(%%rsp); popq %c[rcx](%3) \n\t"
+ "mov %%rdx, %c[rdx](%3) \n\t"
+ "mov %%rsi, %c[rsi](%3) \n\t"
+ "mov %%rdi, %c[rdi](%3) \n\t"
+ "mov %%rbp, %c[rbp](%3) \n\t"
+ "mov %%r8, %c[r8](%3) \n\t"
+ "mov %%r9, %c[r9](%3) \n\t"
+ "mov %%r10, %c[r10](%3) \n\t"
+ "mov %%r11, %c[r11](%3) \n\t"
+ "mov %%r12, %c[r12](%3) \n\t"
+ "mov %%r13, %c[r13](%3) \n\t"
+ "mov %%r14, %c[r14](%3) \n\t"
+ "mov %%r15, %c[r15](%3) \n\t"
+ "mov %%cr2, %%rax \n\t"
+ "mov %%rax, %c[cr2](%3) \n\t"
+ "mov 0(%%rsp), %3 \n\t"
+
+ "pop %%rcx; pop %%r15; pop %%r14; pop %%r13; pop %%r12;"
+ "pop %%r11; pop %%r10; pop %%r9; pop %%r8;"
+ "pop %%rbp; pop %%rdi; pop %%rsi;"
+ "pop %%rdx; pop %%rbx; pop %%rax \n\t"
+#else
+ "xchg %3, 0(%%esp) \n\t"
+ "mov %%eax, %c[rax](%3) \n\t"
+ "mov %%ebx, %c[rbx](%3) \n\t"
+ "pushl 0(%%esp); popl %c[rcx](%3) \n\t"
+ "mov %%edx, %c[rdx](%3) \n\t"
+ "mov %%esi, %c[rsi](%3) \n\t"
+ "mov %%edi, %c[rdi](%3) \n\t"
+ "mov %%ebp, %c[rbp](%3) \n\t"
+ "mov %%cr2, %%eax \n\t"
+ "mov %%eax, %c[cr2](%3) \n\t"
+ "mov 0(%%esp), %3 \n\t"
+
+ "pop %%ecx; popa \n\t"
+#endif
+ "setbe %0 \n\t"
+ "popf \n\t"
+ : "=g" (fail)
+ : "r"(vcpu->launched), "d"((unsigned long)HOST_RSP),
+ "c"(vcpu),
+ [rax]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RAX])),
+ [rbx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RBX])),
+ [rcx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RCX])),
+ [rdx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RDX])),
+ [rsi]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RSI])),
+ [rdi]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RDI])),
+ [rbp]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RBP])),
+#ifdef CONFIG_X86_64
+ [r8 ]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R8 ])),
+ [r9 ]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R9 ])),
+ [r10]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R10])),
+ [r11]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R11])),
+ [r12]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R12])),
+ [r13]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R13])),
+ [r14]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R14])),
+ [r15]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_R15])),
+#endif
+ [cr2]"i"(offsetof(struct kvm_vcpu, cr2))
+ : "cc", "memory" );
+
+ ++kvm_stat.exits;
+
+ save_msrs(vcpu->guest_msrs, NR_BAD_MSRS);
+ load_msrs(vcpu->host_msrs, NR_BAD_MSRS);
+
+ fx_save(vcpu->guest_fx_image);
+ fx_restore(vcpu->host_fx_image);
+
+#ifndef CONFIG_X86_64
+ asm ("mov %0, %%ds; mov %0, %%es" : : "r"(__USER_DS));
+#endif
+
+ kvm_run->exit_type = 0;
+ if (fail) {
+ kvm_run->exit_type = KVM_EXIT_TYPE_FAIL_ENTRY;
+ kvm_run->exit_reason = vmcs_read32(VM_INSTRUCTION_ERROR);
+ } else {
+ if (fs_gs_ldt_reload_needed) {
+ load_ldt(ldt_sel);
+ load_fs(fs_sel);
+ /*
+ * If we have to reload gs, we must take care to
+ * preserve our gs base.
+ */
+ local_irq_disable();
+ load_gs(gs_sel);
+#ifdef CONFIG_X86_64
+ wrmsrl(MSR_GS_BASE, vmcs_readl(HOST_GS_BASE));
+#endif
+ local_irq_enable();
+
+ reload_tss();
+ }
+ vcpu->launched = 1;
+ kvm_run->exit_type = KVM_EXIT_TYPE_VM_EXIT;
+ if (kvm_handle_exit(kvm_run, vcpu)) {
+ /* Give scheduler a change to reschedule. */
+ if (signal_pending(current)) {
+ ++kvm_stat.signal_exits;
+ return -EINTR;
+ }
+ kvm_resched(vcpu);
+ goto again;
+ }
+ }
+ return 0;
+}
+
+static void vmx_flush_tlb(struct kvm_vcpu *vcpu)
+{
+ vmcs_writel(GUEST_CR3, vmcs_readl(GUEST_CR3));
+}
+
+static void vmx_inject_page_fault(struct kvm_vcpu *vcpu,
+ unsigned long addr,
+ u32 err_code)
+{
+ u32 vect_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+
+ ++kvm_stat.pf_guest;
+
+ if (is_page_fault(vect_info)) {
+ printk(KERN_DEBUG "inject_page_fault: "
+ "double fault 0x%lx @ 0x%lx\n",
+ addr, vmcs_readl(GUEST_RIP));
+ vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, 0);
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ DF_VECTOR |
+ INTR_TYPE_EXCEPTION |
+ INTR_INFO_DELIEVER_CODE_MASK |
+ INTR_INFO_VALID_MASK);
+ return;
+ }
+ vcpu->cr2 = addr;
+ vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, err_code);
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ PF_VECTOR |
+ INTR_TYPE_EXCEPTION |
+ INTR_INFO_DELIEVER_CODE_MASK |
+ INTR_INFO_VALID_MASK);
+
+}
+
+static void vmx_free_vmcs(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->vmcs) {
+ on_each_cpu(__vcpu_clear, vcpu, 0, 1);
+ free_vmcs(vcpu->vmcs);
+ vcpu->vmcs = NULL;
+ }
+}
+
+static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
+{
+ vmx_free_vmcs(vcpu);
+}
+
+static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
+{
+ struct vmcs *vmcs;
+
+ vmcs = alloc_vmcs();
+ if (!vmcs)
+ return -ENOMEM;
+ vmcs_clear(vmcs);
+ vcpu->vmcs = vmcs;
+ vcpu->launched = 0;
+ return 0;
+}
+
+static struct kvm_arch_ops vmx_arch_ops = {
+ .cpu_has_kvm_support = cpu_has_kvm_support,
+ .disabled_by_bios = vmx_disabled_by_bios,
+ .hardware_setup = hardware_setup,
+ .hardware_unsetup = hardware_unsetup,
+ .hardware_enable = hardware_enable,
+ .hardware_disable = hardware_disable,
+
+ .vcpu_create = vmx_create_vcpu,
+ .vcpu_free = vmx_free_vcpu,
+
+ .vcpu_load = vmx_vcpu_load,
+ .vcpu_put = vmx_vcpu_put,
+
+ .set_guest_debug = set_guest_debug,
+ .get_msr = vmx_get_msr,
+ .set_msr = vmx_set_msr,
+ .get_segment_base = vmx_get_segment_base,
+ .get_segment = vmx_get_segment,
+ .set_segment = vmx_set_segment,
+ .is_long_mode = vmx_is_long_mode,
+ .get_cs_db_l_bits = vmx_get_cs_db_l_bits,
+ .set_cr0 = vmx_set_cr0,
+ .set_cr0_no_modeswitch = vmx_set_cr0_no_modeswitch,
+ .set_cr3 = vmx_set_cr3,
+ .set_cr4 = vmx_set_cr4,
+#ifdef CONFIG_X86_64
+ .set_efer = vmx_set_efer,
+#endif
+ .get_idt = vmx_get_idt,
+ .set_idt = vmx_set_idt,
+ .get_gdt = vmx_get_gdt,
+ .set_gdt = vmx_set_gdt,
+ .cache_regs = vcpu_load_rsp_rip,
+ .decache_regs = vcpu_put_rsp_rip,
+ .get_rflags = vmx_get_rflags,
+ .set_rflags = vmx_set_rflags,
+
+ .tlb_flush = vmx_flush_tlb,
+ .inject_page_fault = vmx_inject_page_fault,
+
+ .inject_gp = vmx_inject_gp,
+
+ .run = vmx_vcpu_run,
+ .skip_emulated_instruction = skip_emulated_instruction,
+ .vcpu_setup = vmx_vcpu_setup,
+};
+
+static int __init vmx_init(void)
+{
+ return kvm_init_arch(&vmx_arch_ops, THIS_MODULE);
+}
+
+static void __exit vmx_exit(void)
+{
+ kvm_exit_arch();
+}
+
+module_init(vmx_init)
+module_exit(vmx_exit)
diff --git a/drivers/kvm/vmx.h b/drivers/kvm/vmx.h
new file mode 100644
index 0000000..7972783
--- /dev/null
+++ b/drivers/kvm/vmx.h
@@ -0,0 +1,296 @@
+#ifndef VMX_H
+#define VMX_H
+
+/*
+ * vmx.h: VMX Architecture related definitions
+ * Copyright (c) 2004, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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.
+ *
+ * A few random additions are:
+ * Copyright (C) 2006 Qumranet
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ */
+
+#define CPU_BASED_VIRTUAL_INTR_PENDING 0x00000004
+#define CPU_BASED_USE_TSC_OFFSETING 0x00000008
+#define CPU_BASED_HLT_EXITING 0x00000080
+#define CPU_BASED_INVDPG_EXITING 0x00000200
+#define CPU_BASED_MWAIT_EXITING 0x00000400
+#define CPU_BASED_RDPMC_EXITING 0x00000800
+#define CPU_BASED_RDTSC_EXITING 0x00001000
+#define CPU_BASED_CR8_LOAD_EXITING 0x00080000
+#define CPU_BASED_CR8_STORE_EXITING 0x00100000
+#define CPU_BASED_TPR_SHADOW 0x00200000
+#define CPU_BASED_MOV_DR_EXITING 0x00800000
+#define CPU_BASED_UNCOND_IO_EXITING 0x01000000
+#define CPU_BASED_ACTIVATE_IO_BITMAP 0x02000000
+#define CPU_BASED_MSR_BITMAPS 0x10000000
+#define CPU_BASED_MONITOR_EXITING 0x20000000
+#define CPU_BASED_PAUSE_EXITING 0x40000000
+
+#define PIN_BASED_EXT_INTR_MASK 0x1
+#define PIN_BASED_NMI_EXITING 0x8
+
+#define VM_EXIT_ACK_INTR_ON_EXIT 0x00008000
+#define VM_EXIT_HOST_ADD_SPACE_SIZE 0x00000200
+
+
+/* VMCS Encodings */
+enum vmcs_field {
+ GUEST_ES_SELECTOR = 0x00000800,
+ GUEST_CS_SELECTOR = 0x00000802,
+ GUEST_SS_SELECTOR = 0x00000804,
+ GUEST_DS_SELECTOR = 0x00000806,
+ GUEST_FS_SELECTOR = 0x00000808,
+ GUEST_GS_SELECTOR = 0x0000080a,
+ GUEST_LDTR_SELECTOR = 0x0000080c,
+ GUEST_TR_SELECTOR = 0x0000080e,
+ HOST_ES_SELECTOR = 0x00000c00,
+ HOST_CS_SELECTOR = 0x00000c02,
+ HOST_SS_SELECTOR = 0x00000c04,
+ HOST_DS_SELECTOR = 0x00000c06,
+ HOST_FS_SELECTOR = 0x00000c08,
+ HOST_GS_SELECTOR = 0x00000c0a,
+ HOST_TR_SELECTOR = 0x00000c0c,
+ IO_BITMAP_A = 0x00002000,
+ IO_BITMAP_A_HIGH = 0x00002001,
+ IO_BITMAP_B = 0x00002002,
+ IO_BITMAP_B_HIGH = 0x00002003,
+ MSR_BITMAP = 0x00002004,
+ MSR_BITMAP_HIGH = 0x00002005,
+ VM_EXIT_MSR_STORE_ADDR = 0x00002006,
+ VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007,
+ VM_EXIT_MSR_LOAD_ADDR = 0x00002008,
+ VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009,
+ VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a,
+ VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b,
+ TSC_OFFSET = 0x00002010,
+ TSC_OFFSET_HIGH = 0x00002011,
+ VIRTUAL_APIC_PAGE_ADDR = 0x00002012,
+ VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013,
+ VMCS_LINK_POINTER = 0x00002800,
+ VMCS_LINK_POINTER_HIGH = 0x00002801,
+ GUEST_IA32_DEBUGCTL = 0x00002802,
+ GUEST_IA32_DEBUGCTL_HIGH = 0x00002803,
+ PIN_BASED_VM_EXEC_CONTROL = 0x00004000,
+ CPU_BASED_VM_EXEC_CONTROL = 0x00004002,
+ EXCEPTION_BITMAP = 0x00004004,
+ PAGE_FAULT_ERROR_CODE_MASK = 0x00004006,
+ PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008,
+ CR3_TARGET_COUNT = 0x0000400a,
+ VM_EXIT_CONTROLS = 0x0000400c,
+ VM_EXIT_MSR_STORE_COUNT = 0x0000400e,
+ VM_EXIT_MSR_LOAD_COUNT = 0x00004010,
+ VM_ENTRY_CONTROLS = 0x00004012,
+ VM_ENTRY_MSR_LOAD_COUNT = 0x00004014,
+ VM_ENTRY_INTR_INFO_FIELD = 0x00004016,
+ VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018,
+ VM_ENTRY_INSTRUCTION_LEN = 0x0000401a,
+ TPR_THRESHOLD = 0x0000401c,
+ SECONDARY_VM_EXEC_CONTROL = 0x0000401e,
+ VM_INSTRUCTION_ERROR = 0x00004400,
+ VM_EXIT_REASON = 0x00004402,
+ VM_EXIT_INTR_INFO = 0x00004404,
+ VM_EXIT_INTR_ERROR_CODE = 0x00004406,
+ IDT_VECTORING_INFO_FIELD = 0x00004408,
+ IDT_VECTORING_ERROR_CODE = 0x0000440a,
+ VM_EXIT_INSTRUCTION_LEN = 0x0000440c,
+ VMX_INSTRUCTION_INFO = 0x0000440e,
+ GUEST_ES_LIMIT = 0x00004800,
+ GUEST_CS_LIMIT = 0x00004802,
+ GUEST_SS_LIMIT = 0x00004804,
+ GUEST_DS_LIMIT = 0x00004806,
+ GUEST_FS_LIMIT = 0x00004808,
+ GUEST_GS_LIMIT = 0x0000480a,
+ GUEST_LDTR_LIMIT = 0x0000480c,
+ GUEST_TR_LIMIT = 0x0000480e,
+ GUEST_GDTR_LIMIT = 0x00004810,
+ GUEST_IDTR_LIMIT = 0x00004812,
+ GUEST_ES_AR_BYTES = 0x00004814,
+ GUEST_CS_AR_BYTES = 0x00004816,
+ GUEST_SS_AR_BYTES = 0x00004818,
+ GUEST_DS_AR_BYTES = 0x0000481a,
+ GUEST_FS_AR_BYTES = 0x0000481c,
+ GUEST_GS_AR_BYTES = 0x0000481e,
+ GUEST_LDTR_AR_BYTES = 0x00004820,
+ GUEST_TR_AR_BYTES = 0x00004822,
+ GUEST_INTERRUPTIBILITY_INFO = 0x00004824,
+ GUEST_ACTIVITY_STATE = 0X00004826,
+ GUEST_SYSENTER_CS = 0x0000482A,
+ HOST_IA32_SYSENTER_CS = 0x00004c00,
+ CR0_GUEST_HOST_MASK = 0x00006000,
+ CR4_GUEST_HOST_MASK = 0x00006002,
+ CR0_READ_SHADOW = 0x00006004,
+ CR4_READ_SHADOW = 0x00006006,
+ CR3_TARGET_VALUE0 = 0x00006008,
+ CR3_TARGET_VALUE1 = 0x0000600a,
+ CR3_TARGET_VALUE2 = 0x0000600c,
+ CR3_TARGET_VALUE3 = 0x0000600e,
+ EXIT_QUALIFICATION = 0x00006400,
+ GUEST_LINEAR_ADDRESS = 0x0000640a,
+ GUEST_CR0 = 0x00006800,
+ GUEST_CR3 = 0x00006802,
+ GUEST_CR4 = 0x00006804,
+ GUEST_ES_BASE = 0x00006806,
+ GUEST_CS_BASE = 0x00006808,
+ GUEST_SS_BASE = 0x0000680a,
+ GUEST_DS_BASE = 0x0000680c,
+ GUEST_FS_BASE = 0x0000680e,
+ GUEST_GS_BASE = 0x00006810,
+ GUEST_LDTR_BASE = 0x00006812,
+ GUEST_TR_BASE = 0x00006814,
+ GUEST_GDTR_BASE = 0x00006816,
+ GUEST_IDTR_BASE = 0x00006818,
+ GUEST_DR7 = 0x0000681a,
+ GUEST_RSP = 0x0000681c,
+ GUEST_RIP = 0x0000681e,
+ GUEST_RFLAGS = 0x00006820,
+ GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822,
+ GUEST_SYSENTER_ESP = 0x00006824,
+ GUEST_SYSENTER_EIP = 0x00006826,
+ HOST_CR0 = 0x00006c00,
+ HOST_CR3 = 0x00006c02,
+ HOST_CR4 = 0x00006c04,
+ HOST_FS_BASE = 0x00006c06,
+ HOST_GS_BASE = 0x00006c08,
+ HOST_TR_BASE = 0x00006c0a,
+ HOST_GDTR_BASE = 0x00006c0c,
+ HOST_IDTR_BASE = 0x00006c0e,
+ HOST_IA32_SYSENTER_ESP = 0x00006c10,
+ HOST_IA32_SYSENTER_EIP = 0x00006c12,
+ HOST_RSP = 0x00006c14,
+ HOST_RIP = 0x00006c16,
+};
+
+#define VMX_EXIT_REASONS_FAILED_VMENTRY 0x80000000
+
+#define EXIT_REASON_EXCEPTION_NMI 0
+#define EXIT_REASON_EXTERNAL_INTERRUPT 1
+
+#define EXIT_REASON_PENDING_INTERRUPT 7
+
+#define EXIT_REASON_TASK_SWITCH 9
+#define EXIT_REASON_CPUID 10
+#define EXIT_REASON_HLT 12
+#define EXIT_REASON_INVLPG 14
+#define EXIT_REASON_RDPMC 15
+#define EXIT_REASON_RDTSC 16
+#define EXIT_REASON_VMCALL 18
+#define EXIT_REASON_VMCLEAR 19
+#define EXIT_REASON_VMLAUNCH 20
+#define EXIT_REASON_VMPTRLD 21
+#define EXIT_REASON_VMPTRST 22
+#define EXIT_REASON_VMREAD 23
+#define EXIT_REASON_VMRESUME 24
+#define EXIT_REASON_VMWRITE 25
+#define EXIT_REASON_VMOFF 26
+#define EXIT_REASON_VMON 27
+#define EXIT_REASON_CR_ACCESS 28
+#define EXIT_REASON_DR_ACCESS 29
+#define EXIT_REASON_IO_INSTRUCTION 30
+#define EXIT_REASON_MSR_READ 31
+#define EXIT_REASON_MSR_WRITE 32
+#define EXIT_REASON_MWAIT_INSTRUCTION 36
+
+/*
+ * Interruption-information format
+ */
+#define INTR_INFO_VECTOR_MASK 0xff /* 7:0 */
+#define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */
+#define INTR_INFO_DELIEVER_CODE_MASK 0x800 /* 11 */
+#define INTR_INFO_VALID_MASK 0x80000000 /* 31 */
+
+#define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK
+#define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK
+#define VECTORING_INFO_DELIEVER_CODE_MASK INTR_INFO_DELIEVER_CODE_MASK
+#define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK
+
+#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */
+#define INTR_TYPE_EXCEPTION (3 << 8) /* processor exception */
+
+/*
+ * Exit Qualifications for MOV for Control Register Access
+ */
+#define CONTROL_REG_ACCESS_NUM 0x7 /* 2:0, number of control register */
+#define CONTROL_REG_ACCESS_TYPE 0x30 /* 5:4, access type */
+#define CONTROL_REG_ACCESS_REG 0xf00 /* 10:8, general purpose register */
+#define LMSW_SOURCE_DATA_SHIFT 16
+#define LMSW_SOURCE_DATA (0xFFFF << LMSW_SOURCE_DATA_SHIFT) /* 16:31 lmsw source */
+#define REG_EAX (0 << 8)
+#define REG_ECX (1 << 8)
+#define REG_EDX (2 << 8)
+#define REG_EBX (3 << 8)
+#define REG_ESP (4 << 8)
+#define REG_EBP (5 << 8)
+#define REG_ESI (6 << 8)
+#define REG_EDI (7 << 8)
+#define REG_R8 (8 << 8)
+#define REG_R9 (9 << 8)
+#define REG_R10 (10 << 8)
+#define REG_R11 (11 << 8)
+#define REG_R12 (12 << 8)
+#define REG_R13 (13 << 8)
+#define REG_R14 (14 << 8)
+#define REG_R15 (15 << 8)
+
+/*
+ * Exit Qualifications for MOV for Debug Register Access
+ */
+#define DEBUG_REG_ACCESS_NUM 0x7 /* 2:0, number of debug register */
+#define DEBUG_REG_ACCESS_TYPE 0x10 /* 4, direction of access */
+#define TYPE_MOV_TO_DR (0 << 4)
+#define TYPE_MOV_FROM_DR (1 << 4)
+#define DEBUG_REG_ACCESS_REG 0xf00 /* 11:8, general purpose register */
+
+
+/* segment AR */
+#define SEGMENT_AR_L_MASK (1 << 13)
+
+/* entry controls */
+#define VM_ENTRY_CONTROLS_IA32E_MASK (1 << 9)
+
+#define AR_TYPE_ACCESSES_MASK 1
+#define AR_TYPE_READABLE_MASK (1 << 1)
+#define AR_TYPE_WRITEABLE_MASK (1 << 2)
+#define AR_TYPE_CODE_MASK (1 << 3)
+#define AR_TYPE_MASK 0x0f
+#define AR_TYPE_BUSY_64_TSS 11
+#define AR_TYPE_BUSY_32_TSS 11
+#define AR_TYPE_BUSY_16_TSS 3
+#define AR_TYPE_LDT 2
+
+#define AR_UNUSABLE_MASK (1 << 16)
+#define AR_S_MASK (1 << 4)
+#define AR_P_MASK (1 << 7)
+#define AR_L_MASK (1 << 13)
+#define AR_DB_MASK (1 << 14)
+#define AR_G_MASK (1 << 15)
+#define AR_DPL_SHIFT 5
+#define AR_DPL(ar) (((ar) >> AR_DPL_SHIFT) & 3)
+
+#define AR_RESERVD_MASK 0xfffe0f00
+
+#define CR4_VMXE 0x2000
+
+#define MSR_IA32_VMX_BASIC_MSR 0x480
+#define MSR_IA32_FEATURE_CONTROL 0x03a
+#define MSR_IA32_VMX_PINBASED_CTLS_MSR 0x481
+#define MSR_IA32_VMX_PROCBASED_CTLS_MSR 0x482
+#define MSR_IA32_VMX_EXIT_CTLS_MSR 0x483
+#define MSR_IA32_VMX_ENTRY_CTLS_MSR 0x484
+
+#endif
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c
new file mode 100644
index 0000000..1bff3e9
--- /dev/null
+++ b/drivers/kvm/x86_emulate.c
@@ -0,0 +1,1409 @@
+/******************************************************************************
+ * x86_emulate.c
+ *
+ * Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
+ *
+ * Copyright (c) 2005 Keir Fraser
+ *
+ * Linux coding style, mod r/m decoder, segment base fixes, real-mode
+ * privieged instructions:
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * From: xen-unstable 10676:af9809f51f81a3c43f276f00c81a52ef558afda4
+ */
+
+#ifndef __KERNEL__
+#include <stdio.h>
+#include <stdint.h>
+#include <public/xen.h>
+#define DPRINTF(_f, _a ...) printf( _f , ## _a )
+#else
+#include "kvm.h"
+#define DPRINTF(x...) do {} while (0)
+#endif
+#include "x86_emulate.h"
+#include <linux/module.h>
+
+/*
+ * Opcode effective-address decode tables.
+ * Note that we only emulate instructions that have at least one memory
+ * operand (excluding implicit stack references). We assume that stack
+ * references and instruction fetches will never occur in special memory
+ * areas that require emulation. So, for example, 'mov <imm>,<reg>' need
+ * not be handled.
+ */
+
+/* Operand sizes: 8-bit operands or specified/overridden size. */
+#define ByteOp (1<<0) /* 8-bit operands. */
+/* Destination operand type. */
+#define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */
+#define DstReg (2<<1) /* Register operand. */
+#define DstMem (3<<1) /* Memory operand. */
+#define DstMask (3<<1)
+/* Source operand type. */
+#define SrcNone (0<<3) /* No source operand. */
+#define SrcImplicit (0<<3) /* Source operand is implicit in the opcode. */
+#define SrcReg (1<<3) /* Register operand. */
+#define SrcMem (2<<3) /* Memory operand. */
+#define SrcMem16 (3<<3) /* Memory operand (16-bit). */
+#define SrcMem32 (4<<3) /* Memory operand (32-bit). */
+#define SrcImm (5<<3) /* Immediate operand. */
+#define SrcImmByte (6<<3) /* 8-bit sign-extended immediate operand. */
+#define SrcMask (7<<3)
+/* Generic ModRM decode. */
+#define ModRM (1<<6)
+/* Destination is only written; never read. */
+#define Mov (1<<7)
+
+static u8 opcode_table[256] = {
+ /* 0x00 - 0x07 */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x08 - 0x0F */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x10 - 0x17 */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x18 - 0x1F */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x20 - 0x27 */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x28 - 0x2F */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x30 - 0x37 */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x38 - 0x3F */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
+ 0, 0, 0, 0,
+ /* 0x40 - 0x4F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x50 - 0x5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x60 - 0x6F */
+ 0, 0, 0, DstReg | SrcMem32 | ModRM | Mov /* movsxd (x86/64) */ ,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x70 - 0x7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x80 - 0x87 */
+ ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImm | ModRM,
+ ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+ /* 0x88 - 0x8F */
+ ByteOp | DstMem | SrcReg | ModRM | Mov, DstMem | SrcReg | ModRM | Mov,
+ ByteOp | DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ 0, 0, 0, DstMem | SrcNone | ModRM | Mov,
+ /* 0x90 - 0x9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xA0 - 0xA7 */
+ ByteOp | DstReg | SrcMem | Mov, DstReg | SrcMem | Mov,
+ ByteOp | DstMem | SrcReg | Mov, DstMem | SrcReg | Mov,
+ ByteOp | ImplicitOps | Mov, ImplicitOps | Mov,
+ ByteOp | ImplicitOps, ImplicitOps,
+ /* 0xA8 - 0xAF */
+ 0, 0, ByteOp | ImplicitOps | Mov, ImplicitOps | Mov,
+ ByteOp | ImplicitOps | Mov, ImplicitOps | Mov,
+ ByteOp | ImplicitOps, ImplicitOps,
+ /* 0xB0 - 0xBF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xC0 - 0xC7 */
+ ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM, 0, 0,
+ 0, 0, ByteOp | DstMem | SrcImm | ModRM | Mov,
+ DstMem | SrcImm | ModRM | Mov,
+ /* 0xC8 - 0xCF */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xD0 - 0xD7 */
+ ByteOp | DstMem | SrcImplicit | ModRM, DstMem | SrcImplicit | ModRM,
+ ByteOp | DstMem | SrcImplicit | ModRM, DstMem | SrcImplicit | ModRM,
+ 0, 0, 0, 0,
+ /* 0xD8 - 0xDF */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xE0 - 0xEF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xF0 - 0xF7 */
+ 0, 0, 0, 0,
+ 0, 0, ByteOp | DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM,
+ /* 0xF8 - 0xFF */
+ 0, 0, 0, 0,
+ 0, 0, ByteOp | DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM
+};
+
+static u8 twobyte_table[256] = {
+ /* 0x00 - 0x0F */
+ 0, SrcMem | ModRM | DstReg, 0, 0, 0, 0, ImplicitOps, 0,
+ 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0,
+ /* 0x10 - 0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x20 - 0x2F */
+ ModRM | ImplicitOps, ModRM, ModRM | ImplicitOps, ModRM, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x30 - 0x3F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x40 - 0x47 */
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ /* 0x48 - 0x4F */
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
+ /* 0x50 - 0x5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x60 - 0x6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x70 - 0x7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x80 - 0x8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x90 - 0x9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xA0 - 0xA7 */
+ 0, 0, 0, DstMem | SrcReg | ModRM, 0, 0, 0, 0,
+ /* 0xA8 - 0xAF */
+ 0, 0, 0, DstMem | SrcReg | ModRM, 0, 0, 0, 0,
+ /* 0xB0 - 0xB7 */
+ ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM, 0,
+ DstMem | SrcReg | ModRM,
+ 0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem16 | ModRM | Mov,
+ /* 0xB8 - 0xBF */
+ 0, 0, DstMem | SrcImmByte | ModRM, DstMem | SrcReg | ModRM,
+ 0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
+ DstReg | SrcMem16 | ModRM | Mov,
+ /* 0xC0 - 0xCF */
+ 0, 0, 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xD0 - 0xDF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xE0 - 0xEF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xF0 - 0xFF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Tell the emulator that of the Group 7 instructions (sgdt, lidt, etc.) we
+ * are interested only in invlpg and not in any of the rest.
+ *
+ * invlpg is a special instruction in that the data it references may not
+ * be mapped.
+ */
+void kvm_emulator_want_group7_invlpg(void)
+{
+ twobyte_table[1] &= ~SrcMem;
+}
+EXPORT_SYMBOL_GPL(kvm_emulator_want_group7_invlpg);
+
+/* Type, address-of, and value of an instruction's operand. */
+struct operand {
+ enum { OP_REG, OP_MEM, OP_IMM } type;
+ unsigned int bytes;
+ unsigned long val, orig_val, *ptr;
+};
+
+/* EFLAGS bit definitions. */
+#define EFLG_OF (1<<11)
+#define EFLG_DF (1<<10)
+#define EFLG_SF (1<<7)
+#define EFLG_ZF (1<<6)
+#define EFLG_AF (1<<4)
+#define EFLG_PF (1<<2)
+#define EFLG_CF (1<<0)
+
+/*
+ * Instruction emulation:
+ * Most instructions are emulated directly via a fragment of inline assembly
+ * code. This allows us to save/restore EFLAGS and thus very easily pick up
+ * any modified flags.
+ */
+
+#if defined(CONFIG_X86_64)
+#define _LO32 "k" /* force 32-bit operand */
+#define _STK "%%rsp" /* stack pointer */
+#elif defined(__i386__)
+#define _LO32 "" /* force 32-bit operand */
+#define _STK "%%esp" /* stack pointer */
+#endif
+
+/*
+ * These EFLAGS bits are restored from saved value during emulation, and
+ * any changes are written back to the saved value after emulation.
+ */
+#define EFLAGS_MASK (EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_AF|EFLG_PF|EFLG_CF)
+
+/* Before executing instruction: restore necessary bits in EFLAGS. */
+#define _PRE_EFLAGS(_sav, _msk, _tmp) \
+ /* EFLAGS = (_sav & _msk) | (EFLAGS & ~_msk); */ \
+ "push %"_sav"; " \
+ "movl %"_msk",%"_LO32 _tmp"; " \
+ "andl %"_LO32 _tmp",("_STK"); " \
+ "pushf; " \
+ "notl %"_LO32 _tmp"; " \
+ "andl %"_LO32 _tmp",("_STK"); " \
+ "pop %"_tmp"; " \
+ "orl %"_LO32 _tmp",("_STK"); " \
+ "popf; " \
+ /* _sav &= ~msk; */ \
+ "movl %"_msk",%"_LO32 _tmp"; " \
+ "notl %"_LO32 _tmp"; " \
+ "andl %"_LO32 _tmp",%"_sav"; "
+
+/* After executing instruction: write-back necessary bits in EFLAGS. */
+#define _POST_EFLAGS(_sav, _msk, _tmp) \
+ /* _sav |= EFLAGS & _msk; */ \
+ "pushf; " \
+ "pop %"_tmp"; " \
+ "andl %"_msk",%"_LO32 _tmp"; " \
+ "orl %"_LO32 _tmp",%"_sav"; "
+
+/* Raw emulation: instruction has two explicit operands. */
+#define __emulate_2op_nobyte(_op,_src,_dst,_eflags,_wx,_wy,_lx,_ly,_qx,_qy) \
+ do { \
+ unsigned long _tmp; \
+ \
+ switch ((_dst).bytes) { \
+ case 2: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","4","2") \
+ _op"w %"_wx"3,%1; " \
+ _POST_EFLAGS("0","4","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : _wy ((_src).val), "i" (EFLAGS_MASK) ); \
+ break; \
+ case 4: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","4","2") \
+ _op"l %"_lx"3,%1; " \
+ _POST_EFLAGS("0","4","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : _ly ((_src).val), "i" (EFLAGS_MASK) ); \
+ break; \
+ case 8: \
+ __emulate_2op_8byte(_op, _src, _dst, \
+ _eflags, _qx, _qy); \
+ break; \
+ } \
+ } while (0)
+
+#define __emulate_2op(_op,_src,_dst,_eflags,_bx,_by,_wx,_wy,_lx,_ly,_qx,_qy) \
+ do { \
+ unsigned long _tmp; \
+ switch ( (_dst).bytes ) \
+ { \
+ case 1: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","4","2") \
+ _op"b %"_bx"3,%1; " \
+ _POST_EFLAGS("0","4","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : _by ((_src).val), "i" (EFLAGS_MASK) ); \
+ break; \
+ default: \
+ __emulate_2op_nobyte(_op, _src, _dst, _eflags, \
+ _wx, _wy, _lx, _ly, _qx, _qy); \
+ break; \
+ } \
+ } while (0)
+
+/* Source operand is byte-sized and may be restricted to just %cl. */
+#define emulate_2op_SrcB(_op, _src, _dst, _eflags) \
+ __emulate_2op(_op, _src, _dst, _eflags, \
+ "b", "c", "b", "c", "b", "c", "b", "c")
+
+/* Source operand is byte, word, long or quad sized. */
+#define emulate_2op_SrcV(_op, _src, _dst, _eflags) \
+ __emulate_2op(_op, _src, _dst, _eflags, \
+ "b", "q", "w", "r", _LO32, "r", "", "r")
+
+/* Source operand is word, long or quad sized. */
+#define emulate_2op_SrcV_nobyte(_op, _src, _dst, _eflags) \
+ __emulate_2op_nobyte(_op, _src, _dst, _eflags, \
+ "w", "r", _LO32, "r", "", "r")
+
+/* Instruction has only one explicit operand (no source operand). */
+#define emulate_1op(_op, _dst, _eflags) \
+ do { \
+ unsigned long _tmp; \
+ \
+ switch ( (_dst).bytes ) \
+ { \
+ case 1: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","3","2") \
+ _op"b %1; " \
+ _POST_EFLAGS("0","3","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : "i" (EFLAGS_MASK) ); \
+ break; \
+ case 2: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","3","2") \
+ _op"w %1; " \
+ _POST_EFLAGS("0","3","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : "i" (EFLAGS_MASK) ); \
+ break; \
+ case 4: \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","3","2") \
+ _op"l %1; " \
+ _POST_EFLAGS("0","3","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), \
+ "=&r" (_tmp) \
+ : "i" (EFLAGS_MASK) ); \
+ break; \
+ case 8: \
+ __emulate_1op_8byte(_op, _dst, _eflags); \
+ break; \
+ } \
+ } while (0)
+
+/* Emulate an instruction with quadword operands (x86/64 only). */
+#if defined(CONFIG_X86_64)
+#define __emulate_2op_8byte(_op, _src, _dst, _eflags, _qx, _qy) \
+ do { \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","4","2") \
+ _op"q %"_qx"3,%1; " \
+ _POST_EFLAGS("0","4","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
+ : _qy ((_src).val), "i" (EFLAGS_MASK) ); \
+ } while (0)
+
+#define __emulate_1op_8byte(_op, _dst, _eflags) \
+ do { \
+ __asm__ __volatile__ ( \
+ _PRE_EFLAGS("0","3","2") \
+ _op"q %1; " \
+ _POST_EFLAGS("0","3","2") \
+ : "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
+ : "i" (EFLAGS_MASK) ); \
+ } while (0)
+
+#elif defined(__i386__)
+#define __emulate_2op_8byte(_op, _src, _dst, _eflags, _qx, _qy)
+#define __emulate_1op_8byte(_op, _dst, _eflags)
+#endif /* __i386__ */
+
+/* Fetch next part of the instruction being emulated. */
+#define insn_fetch(_type, _size, _eip) \
+({ unsigned long _x; \
+ rc = ops->read_std((unsigned long)(_eip) + ctxt->cs_base, &_x, \
+ (_size), ctxt); \
+ if ( rc != 0 ) \
+ goto done; \
+ (_eip) += (_size); \
+ (_type)_x; \
+})
+
+/* Access/update address held in a register, based on addressing mode. */
+#define register_address(base, reg) \
+ ((base) + ((ad_bytes == sizeof(unsigned long)) ? (reg) : \
+ ((reg) & ((1UL << (ad_bytes << 3)) - 1))))
+
+#define register_address_increment(reg, inc) \
+ do { \
+ /* signed type ensures sign extension to long */ \
+ int _inc = (inc); \
+ if ( ad_bytes == sizeof(unsigned long) ) \
+ (reg) += _inc; \
+ else \
+ (reg) = ((reg) & ~((1UL << (ad_bytes << 3)) - 1)) | \
+ (((reg) + _inc) & ((1UL << (ad_bytes << 3)) - 1)); \
+ } while (0)
+
+void *decode_register(u8 modrm_reg, unsigned long *regs,
+ int highbyte_regs)
+{
+ void *p;
+
+ p = &regs[modrm_reg];
+ if (highbyte_regs && modrm_reg >= 4 && modrm_reg < 8)
+ p = (unsigned char *)&regs[modrm_reg & 3] + 1;
+ return p;
+}
+
+static int read_descriptor(struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops,
+ void *ptr,
+ u16 *size, unsigned long *address, int op_bytes)
+{
+ int rc;
+
+ if (op_bytes == 2)
+ op_bytes = 3;
+ *address = 0;
+ rc = ops->read_std((unsigned long)ptr, (unsigned long *)size, 2, ctxt);
+ if (rc)
+ return rc;
+ rc = ops->read_std((unsigned long)ptr + 2, address, op_bytes, ctxt);
+ return rc;
+}
+
+int
+x86_emulate_memop(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
+{
+ u8 b, d, sib, twobyte = 0, rex_prefix = 0;
+ u8 modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0;
+ unsigned long *override_base = NULL;
+ unsigned int op_bytes, ad_bytes, lock_prefix = 0, rep_prefix = 0, i;
+ int rc = 0;
+ struct operand src, dst;
+ unsigned long cr2 = ctxt->cr2;
+ int mode = ctxt->mode;
+ unsigned long modrm_ea;
+ int use_modrm_ea, index_reg = 0, base_reg = 0, scale, rip_relative = 0;
+
+ /* Shadow copy of register state. Committed on successful emulation. */
+ unsigned long _regs[NR_VCPU_REGS];
+ unsigned long _eip = ctxt->vcpu->rip, _eflags = ctxt->eflags;
+ unsigned long modrm_val = 0;
+
+ memcpy(_regs, ctxt->vcpu->regs, sizeof _regs);
+
+ switch (mode) {
+ case X86EMUL_MODE_REAL:
+ case X86EMUL_MODE_PROT16:
+ op_bytes = ad_bytes = 2;
+ break;
+ case X86EMUL_MODE_PROT32:
+ op_bytes = ad_bytes = 4;
+ break;
+#ifdef CONFIG_X86_64
+ case X86EMUL_MODE_PROT64:
+ op_bytes = 4;
+ ad_bytes = 8;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ /* Legacy prefixes. */
+ for (i = 0; i < 8; i++) {
+ switch (b = insn_fetch(u8, 1, _eip)) {
+ case 0x66: /* operand-size override */
+ op_bytes ^= 6; /* switch between 2/4 bytes */
+ break;
+ case 0x67: /* address-size override */
+ if (mode == X86EMUL_MODE_PROT64)
+ ad_bytes ^= 12; /* switch between 4/8 bytes */
+ else
+ ad_bytes ^= 6; /* switch between 2/4 bytes */
+ break;
+ case 0x2e: /* CS override */
+ override_base = &ctxt->cs_base;
+ break;
+ case 0x3e: /* DS override */
+ override_base = &ctxt->ds_base;
+ break;
+ case 0x26: /* ES override */
+ override_base = &ctxt->es_base;
+ break;
+ case 0x64: /* FS override */
+ override_base = &ctxt->fs_base;
+ break;
+ case 0x65: /* GS override */
+ override_base = &ctxt->gs_base;
+ break;
+ case 0x36: /* SS override */
+ override_base = &ctxt->ss_base;
+ break;
+ case 0xf0: /* LOCK */
+ lock_prefix = 1;
+ break;
+ case 0xf3: /* REP/REPE/REPZ */
+ rep_prefix = 1;
+ break;
+ case 0xf2: /* REPNE/REPNZ */
+ break;
+ default:
+ goto done_prefixes;
+ }
+ }
+
+done_prefixes:
+
+ /* REX prefix. */
+ if ((mode == X86EMUL_MODE_PROT64) && ((b & 0xf0) == 0x40)) {
+ rex_prefix = b;
+ if (b & 8)
+ op_bytes = 8; /* REX.W */
+ modrm_reg = (b & 4) << 1; /* REX.R */
+ index_reg = (b & 2) << 2; /* REX.X */
+ modrm_rm = base_reg = (b & 1) << 3; /* REG.B */
+ b = insn_fetch(u8, 1, _eip);
+ }
+
+ /* Opcode byte(s). */
+ d = opcode_table[b];
+ if (d == 0) {
+ /* Two-byte opcode? */
+ if (b == 0x0f) {
+ twobyte = 1;
+ b = insn_fetch(u8, 1, _eip);
+ d = twobyte_table[b];
+ }
+
+ /* Unrecognised? */
+ if (d == 0)
+ goto cannot_emulate;
+ }
+
+ /* ModRM and SIB bytes. */
+ if (d & ModRM) {
+ modrm = insn_fetch(u8, 1, _eip);
+ modrm_mod |= (modrm & 0xc0) >> 6;
+ modrm_reg |= (modrm & 0x38) >> 3;
+ modrm_rm |= (modrm & 0x07);
+ modrm_ea = 0;
+ use_modrm_ea = 1;
+
+ if (modrm_mod == 3) {
+ modrm_val = *(unsigned long *)
+ decode_register(modrm_rm, _regs, d & ByteOp);
+ goto modrm_done;
+ }
+
+ if (ad_bytes == 2) {
+ unsigned bx = _regs[VCPU_REGS_RBX];
+ unsigned bp = _regs[VCPU_REGS_RBP];
+ unsigned si = _regs[VCPU_REGS_RSI];
+ unsigned di = _regs[VCPU_REGS_RDI];
+
+ /* 16-bit ModR/M decode. */
+ switch (modrm_mod) {
+ case 0:
+ if (modrm_rm == 6)
+ modrm_ea += insn_fetch(u16, 2, _eip);
+ break;
+ case 1:
+ modrm_ea += insn_fetch(s8, 1, _eip);
+ break;
+ case 2:
+ modrm_ea += insn_fetch(u16, 2, _eip);
+ break;
+ }
+ switch (modrm_rm) {
+ case 0:
+ modrm_ea += bx + si;
+ break;
+ case 1:
+ modrm_ea += bx + di;
+ break;
+ case 2:
+ modrm_ea += bp + si;
+ break;
+ case 3:
+ modrm_ea += bp + di;
+ break;
+ case 4:
+ modrm_ea += si;
+ break;
+ case 5:
+ modrm_ea += di;
+ break;
+ case 6:
+ if (modrm_mod != 0)
+ modrm_ea += bp;
+ break;
+ case 7:
+ modrm_ea += bx;
+ break;
+ }
+ if (modrm_rm == 2 || modrm_rm == 3 ||
+ (modrm_rm == 6 && modrm_mod != 0))
+ if (!override_base)
+ override_base = &ctxt->ss_base;
+ modrm_ea = (u16)modrm_ea;
+ } else {
+ /* 32/64-bit ModR/M decode. */
+ switch (modrm_rm) {
+ case 4:
+ case 12:
+ sib = insn_fetch(u8, 1, _eip);
+ index_reg |= (sib >> 3) & 7;
+ base_reg |= sib & 7;
+ scale = sib >> 6;
+
+ switch (base_reg) {
+ case 5:
+ if (modrm_mod != 0)
+ modrm_ea += _regs[base_reg];
+ else
+ modrm_ea += insn_fetch(s32, 4, _eip);
+ break;
+ default:
+ modrm_ea += _regs[base_reg];
+ }
+ switch (index_reg) {
+ case 4:
+ break;
+ default:
+ modrm_ea += _regs[index_reg] << scale;
+
+ }
+ break;
+ case 5:
+ if (modrm_mod != 0)
+ modrm_ea += _regs[modrm_rm];
+ else if (mode == X86EMUL_MODE_PROT64)
+ rip_relative = 1;
+ break;
+ default:
+ modrm_ea += _regs[modrm_rm];
+ break;
+ }
+ switch (modrm_mod) {
+ case 0:
+ if (modrm_rm == 5)
+ modrm_ea += insn_fetch(s32, 4, _eip);
+ break;
+ case 1:
+ modrm_ea += insn_fetch(s8, 1, _eip);
+ break;
+ case 2:
+ modrm_ea += insn_fetch(s32, 4, _eip);
+ break;
+ }
+ }
+ if (!override_base)
+ override_base = &ctxt->ds_base;
+ if (mode == X86EMUL_MODE_PROT64 &&
+ override_base != &ctxt->fs_base &&
+ override_base != &ctxt->gs_base)
+ override_base = NULL;
+
+ if (override_base)
+ modrm_ea += *override_base;
+
+ if (rip_relative) {
+ modrm_ea += _eip;
+ switch (d & SrcMask) {
+ case SrcImmByte:
+ modrm_ea += 1;
+ break;
+ case SrcImm:
+ if (d & ByteOp)
+ modrm_ea += 1;
+ else
+ if (op_bytes == 8)
+ modrm_ea += 4;
+ else
+ modrm_ea += op_bytes;
+ }
+ }
+ if (ad_bytes != 8)
+ modrm_ea = (u32)modrm_ea;
+ cr2 = modrm_ea;
+ modrm_done:
+ ;
+ }
+
+ /* Decode and fetch the destination operand: register or memory. */
+ switch (d & DstMask) {
+ case ImplicitOps:
+ /* Special instructions do their own operand decoding. */
+ goto special_insn;
+ case DstReg:
+ dst.type = OP_REG;
+ if ((d & ByteOp)
+ && !(twobyte_table && (b == 0xb6 || b == 0xb7))) {
+ dst.ptr = decode_register(modrm_reg, _regs,
+ (rex_prefix == 0));
+ dst.val = *(u8 *) dst.ptr;
+ dst.bytes = 1;
+ } else {
+ dst.ptr = decode_register(modrm_reg, _regs, 0);
+ switch ((dst.bytes = op_bytes)) {
+ case 2:
+ dst.val = *(u16 *)dst.ptr;
+ break;
+ case 4:
+ dst.val = *(u32 *)dst.ptr;
+ break;
+ case 8:
+ dst.val = *(u64 *)dst.ptr;
+ break;
+ }
+ }
+ break;
+ case DstMem:
+ dst.type = OP_MEM;
+ dst.ptr = (unsigned long *)cr2;
+ dst.bytes = (d & ByteOp) ? 1 : op_bytes;
+ if (!(d & Mov) && /* optimisation - avoid slow emulated read */
+ ((rc = ops->read_emulated((unsigned long)dst.ptr,
+ &dst.val, dst.bytes, ctxt)) != 0))
+ goto done;
+ break;
+ }
+ dst.orig_val = dst.val;
+
+ /*
+ * Decode and fetch the source operand: register, memory
+ * or immediate.
+ */
+ switch (d & SrcMask) {
+ case SrcNone:
+ break;
+ case SrcReg:
+ src.type = OP_REG;
+ if (d & ByteOp) {
+ src.ptr = decode_register(modrm_reg, _regs,
+ (rex_prefix == 0));
+ src.val = src.orig_val = *(u8 *) src.ptr;
+ src.bytes = 1;
+ } else {
+ src.ptr = decode_register(modrm_reg, _regs, 0);
+ switch ((src.bytes = op_bytes)) {
+ case 2:
+ src.val = src.orig_val = *(u16 *) src.ptr;
+ break;
+ case 4:
+ src.val = src.orig_val = *(u32 *) src.ptr;
+ break;
+ case 8:
+ src.val = src.orig_val = *(u64 *) src.ptr;
+ break;
+ }
+ }
+ break;
+ case SrcMem16:
+ src.bytes = 2;
+ goto srcmem_common;
+ case SrcMem32:
+ src.bytes = 4;
+ goto srcmem_common;
+ case SrcMem:
+ src.bytes = (d & ByteOp) ? 1 : op_bytes;
+ srcmem_common:
+ src.type = OP_MEM;
+ src.ptr = (unsigned long *)cr2;
+ if ((rc = ops->read_emulated((unsigned long)src.ptr,
+ &src.val, src.bytes, ctxt)) != 0)
+ goto done;
+ src.orig_val = src.val;
+ break;
+ case SrcImm:
+ src.type = OP_IMM;
+ src.ptr = (unsigned long *)_eip;
+ src.bytes = (d & ByteOp) ? 1 : op_bytes;
+ if (src.bytes == 8)
+ src.bytes = 4;
+ /* NB. Immediates are sign-extended as necessary. */
+ switch (src.bytes) {
+ case 1:
+ src.val = insn_fetch(s8, 1, _eip);
+ break;
+ case 2:
+ src.val = insn_fetch(s16, 2, _eip);
+ break;
+ case 4:
+ src.val = insn_fetch(s32, 4, _eip);
+ break;
+ }
+ break;
+ case SrcImmByte:
+ src.type = OP_IMM;
+ src.ptr = (unsigned long *)_eip;
+ src.bytes = 1;
+ src.val = insn_fetch(s8, 1, _eip);
+ break;
+ }
+
+ if (twobyte)
+ goto twobyte_insn;
+
+ switch (b) {
+ case 0x00 ... 0x05:
+ add: /* add */
+ emulate_2op_SrcV("add", src, dst, _eflags);
+ break;
+ case 0x08 ... 0x0d:
+ or: /* or */
+ emulate_2op_SrcV("or", src, dst, _eflags);
+ break;
+ case 0x10 ... 0x15:
+ adc: /* adc */
+ emulate_2op_SrcV("adc", src, dst, _eflags);
+ break;
+ case 0x18 ... 0x1d:
+ sbb: /* sbb */
+ emulate_2op_SrcV("sbb", src, dst, _eflags);
+ break;
+ case 0x20 ... 0x25:
+ and: /* and */
+ emulate_2op_SrcV("and", src, dst, _eflags);
+ break;
+ case 0x28 ... 0x2d:
+ sub: /* sub */
+ emulate_2op_SrcV("sub", src, dst, _eflags);
+ break;
+ case 0x30 ... 0x35:
+ xor: /* xor */
+ emulate_2op_SrcV("xor", src, dst, _eflags);
+ break;
+ case 0x38 ... 0x3d:
+ cmp: /* cmp */
+ emulate_2op_SrcV("cmp", src, dst, _eflags);
+ break;
+ case 0x63: /* movsxd */
+ if (mode != X86EMUL_MODE_PROT64)
+ goto cannot_emulate;
+ dst.val = (s32) src.val;
+ break;
+ case 0x80 ... 0x83: /* Grp1 */
+ switch (modrm_reg) {
+ case 0:
+ goto add;
+ case 1:
+ goto or;
+ case 2:
+ goto adc;
+ case 3:
+ goto sbb;
+ case 4:
+ goto and;
+ case 5:
+ goto sub;
+ case 6:
+ goto xor;
+ case 7:
+ goto cmp;
+ }
+ break;
+ case 0x84 ... 0x85:
+ test: /* test */
+ emulate_2op_SrcV("test", src, dst, _eflags);
+ break;
+ case 0x86 ... 0x87: /* xchg */
+ /* Write back the register source. */
+ switch (dst.bytes) {
+ case 1:
+ *(u8 *) src.ptr = (u8) dst.val;
+ break;
+ case 2:
+ *(u16 *) src.ptr = (u16) dst.val;
+ break;
+ case 4:
+ *src.ptr = (u32) dst.val;
+ break; /* 64b reg: zero-extend */
+ case 8:
+ *src.ptr = dst.val;
+ break;
+ }
+ /*
+ * Write back the memory destination with implicit LOCK
+ * prefix.
+ */
+ dst.val = src.val;
+ lock_prefix = 1;
+ break;
+ case 0xa0 ... 0xa1: /* mov */
+ dst.ptr = (unsigned long *)&_regs[VCPU_REGS_RAX];
+ dst.val = src.val;
+ _eip += ad_bytes; /* skip src displacement */
+ break;
+ case 0xa2 ... 0xa3: /* mov */
+ dst.val = (unsigned long)_regs[VCPU_REGS_RAX];
+ _eip += ad_bytes; /* skip dst displacement */
+ break;
+ case 0x88 ... 0x8b: /* mov */
+ case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */
+ dst.val = src.val;
+ break;
+ case 0x8f: /* pop (sole member of Grp1a) */
+ /* 64-bit mode: POP always pops a 64-bit operand. */
+ if (mode == X86EMUL_MODE_PROT64)
+ dst.bytes = 8;
+ if ((rc = ops->read_std(register_address(ctxt->ss_base,
+ _regs[VCPU_REGS_RSP]),
+ &dst.val, dst.bytes, ctxt)) != 0)
+ goto done;
+ register_address_increment(_regs[VCPU_REGS_RSP], dst.bytes);
+ break;
+ case 0xc0 ... 0xc1:
+ grp2: /* Grp2 */
+ switch (modrm_reg) {
+ case 0: /* rol */
+ emulate_2op_SrcB("rol", src, dst, _eflags);
+ break;
+ case 1: /* ror */
+ emulate_2op_SrcB("ror", src, dst, _eflags);
+ break;
+ case 2: /* rcl */
+ emulate_2op_SrcB("rcl", src, dst, _eflags);
+ break;
+ case 3: /* rcr */
+ emulate_2op_SrcB("rcr", src, dst, _eflags);
+ break;
+ case 4: /* sal/shl */
+ case 6: /* sal/shl */
+ emulate_2op_SrcB("sal", src, dst, _eflags);
+ break;
+ case 5: /* shr */
+ emulate_2op_SrcB("shr", src, dst, _eflags);
+ break;
+ case 7: /* sar */
+ emulate_2op_SrcB("sar", src, dst, _eflags);
+ break;
+ }
+ break;
+ case 0xd0 ... 0xd1: /* Grp2 */
+ src.val = 1;
+ goto grp2;
+ case 0xd2 ... 0xd3: /* Grp2 */
+ src.val = _regs[VCPU_REGS_RCX];
+ goto grp2;
+ case 0xf6 ... 0xf7: /* Grp3 */
+ switch (modrm_reg) {
+ case 0 ... 1: /* test */
+ /*
+ * Special case in Grp3: test has an immediate
+ * source operand.
+ */
+ src.type = OP_IMM;
+ src.ptr = (unsigned long *)_eip;
+ src.bytes = (d & ByteOp) ? 1 : op_bytes;
+ if (src.bytes == 8)
+ src.bytes = 4;
+ switch (src.bytes) {
+ case 1:
+ src.val = insn_fetch(s8, 1, _eip);
+ break;
+ case 2:
+ src.val = insn_fetch(s16, 2, _eip);
+ break;
+ case 4:
+ src.val = insn_fetch(s32, 4, _eip);
+ break;
+ }
+ goto test;
+ case 2: /* not */
+ dst.val = ~dst.val;
+ break;
+ case 3: /* neg */
+ emulate_1op("neg", dst, _eflags);
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ break;
+ case 0xfe ... 0xff: /* Grp4/Grp5 */
+ switch (modrm_reg) {
+ case 0: /* inc */
+ emulate_1op("inc", dst, _eflags);
+ break;
+ case 1: /* dec */
+ emulate_1op("dec", dst, _eflags);
+ break;
+ case 6: /* push */
+ /* 64-bit mode: PUSH always pushes a 64-bit operand. */
+ if (mode == X86EMUL_MODE_PROT64) {
+ dst.bytes = 8;
+ if ((rc = ops->read_std((unsigned long)dst.ptr,
+ &dst.val, 8,
+ ctxt)) != 0)
+ goto done;
+ }
+ register_address_increment(_regs[VCPU_REGS_RSP],
+ -dst.bytes);
+ if ((rc = ops->write_std(
+ register_address(ctxt->ss_base,
+ _regs[VCPU_REGS_RSP]),
+ dst.val, dst.bytes, ctxt)) != 0)
+ goto done;
+ dst.val = dst.orig_val; /* skanky: disable writeback */
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ break;
+ }
+
+writeback:
+ if ((d & Mov) || (dst.orig_val != dst.val)) {
+ switch (dst.type) {
+ case OP_REG:
+ /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
+ switch (dst.bytes) {
+ case 1:
+ *(u8 *)dst.ptr = (u8)dst.val;
+ break;
+ case 2:
+ *(u16 *)dst.ptr = (u16)dst.val;
+ break;
+ case 4:
+ *dst.ptr = (u32)dst.val;
+ break; /* 64b: zero-ext */
+ case 8:
+ *dst.ptr = dst.val;
+ break;
+ }
+ break;
+ case OP_MEM:
+ if (lock_prefix)
+ rc = ops->cmpxchg_emulated((unsigned long)dst.
+ ptr, dst.orig_val,
+ dst.val, dst.bytes,
+ ctxt);
+ else
+ rc = ops->write_emulated((unsigned long)dst.ptr,
+ dst.val, dst.bytes,
+ ctxt);
+ if (rc != 0)
+ goto done;
+ default:
+ break;
+ }
+ }
+
+ /* Commit shadow register state. */
+ memcpy(ctxt->vcpu->regs, _regs, sizeof _regs);
+ ctxt->eflags = _eflags;
+ ctxt->vcpu->rip = _eip;
+
+done:
+ return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
+
+special_insn:
+ if (twobyte)
+ goto twobyte_special_insn;
+ if (rep_prefix) {
+ if (_regs[VCPU_REGS_RCX] == 0) {
+ ctxt->vcpu->rip = _eip;
+ goto done;
+ }
+ _regs[VCPU_REGS_RCX]--;
+ _eip = ctxt->vcpu->rip;
+ }
+ switch (b) {
+ case 0xa4 ... 0xa5: /* movs */
+ dst.type = OP_MEM;
+ dst.bytes = (d & ByteOp) ? 1 : op_bytes;
+ dst.ptr = (unsigned long *)register_address(ctxt->es_base,
+ _regs[VCPU_REGS_RDI]);
+ if ((rc = ops->read_emulated(register_address(
+ override_base ? *override_base : ctxt->ds_base,
+ _regs[VCPU_REGS_RSI]), &dst.val, dst.bytes, ctxt)) != 0)
+ goto done;
+ register_address_increment(_regs[VCPU_REGS_RSI],
+ (_eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
+ register_address_increment(_regs[VCPU_REGS_RDI],
+ (_eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
+ break;
+ case 0xa6 ... 0xa7: /* cmps */
+ DPRINTF("Urk! I don't handle CMPS.\n");
+ goto cannot_emulate;
+ case 0xaa ... 0xab: /* stos */
+ dst.type = OP_MEM;
+ dst.bytes = (d & ByteOp) ? 1 : op_bytes;
+ dst.ptr = (unsigned long *)cr2;
+ dst.val = _regs[VCPU_REGS_RAX];
+ register_address_increment(_regs[VCPU_REGS_RDI],
+ (_eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
+ break;
+ case 0xac ... 0xad: /* lods */
+ dst.type = OP_REG;
+ dst.bytes = (d & ByteOp) ? 1 : op_bytes;
+ dst.ptr = (unsigned long *)&_regs[VCPU_REGS_RAX];
+ if ((rc = ops->read_emulated(cr2, &dst.val, dst.bytes, ctxt)) != 0)
+ goto done;
+ register_address_increment(_regs[VCPU_REGS_RSI],
+ (_eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
+ break;
+ case 0xae ... 0xaf: /* scas */
+ DPRINTF("Urk! I don't handle SCAS.\n");
+ goto cannot_emulate;
+ }
+ goto writeback;
+
+twobyte_insn:
+ switch (b) {
+ case 0x01: /* lgdt, lidt, lmsw */
+ switch (modrm_reg) {
+ u16 size;
+ unsigned long address;
+
+ case 2: /* lgdt */
+ rc = read_descriptor(ctxt, ops, src.ptr,
+ &size, &address, op_bytes);
+ if (rc)
+ goto done;
+ realmode_lgdt(ctxt->vcpu, size, address);
+ break;
+ case 3: /* lidt */
+ rc = read_descriptor(ctxt, ops, src.ptr,
+ &size, &address, op_bytes);
+ if (rc)
+ goto done;
+ realmode_lidt(ctxt->vcpu, size, address);
+ break;
+ case 4: /* smsw */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ *(u16 *)&_regs[modrm_rm]
+ = realmode_get_cr(ctxt->vcpu, 0);
+ break;
+ case 6: /* lmsw */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ realmode_lmsw(ctxt->vcpu, (u16)modrm_val, &_eflags);
+ break;
+ case 7: /* invlpg*/
+ emulate_invlpg(ctxt->vcpu, cr2);
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ break;
+ case 0x21: /* mov from dr to reg */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ rc = emulator_get_dr(ctxt, modrm_reg, &_regs[modrm_rm]);
+ break;
+ case 0x23: /* mov from reg to dr */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ rc = emulator_set_dr(ctxt, modrm_reg, _regs[modrm_rm]);
+ break;
+ case 0x40 ... 0x4f: /* cmov */
+ dst.val = dst.orig_val = src.val;
+ d &= ~Mov; /* default to no move */
+ /*
+ * First, assume we're decoding an even cmov opcode
+ * (lsb == 0).
+ */
+ switch ((b & 15) >> 1) {
+ case 0: /* cmovo */
+ d |= (_eflags & EFLG_OF) ? Mov : 0;
+ break;
+ case 1: /* cmovb/cmovc/cmovnae */
+ d |= (_eflags & EFLG_CF) ? Mov : 0;
+ break;
+ case 2: /* cmovz/cmove */
+ d |= (_eflags & EFLG_ZF) ? Mov : 0;
+ break;
+ case 3: /* cmovbe/cmovna */
+ d |= (_eflags & (EFLG_CF | EFLG_ZF)) ? Mov : 0;
+ break;
+ case 4: /* cmovs */
+ d |= (_eflags & EFLG_SF) ? Mov : 0;
+ break;
+ case 5: /* cmovp/cmovpe */
+ d |= (_eflags & EFLG_PF) ? Mov : 0;
+ break;
+ case 7: /* cmovle/cmovng */
+ d |= (_eflags & EFLG_ZF) ? Mov : 0;
+ /* fall through */
+ case 6: /* cmovl/cmovnge */
+ d |= (!(_eflags & EFLG_SF) !=
+ !(_eflags & EFLG_OF)) ? Mov : 0;
+ break;
+ }
+ /* Odd cmov opcodes (lsb == 1) have inverted sense. */
+ d ^= (b & 1) ? Mov : 0;
+ break;
+ case 0xb0 ... 0xb1: /* cmpxchg */
+ /*
+ * Save real source value, then compare EAX against
+ * destination.
+ */
+ src.orig_val = src.val;
+ src.val = _regs[VCPU_REGS_RAX];
+ emulate_2op_SrcV("cmp", src, dst, _eflags);
+ /* Always write back. The question is: where to? */
+ d |= Mov;
+ if (_eflags & EFLG_ZF) {
+ /* Success: write back to memory. */
+ dst.val = src.orig_val;
+ } else {
+ /* Failure: write the value we saw to EAX. */
+ dst.type = OP_REG;
+ dst.ptr = (unsigned long *)&_regs[VCPU_REGS_RAX];
+ }
+ break;
+ case 0xa3:
+ bt: /* bt */
+ src.val &= (dst.bytes << 3) - 1; /* only subword offset */
+ emulate_2op_SrcV_nobyte("bt", src, dst, _eflags);
+ break;
+ case 0xb3:
+ btr: /* btr */
+ src.val &= (dst.bytes << 3) - 1; /* only subword offset */
+ emulate_2op_SrcV_nobyte("btr", src, dst, _eflags);
+ break;
+ case 0xab:
+ bts: /* bts */
+ src.val &= (dst.bytes << 3) - 1; /* only subword offset */
+ emulate_2op_SrcV_nobyte("bts", src, dst, _eflags);
+ break;
+ case 0xb6 ... 0xb7: /* movzx */
+ dst.bytes = op_bytes;
+ dst.val = (d & ByteOp) ? (u8) src.val : (u16) src.val;
+ break;
+ case 0xbb:
+ btc: /* btc */
+ src.val &= (dst.bytes << 3) - 1; /* only subword offset */
+ emulate_2op_SrcV_nobyte("btc", src, dst, _eflags);
+ break;
+ case 0xba: /* Grp8 */
+ switch (modrm_reg & 3) {
+ case 0:
+ goto bt;
+ case 1:
+ goto bts;
+ case 2:
+ goto btr;
+ case 3:
+ goto btc;
+ }
+ break;
+ case 0xbe ... 0xbf: /* movsx */
+ dst.bytes = op_bytes;
+ dst.val = (d & ByteOp) ? (s8) src.val : (s16) src.val;
+ break;
+ }
+ goto writeback;
+
+twobyte_special_insn:
+ /* Disable writeback. */
+ dst.orig_val = dst.val;
+ switch (b) {
+ case 0x0d: /* GrpP (prefetch) */
+ case 0x18: /* Grp16 (prefetch/nop) */
+ break;
+ case 0x06:
+ emulate_clts(ctxt->vcpu);
+ break;
+ case 0x20: /* mov cr, reg */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ _regs[modrm_rm] = realmode_get_cr(ctxt->vcpu, modrm_reg);
+ break;
+ case 0x22: /* mov reg, cr */
+ if (modrm_mod != 3)
+ goto cannot_emulate;
+ realmode_set_cr(ctxt->vcpu, modrm_reg, modrm_val, &_eflags);
+ break;
+ case 0xc7: /* Grp9 (cmpxchg8b) */
+#if defined(__i386__)
+ {
+ unsigned long old_lo, old_hi;
+ if (((rc = ops->read_emulated(cr2 + 0, &old_lo, 4,
+ ctxt)) != 0)
+ || ((rc = ops->read_emulated(cr2 + 4, &old_hi, 4,
+ ctxt)) != 0))
+ goto done;
+ if ((old_lo != _regs[VCPU_REGS_RAX])
+ || (old_hi != _regs[VCPU_REGS_RDI])) {
+ _regs[VCPU_REGS_RAX] = old_lo;
+ _regs[VCPU_REGS_RDX] = old_hi;
+ _eflags &= ~EFLG_ZF;
+ } else if (ops->cmpxchg8b_emulated == NULL) {
+ rc = X86EMUL_UNHANDLEABLE;
+ goto done;
+ } else {
+ if ((rc = ops->cmpxchg8b_emulated(cr2, old_lo,
+ old_hi,
+ _regs[VCPU_REGS_RBX],
+ _regs[VCPU_REGS_RCX],
+ ctxt)) != 0)
+ goto done;
+ _eflags |= EFLG_ZF;
+ }
+ break;
+ }
+#elif defined(CONFIG_X86_64)
+ {
+ unsigned long old, new;
+ if ((rc = ops->read_emulated(cr2, &old, 8, ctxt)) != 0)
+ goto done;
+ if (((u32) (old >> 0) != (u32) _regs[VCPU_REGS_RAX]) ||
+ ((u32) (old >> 32) != (u32) _regs[VCPU_REGS_RDX])) {
+ _regs[VCPU_REGS_RAX] = (u32) (old >> 0);
+ _regs[VCPU_REGS_RDX] = (u32) (old >> 32);
+ _eflags &= ~EFLG_ZF;
+ } else {
+ new = (_regs[VCPU_REGS_RCX] << 32) | (u32) _regs[VCPU_REGS_RBX];
+ if ((rc = ops->cmpxchg_emulated(cr2, old,
+ new, 8, ctxt)) != 0)
+ goto done;
+ _eflags |= EFLG_ZF;
+ }
+ break;
+ }
+#endif
+ }
+ goto writeback;
+
+cannot_emulate:
+ DPRINTF("Cannot emulate %02x\n", b);
+ return -1;
+}
+
+#ifdef __XEN__
+
+#include <asm/mm.h>
+#include <asm/uaccess.h>
+
+int
+x86_emulate_read_std(unsigned long addr,
+ unsigned long *val,
+ unsigned int bytes, struct x86_emulate_ctxt *ctxt)
+{
+ unsigned int rc;
+
+ *val = 0;
+
+ if ((rc = copy_from_user((void *)val, (void *)addr, bytes)) != 0) {
+ propagate_page_fault(addr + bytes - rc, 0); /* read fault */
+ return X86EMUL_PROPAGATE_FAULT;
+ }
+
+ return X86EMUL_CONTINUE;
+}
+
+int
+x86_emulate_write_std(unsigned long addr,
+ unsigned long val,
+ unsigned int bytes, struct x86_emulate_ctxt *ctxt)
+{
+ unsigned int rc;
+
+ if ((rc = copy_to_user((void *)addr, (void *)&val, bytes)) != 0) {
+ propagate_page_fault(addr + bytes - rc, PGERR_write_access);
+ return X86EMUL_PROPAGATE_FAULT;
+ }
+
+ return X86EMUL_CONTINUE;
+}
+
+#endif
diff --git a/drivers/kvm/x86_emulate.h b/drivers/kvm/x86_emulate.h
new file mode 100644
index 0000000..5d41bd5
--- /dev/null
+++ b/drivers/kvm/x86_emulate.h
@@ -0,0 +1,185 @@
+/******************************************************************************
+ * x86_emulate.h
+ *
+ * Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
+ *
+ * Copyright (c) 2005 Keir Fraser
+ *
+ * From: xen-unstable 10676:af9809f51f81a3c43f276f00c81a52ef558afda4
+ */
+
+#ifndef __X86_EMULATE_H__
+#define __X86_EMULATE_H__
+
+struct x86_emulate_ctxt;
+
+/*
+ * x86_emulate_ops:
+ *
+ * These operations represent the instruction emulator's interface to memory.
+ * There are two categories of operation: those that act on ordinary memory
+ * regions (*_std), and those that act on memory regions known to require
+ * special treatment or emulation (*_emulated).
+ *
+ * The emulator assumes that an instruction accesses only one 'emulated memory'
+ * location, that this location is the given linear faulting address (cr2), and
+ * that this is one of the instruction's data operands. Instruction fetches and
+ * stack operations are assumed never to access emulated memory. The emulator
+ * automatically deduces which operand of a string-move operation is accessing
+ * emulated memory, and assumes that the other operand accesses normal memory.
+ *
+ * NOTES:
+ * 1. The emulator isn't very smart about emulated vs. standard memory.
+ * 'Emulated memory' access addresses should be checked for sanity.
+ * 'Normal memory' accesses may fault, and the caller must arrange to
+ * detect and handle reentrancy into the emulator via recursive faults.
+ * Accesses may be unaligned and may cross page boundaries.
+ * 2. If the access fails (cannot emulate, or a standard access faults) then
+ * it is up to the memop to propagate the fault to the guest VM via
+ * some out-of-band mechanism, unknown to the emulator. The memop signals
+ * failure by returning X86EMUL_PROPAGATE_FAULT to the emulator, which will
+ * then immediately bail.
+ * 3. Valid access sizes are 1, 2, 4 and 8 bytes. On x86/32 systems only
+ * cmpxchg8b_emulated need support 8-byte accesses.
+ * 4. The emulator cannot handle 64-bit mode emulation on an x86/32 system.
+ */
+/* Access completed successfully: continue emulation as normal. */
+#define X86EMUL_CONTINUE 0
+/* Access is unhandleable: bail from emulation and return error to caller. */
+#define X86EMUL_UNHANDLEABLE 1
+/* Terminate emulation but return success to the caller. */
+#define X86EMUL_PROPAGATE_FAULT 2 /* propagate a generated fault to guest */
+#define X86EMUL_RETRY_INSTR 2 /* retry the instruction for some reason */
+#define X86EMUL_CMPXCHG_FAILED 2 /* cmpxchg did not see expected value */
+struct x86_emulate_ops {
+ /*
+ * read_std: Read bytes of standard (non-emulated/special) memory.
+ * Used for instruction fetch, stack operations, and others.
+ * @addr: [IN ] Linear address from which to read.
+ * @val: [OUT] Value read from memory, zero-extended to 'u_long'.
+ * @bytes: [IN ] Number of bytes to read from memory.
+ */
+ int (*read_std)(unsigned long addr,
+ unsigned long *val,
+ unsigned int bytes, struct x86_emulate_ctxt * ctxt);
+
+ /*
+ * write_std: Write bytes of standard (non-emulated/special) memory.
+ * Used for stack operations, and others.
+ * @addr: [IN ] Linear address to which to write.
+ * @val: [IN ] Value to write to memory (low-order bytes used as
+ * required).
+ * @bytes: [IN ] Number of bytes to write to memory.
+ */
+ int (*write_std)(unsigned long addr,
+ unsigned long val,
+ unsigned int bytes, struct x86_emulate_ctxt * ctxt);
+
+ /*
+ * read_emulated: Read bytes from emulated/special memory area.
+ * @addr: [IN ] Linear address from which to read.
+ * @val: [OUT] Value read from memory, zero-extended to 'u_long'.
+ * @bytes: [IN ] Number of bytes to read from memory.
+ */
+ int (*read_emulated) (unsigned long addr,
+ unsigned long *val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt * ctxt);
+
+ /*
+ * write_emulated: Read bytes from emulated/special memory area.
+ * @addr: [IN ] Linear address to which to write.
+ * @val: [IN ] Value to write to memory (low-order bytes used as
+ * required).
+ * @bytes: [IN ] Number of bytes to write to memory.
+ */
+ int (*write_emulated) (unsigned long addr,
+ unsigned long val,
+ unsigned int bytes,
+ struct x86_emulate_ctxt * ctxt);
+
+ /*
+ * cmpxchg_emulated: Emulate an atomic (LOCKed) CMPXCHG operation on an
+ * emulated/special memory area.
+ * @addr: [IN ] Linear address to access.
+ * @old: [IN ] Value expected to be current at @addr.
+ * @new: [IN ] Value to write to @addr.
+ * @bytes: [IN ] Number of bytes to access using CMPXCHG.
+ */
+ int (*cmpxchg_emulated) (unsigned long addr,
+ unsigned long old,
+ unsigned long new,
+ unsigned int bytes,
+ struct x86_emulate_ctxt * ctxt);
+
+ /*
+ * cmpxchg8b_emulated: Emulate an atomic (LOCKed) CMPXCHG8B operation on an
+ * emulated/special memory area.
+ * @addr: [IN ] Linear address to access.
+ * @old: [IN ] Value expected to be current at @addr.
+ * @new: [IN ] Value to write to @addr.
+ * NOTES:
+ * 1. This function is only ever called when emulating a real CMPXCHG8B.
+ * 2. This function is *never* called on x86/64 systems.
+ * 2. Not defining this function (i.e., specifying NULL) is equivalent
+ * to defining a function that always returns X86EMUL_UNHANDLEABLE.
+ */
+ int (*cmpxchg8b_emulated) (unsigned long addr,
+ unsigned long old_lo,
+ unsigned long old_hi,
+ unsigned long new_lo,
+ unsigned long new_hi,
+ struct x86_emulate_ctxt * ctxt);
+};
+
+struct cpu_user_regs;
+
+struct x86_emulate_ctxt {
+ /* Register state before/after emulation. */
+ struct kvm_vcpu *vcpu;
+
+ /* Linear faulting address (if emulating a page-faulting instruction). */
+ unsigned long eflags;
+ unsigned long cr2;
+
+ /* Emulated execution mode, represented by an X86EMUL_MODE value. */
+ int mode;
+
+ unsigned long cs_base;
+ unsigned long ds_base;
+ unsigned long es_base;
+ unsigned long ss_base;
+ unsigned long gs_base;
+ unsigned long fs_base;
+};
+
+/* Execution mode, passed to the emulator. */
+#define X86EMUL_MODE_REAL 0 /* Real mode. */
+#define X86EMUL_MODE_PROT16 2 /* 16-bit protected mode. */
+#define X86EMUL_MODE_PROT32 4 /* 32-bit protected mode. */
+#define X86EMUL_MODE_PROT64 8 /* 64-bit (long) mode. */
+
+/* Host execution mode. */
+#if defined(__i386__)
+#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT32
+#elif defined(CONFIG_X86_64)
+#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT64
+#endif
+
+/*
+ * x86_emulate_memop: Emulate an instruction that faulted attempting to
+ * read/write a 'special' memory area.
+ * Returns -1 on failure, 0 on success.
+ */
+int x86_emulate_memop(struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops);
+
+/*
+ * Given the 'reg' portion of a ModRM byte, and a register block, return a
+ * pointer into the block that addresses the relevant register.
+ * @highbyte_regs specifies whether to decode AH,CH,DH,BH.
+ */
+void *decode_register(u8 modrm_reg, unsigned long *regs,
+ int highbyte_regs);
+
+#endif /* __X86_EMULATE_H__ */
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 176142c..7399ba7 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -12,7 +12,7 @@ config NEW_LEDS
config LEDS_CLASS
tristate "LED Class Support"
- depends NEW_LEDS
+ depends on NEW_LEDS
help
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
@@ -21,28 +21,28 @@ comment "LED drivers"
config LEDS_CORGI
tristate "LED Support for the Sharp SL-C7x0 series"
- depends LEDS_CLASS && PXA_SHARP_C7xx
+ depends on LEDS_CLASS && PXA_SHARP_C7xx
help
This option enables support for the LEDs on Sharp Zaurus
SL-C7x0 series (C700, C750, C760, C860).
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
- depends LEDS_CLASS && SHARP_LOCOMO
+ depends on LEDS_CLASS && SHARP_LOCOMO
help
This option enables support for the LEDs on Sharp Locomo.
Zaurus models SL-5500 and SL-5600.
config LEDS_SPITZ
tristate "LED Support for the Sharp SL-Cxx00 series"
- depends LEDS_CLASS && PXA_SHARP_Cxx00
+ depends on LEDS_CLASS && PXA_SHARP_Cxx00
help
This option enables support for the LEDs on Sharp Zaurus
SL-Cxx00 series (C1000, C3000, C3100).
config LEDS_IXP4XX
tristate "LED Support for GPIO connected LEDs on IXP4XX processors"
- depends LEDS_CLASS && ARCH_IXP4XX
+ depends on LEDS_CLASS && ARCH_IXP4XX
help
This option enables support for the LEDs connected to GPIO
outputs of the Intel IXP4XX processors. To be useful the
@@ -51,7 +51,7 @@ config LEDS_IXP4XX
config LEDS_TOSA
tristate "LED Support for the Sharp SL-6000 series"
- depends LEDS_CLASS && PXA_SHARPSL
+ depends on LEDS_CLASS && PXA_SHARPSL
help
This option enables support for the LEDs on Sharp Zaurus
SL-6000 series.
@@ -65,7 +65,7 @@ config LEDS_S3C24XX
config LEDS_AMS_DELTA
tristate "LED Support for the Amstrad Delta (E3)"
- depends LEDS_CLASS && MACH_AMS_DELTA
+ depends on LEDS_CLASS && MACH_AMS_DELTA
help
This option enables support for the LEDs on Amstrad Delta (E3).
@@ -86,7 +86,7 @@ comment "LED Triggers"
config LEDS_TRIGGERS
bool "LED Trigger support"
- depends NEW_LEDS
+ depends on NEW_LEDS
help
This option enables trigger support for the leds class.
These triggers allow kernel events to drive the LEDs and can
@@ -94,21 +94,21 @@ config LEDS_TRIGGERS
config LEDS_TRIGGER_TIMER
tristate "LED Timer Trigger"
- depends LEDS_TRIGGERS
+ depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by a programmable timer
via sysfs. If unsure, say Y.
config LEDS_TRIGGER_IDE_DISK
bool "LED IDE Disk Trigger"
- depends LEDS_TRIGGERS && BLK_DEV_IDEDISK
+ depends on LEDS_TRIGGERS && BLK_DEV_IDEDISK
help
This allows LEDs to be controlled by IDE disk activity.
If unsure, say Y.
config LEDS_TRIGGER_HEARTBEAT
tristate "LED Heartbeat Trigger"
- depends LEDS_TRIGGERS
+ depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by a CPU load average.
The flash frequency is a hyperbolic function of the 1-minute
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index d43ea81..7cec6de 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -828,7 +828,7 @@ static ssize_t adb_write(struct file *file, const char __user *buf,
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
- req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
+ req = kmalloc(sizeof(struct adb_request),
GFP_KERNEL);
if (req == NULL)
return -ENOMEM;
diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c
index 8862a83..4300c62 100644
--- a/drivers/macintosh/apm_emu.c
+++ b/drivers/macintosh/apm_emu.c
@@ -321,7 +321,7 @@ static int do_open(struct inode * inode, struct file * filp)
{
struct apm_user * as;
- as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
+ as = kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
sizeof(*as));
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 6dde27a..6f30459 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -945,7 +945,7 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id)
*/
tlen = sizeof(struct property) + len + 18;
- prop = kcalloc(tlen, 1, GFP_KERNEL);
+ prop = kzalloc(tlen, GFP_KERNEL);
if (prop == NULL)
return NULL;
hdr = (struct smu_sdbp_header *)(prop + 1);
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index d9986f3..93e6ef92 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -847,7 +847,7 @@ pbook_pci_save(void)
n_pbook_pci_saves = npci;
if (npci == 0)
return;
- ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
+ ps = kmalloc(npci * sizeof(*ps), GFP_KERNEL);
pbook_pci_saves = ps;
if (ps == NULL)
return;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index c92c1521..4540ade 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -215,6 +215,7 @@ config DM_CRYPT
tristate "Crypt target support"
depends on BLK_DEV_DM && EXPERIMENTAL
select CRYPTO
+ select CRYPTO_CBC
---help---
This device-mapper target allows you to create a device that
transparently encrypts the data on it. You'll need to activate
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
index a7a5ab5..4ebd0f2 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -173,7 +173,7 @@ static int make_request(request_queue_t *q, struct bio *bio)
conf_t *conf = (conf_t*)mddev->private;
int failit = 0;
- if (bio->bi_rw & 1) {
+ if (bio_data_dir(bio) == WRITE) {
/* write request */
if (atomic_read(&conf->counters[WriteAll])) {
/* special case - don't decrement, don't generic_make_request,
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 53bd46d..21e2a7b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3314,6 +3314,10 @@ static int do_md_stop(mddev_t * mddev, int mode)
module_put(mddev->pers->owner);
mddev->pers = NULL;
+
+ set_capacity(disk, 0);
+ mddev->changed = 1;
+
if (mddev->ro)
mddev->ro = 0;
}
@@ -3333,7 +3337,7 @@ static int do_md_stop(mddev_t * mddev, int mode)
if (mode == 0) {
mdk_rdev_t *rdev;
struct list_head *tmp;
- struct gendisk *disk;
+
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
bitmap_destroy(mddev);
@@ -3358,10 +3362,6 @@ static int do_md_stop(mddev_t * mddev, int mode)
mddev->raid_disks = 0;
mddev->recovery_cp = 0;
- disk = mddev->gendisk;
- if (disk)
- set_capacity(disk, 0);
- mddev->changed = 1;
} else if (mddev->pers)
printk(KERN_INFO "md: %s switched to read-only mode.\n",
mdname(mddev));
@@ -3371,6 +3371,7 @@ out:
return err;
}
+#ifndef MODULE
static void autorun_array(mddev_t *mddev)
{
mdk_rdev_t *rdev;
@@ -3485,6 +3486,7 @@ static void autorun_devices(int part)
}
printk(KERN_INFO "md: ... autorun DONE.\n");
}
+#endif /* !MODULE */
static int get_version(void __user * arg)
{
@@ -3722,6 +3724,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
if (err)
export_rdev(rdev);
+ md_update_sb(mddev, 1);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
return err;
@@ -5273,7 +5276,6 @@ void md_do_sync(mddev_t *mddev)
mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);
if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) &&
- test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
mddev->curr_resync > 2) {
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
@@ -5297,6 +5299,7 @@ void md_do_sync(mddev_t *mddev)
rdev->recovery_offset = mddev->curr_resync;
}
}
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
skip:
mddev->curr_resync = 0;
@@ -5593,7 +5596,7 @@ static void autostart_arrays(int part)
autorun_devices(part);
}
-#endif
+#endif /* !MODULE */
static __exit void md_exit(void)
{
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 656fae9..b30f74b 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1736,7 +1736,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
/* take from bio_init */
bio->bi_next = NULL;
bio->bi_flags |= 1 << BIO_UPTODATE;
- bio->bi_rw = 0;
+ bio->bi_rw = READ;
bio->bi_vcnt = 0;
bio->bi_idx = 0;
bio->bi_phys_segments = 0;
@@ -1951,6 +1951,7 @@ static int run(mddev_t *mddev)
!test_bit(In_sync, &disk->rdev->flags)) {
disk->head_position = 0;
mddev->degraded++;
+ conf->fullsync = 1;
}
}
if (mddev->degraded == conf->raid_disks) {
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 7492d60..f014191 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1785,7 +1785,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
biolist = bio;
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_read;
- bio->bi_rw = 0;
+ bio->bi_rw = READ;
bio->bi_sector = r10_bio->devs[j].addr +
conf->mirrors[d].rdev->data_offset;
bio->bi_bdev = conf->mirrors[d].rdev->bdev;
@@ -1801,7 +1801,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
biolist = bio;
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_write;
- bio->bi_rw = 1;
+ bio->bi_rw = WRITE;
bio->bi_sector = r10_bio->devs[k].addr +
conf->mirrors[i].rdev->data_offset;
bio->bi_bdev = conf->mirrors[i].rdev->bdev;
@@ -1870,7 +1870,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
biolist = bio;
bio->bi_private = r10_bio;
bio->bi_end_io = end_sync_read;
- bio->bi_rw = 0;
+ bio->bi_rw = READ;
bio->bi_sector = r10_bio->devs[i].addr +
conf->mirrors[d].rdev->data_offset;
bio->bi_bdev = conf->mirrors[d].rdev->bdev;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 52914d5..be008f0 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -134,6 +134,8 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
list_add_tail(&sh->lru, &conf->inactive_list);
wake_up(&conf->wait_for_stripe);
+ if (conf->retry_read_aligned)
+ md_wakeup_thread(conf->mddev->thread);
}
}
}
@@ -542,35 +544,7 @@ static int raid5_end_read_request(struct bio * bi, unsigned int bytes_done,
}
if (uptodate) {
-#if 0
- struct bio *bio;
- unsigned long flags;
- spin_lock_irqsave(&conf->device_lock, flags);
- /* we can return a buffer if we bypassed the cache or
- * if the top buffer is not in highmem. If there are
- * multiple buffers, leave the extra work to
- * handle_stripe
- */
- buffer = sh->bh_read[i];
- if (buffer &&
- (!PageHighMem(buffer->b_page)
- || buffer->b_page == bh->b_page )
- ) {
- sh->bh_read[i] = buffer->b_reqnext;
- buffer->b_reqnext = NULL;
- } else
- buffer = NULL;
- spin_unlock_irqrestore(&conf->device_lock, flags);
- if (sh->bh_page[i]==bh->b_page)
- set_buffer_uptodate(bh);
- if (buffer) {
- if (buffer->b_page != bh->b_page)
- memcpy(buffer->b_data, bh->b_data, bh->b_size);
- buffer->b_end_io(buffer, 1);
- }
-#else
set_bit(R5_UPTODATE, &sh->dev[i].flags);
-#endif
if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
rdev = conf->disks[i].rdev;
printk(KERN_INFO "raid5:%s: read error corrected (%lu sectors at %llu on %s)\n",
@@ -616,14 +590,6 @@ static int raid5_end_read_request(struct bio * bi, unsigned int bytes_done,
}
}
rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
-#if 0
- /* must restore b_page before unlocking buffer... */
- if (sh->bh_page[i] != bh->b_page) {
- bh->b_page = sh->bh_page[i];
- bh->b_data = page_address(bh->b_page);
- clear_buffer_uptodate(bh);
- }
-#endif
clear_bit(R5_LOCKED, &sh->dev[i].flags);
set_bit(STRIPE_HANDLE, &sh->state);
release_stripe(sh);
@@ -821,7 +787,8 @@ static sector_t raid5_compute_sector(sector_t r_sector, unsigned int raid_disks,
static sector_t compute_blocknr(struct stripe_head *sh, int i)
{
raid5_conf_t *conf = sh->raid_conf;
- int raid_disks = sh->disks, data_disks = raid_disks - 1;
+ int raid_disks = sh->disks;
+ int data_disks = raid_disks - conf->max_degraded;
sector_t new_sector = sh->sector, check;
int sectors_per_chunk = conf->chunk_size >> 9;
sector_t stripe;
@@ -857,7 +824,6 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i)
}
break;
case 6:
- data_disks = raid_disks - 2;
if (i == raid6_next_disk(sh->pd_idx, raid_disks))
return 0; /* It is the Q disk */
switch (conf->algorithm) {
@@ -1353,8 +1319,10 @@ static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks)
int pd_idx, dd_idx;
int chunk_offset = sector_div(stripe, sectors_per_chunk);
- raid5_compute_sector(stripe*(disks-1)*sectors_per_chunk
- + chunk_offset, disks, disks-1, &dd_idx, &pd_idx, conf);
+ raid5_compute_sector(stripe * (disks - conf->max_degraded)
+ *sectors_per_chunk + chunk_offset,
+ disks, disks - conf->max_degraded,
+ &dd_idx, &pd_idx, conf);
return pd_idx;
}
@@ -1615,15 +1583,6 @@ static void handle_stripe5(struct stripe_head *sh)
} else if (test_bit(R5_Insync, &dev->flags)) {
set_bit(R5_LOCKED, &dev->flags);
set_bit(R5_Wantread, &dev->flags);
-#if 0
- /* if I am just reading this block and we don't have
- a failed drive, or any pending writes then sidestep the cache */
- if (sh->bh_read[i] && !sh->bh_read[i]->b_reqnext &&
- ! syncing && !failed && !to_write) {
- sh->bh_cache[i]->b_page = sh->bh_read[i]->b_page;
- sh->bh_cache[i]->b_data = sh->bh_read[i]->b_data;
- }
-#endif
locked++;
PRINTK("Reading block %d (sync=%d)\n",
i, syncing);
@@ -1641,9 +1600,6 @@ static void handle_stripe5(struct stripe_head *sh)
dev = &sh->dev[i];
if ((dev->towrite || i == sh->pd_idx) &&
(!test_bit(R5_LOCKED, &dev->flags)
-#if 0
-|| sh->bh_page[i]!=bh->b_page
-#endif
) &&
!test_bit(R5_UPTODATE, &dev->flags)) {
if (test_bit(R5_Insync, &dev->flags)
@@ -1655,9 +1611,6 @@ static void handle_stripe5(struct stripe_head *sh)
/* Would I have to read this buffer for reconstruct_write */
if (!test_bit(R5_OVERWRITE, &dev->flags) && i != sh->pd_idx &&
(!test_bit(R5_LOCKED, &dev->flags)
-#if 0
-|| sh->bh_page[i] != bh->b_page
-#endif
) &&
!test_bit(R5_UPTODATE, &dev->flags)) {
if (test_bit(R5_Insync, &dev->flags)) rcw++;
@@ -1865,23 +1818,25 @@ static void handle_stripe5(struct stripe_head *sh)
return_bi = bi->bi_next;
bi->bi_next = NULL;
bi->bi_size = 0;
- bi->bi_end_io(bi, bytes, 0);
+ bi->bi_end_io(bi, bytes,
+ test_bit(BIO_UPTODATE, &bi->bi_flags)
+ ? 0 : -EIO);
}
for (i=disks; i-- ;) {
int rw;
struct bio *bi;
mdk_rdev_t *rdev;
if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags))
- rw = 1;
+ rw = WRITE;
else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags))
- rw = 0;
+ rw = READ;
else
continue;
bi = &sh->dev[i].req;
bi->bi_rw = rw;
- if (rw)
+ if (rw == WRITE)
bi->bi_end_io = raid5_end_write_request;
else
bi->bi_end_io = raid5_end_read_request;
@@ -1917,7 +1872,7 @@ static void handle_stripe5(struct stripe_head *sh)
atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
generic_make_request(bi);
} else {
- if (rw == 1)
+ if (rw == WRITE)
set_bit(STRIPE_DEGRADED, &sh->state);
PRINTK("skip op %ld on disc %d for sector %llu\n",
bi->bi_rw, i, (unsigned long long)sh->sector);
@@ -2193,15 +2148,6 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
} else if (test_bit(R5_Insync, &dev->flags)) {
set_bit(R5_LOCKED, &dev->flags);
set_bit(R5_Wantread, &dev->flags);
-#if 0
- /* if I am just reading this block and we don't have
- a failed drive, or any pending writes then sidestep the cache */
- if (sh->bh_read[i] && !sh->bh_read[i]->b_reqnext &&
- ! syncing && !failed && !to_write) {
- sh->bh_cache[i]->b_page = sh->bh_read[i]->b_page;
- sh->bh_cache[i]->b_data = sh->bh_read[i]->b_data;
- }
-#endif
locked++;
PRINTK("Reading block %d (sync=%d)\n",
i, syncing);
@@ -2220,9 +2166,6 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
if (!test_bit(R5_OVERWRITE, &dev->flags)
&& i != pd_idx && i != qd_idx
&& (!test_bit(R5_LOCKED, &dev->flags)
-#if 0
- || sh->bh_page[i] != bh->b_page
-#endif
) &&
!test_bit(R5_UPTODATE, &dev->flags)) {
if (test_bit(R5_Insync, &dev->flags)) rcw++;
@@ -2418,23 +2361,25 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
return_bi = bi->bi_next;
bi->bi_next = NULL;
bi->bi_size = 0;
- bi->bi_end_io(bi, bytes, 0);
+ bi->bi_end_io(bi, bytes,
+ test_bit(BIO_UPTODATE, &bi->bi_flags)
+ ? 0 : -EIO);
}
for (i=disks; i-- ;) {
int rw;
struct bio *bi;
mdk_rdev_t *rdev;
if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags))
- rw = 1;
+ rw = WRITE;
else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags))
- rw = 0;
+ rw = READ;
else
continue;
bi = &sh->dev[i].req;
bi->bi_rw = rw;
- if (rw)
+ if (rw == WRITE)
bi->bi_end_io = raid5_end_write_request;
else
bi->bi_end_io = raid5_end_read_request;
@@ -2470,7 +2415,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
generic_make_request(bi);
} else {
- if (rw == 1)
+ if (rw == WRITE)
set_bit(STRIPE_DEGRADED, &sh->state);
PRINTK("skip op %ld on disc %d for sector %llu\n",
bi->bi_rw, i, (unsigned long long)sh->sector);
@@ -2611,6 +2556,180 @@ static int raid5_congested(void *data, int bits)
return 0;
}
+/* We want read requests to align with chunks where possible,
+ * but write requests don't need to.
+ */
+static int raid5_mergeable_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *biovec)
+{
+ mddev_t *mddev = q->queuedata;
+ sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+ int max;
+ unsigned int chunk_sectors = mddev->chunk_size >> 9;
+ unsigned int bio_sectors = bio->bi_size >> 9;
+
+ if (bio_data_dir(bio) == WRITE)
+ return biovec->bv_len; /* always allow writes to be mergeable */
+
+ max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9;
+ if (max < 0) max = 0;
+ if (max <= biovec->bv_len && bio_sectors == 0)
+ return biovec->bv_len;
+ else
+ return max;
+}
+
+
+static int in_chunk_boundary(mddev_t *mddev, struct bio *bio)
+{
+ sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+ unsigned int chunk_sectors = mddev->chunk_size >> 9;
+ unsigned int bio_sectors = bio->bi_size >> 9;
+
+ return chunk_sectors >=
+ ((sector & (chunk_sectors - 1)) + bio_sectors);
+}
+
+/*
+ * add bio to the retry LIFO ( in O(1) ... we are in interrupt )
+ * later sampled by raid5d.
+ */
+static void add_bio_to_retry(struct bio *bi,raid5_conf_t *conf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&conf->device_lock, flags);
+
+ bi->bi_next = conf->retry_read_aligned_list;
+ conf->retry_read_aligned_list = bi;
+
+ spin_unlock_irqrestore(&conf->device_lock, flags);
+ md_wakeup_thread(conf->mddev->thread);
+}
+
+
+static struct bio *remove_bio_from_retry(raid5_conf_t *conf)
+{
+ struct bio *bi;
+
+ bi = conf->retry_read_aligned;
+ if (bi) {
+ conf->retry_read_aligned = NULL;
+ return bi;
+ }
+ bi = conf->retry_read_aligned_list;
+ if(bi) {
+ conf->retry_read_aligned = bi->bi_next;
+ bi->bi_next = NULL;
+ bi->bi_phys_segments = 1; /* biased count of active stripes */
+ bi->bi_hw_segments = 0; /* count of processed stripes */
+ }
+
+ return bi;
+}
+
+
+/*
+ * The "raid5_align_endio" should check if the read succeeded and if it
+ * did, call bio_endio on the original bio (having bio_put the new bio
+ * first).
+ * If the read failed..
+ */
+static int raid5_align_endio(struct bio *bi, unsigned int bytes, int error)
+{
+ struct bio* raid_bi = bi->bi_private;
+ mddev_t *mddev;
+ raid5_conf_t *conf;
+ int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
+ mdk_rdev_t *rdev;
+
+ if (bi->bi_size)
+ return 1;
+ bio_put(bi);
+
+ mddev = raid_bi->bi_bdev->bd_disk->queue->queuedata;
+ conf = mddev_to_conf(mddev);
+ rdev = (void*)raid_bi->bi_next;
+ raid_bi->bi_next = NULL;
+
+ rdev_dec_pending(rdev, conf->mddev);
+
+ if (!error && uptodate) {
+ bio_endio(raid_bi, bytes, 0);
+ if (atomic_dec_and_test(&conf->active_aligned_reads))
+ wake_up(&conf->wait_for_stripe);
+ return 0;
+ }
+
+
+ PRINTK("raid5_align_endio : io error...handing IO for a retry\n");
+
+ add_bio_to_retry(raid_bi, conf);
+ return 0;
+}
+
+static int chunk_aligned_read(request_queue_t *q, struct bio * raid_bio)
+{
+ mddev_t *mddev = q->queuedata;
+ raid5_conf_t *conf = mddev_to_conf(mddev);
+ const unsigned int raid_disks = conf->raid_disks;
+ const unsigned int data_disks = raid_disks - conf->max_degraded;
+ unsigned int dd_idx, pd_idx;
+ struct bio* align_bi;
+ mdk_rdev_t *rdev;
+
+ if (!in_chunk_boundary(mddev, raid_bio)) {
+ printk("chunk_aligned_read : non aligned\n");
+ return 0;
+ }
+ /*
+ * use bio_clone to make a copy of the bio
+ */
+ align_bi = bio_clone(raid_bio, GFP_NOIO);
+ if (!align_bi)
+ return 0;
+ /*
+ * set bi_end_io to a new function, and set bi_private to the
+ * original bio.
+ */
+ align_bi->bi_end_io = raid5_align_endio;
+ align_bi->bi_private = raid_bio;
+ /*
+ * compute position
+ */
+ align_bi->bi_sector = raid5_compute_sector(raid_bio->bi_sector,
+ raid_disks,
+ data_disks,
+ &dd_idx,
+ &pd_idx,
+ conf);
+
+ rcu_read_lock();
+ rdev = rcu_dereference(conf->disks[dd_idx].rdev);
+ if (rdev && test_bit(In_sync, &rdev->flags)) {
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ raid_bio->bi_next = (void*)rdev;
+ align_bi->bi_bdev = rdev->bdev;
+ align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
+ align_bi->bi_sector += rdev->data_offset;
+
+ spin_lock_irq(&conf->device_lock);
+ wait_event_lock_irq(conf->wait_for_stripe,
+ conf->quiesce == 0,
+ conf->device_lock, /* nothing */);
+ atomic_inc(&conf->active_aligned_reads);
+ spin_unlock_irq(&conf->device_lock);
+
+ generic_make_request(align_bi);
+ return 1;
+ } else {
+ rcu_read_unlock();
+ bio_put(align_bi);
+ return 0;
+ }
+}
+
+
static int make_request(request_queue_t *q, struct bio * bi)
{
mddev_t *mddev = q->queuedata;
@@ -2632,6 +2751,11 @@ static int make_request(request_queue_t *q, struct bio * bi)
disk_stat_inc(mddev->gendisk, ios[rw]);
disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bi));
+ if (rw == READ &&
+ mddev->reshape_position == MaxSector &&
+ chunk_aligned_read(q,bi))
+ return 0;
+
logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
last_sector = bi->bi_sector + (bi->bi_size>>9);
bi->bi_next = NULL;
@@ -2739,7 +2863,9 @@ static int make_request(request_queue_t *q, struct bio * bi)
if ( rw == WRITE )
md_write_end(mddev);
bi->bi_size = 0;
- bi->bi_end_io(bi, bytes, 0);
+ bi->bi_end_io(bi, bytes,
+ test_bit(BIO_UPTODATE, &bi->bi_flags)
+ ? 0 : -EIO);
}
return 0;
}
@@ -2950,6 +3076,74 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
return STRIPE_SECTORS;
}
+static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio)
+{
+ /* We may not be able to submit a whole bio at once as there
+ * may not be enough stripe_heads available.
+ * We cannot pre-allocate enough stripe_heads as we may need
+ * more than exist in the cache (if we allow ever large chunks).
+ * So we do one stripe head at a time and record in
+ * ->bi_hw_segments how many have been done.
+ *
+ * We *know* that this entire raid_bio is in one chunk, so
+ * it will be only one 'dd_idx' and only need one call to raid5_compute_sector.
+ */
+ struct stripe_head *sh;
+ int dd_idx, pd_idx;
+ sector_t sector, logical_sector, last_sector;
+ int scnt = 0;
+ int remaining;
+ int handled = 0;
+
+ logical_sector = raid_bio->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
+ sector = raid5_compute_sector( logical_sector,
+ conf->raid_disks,
+ conf->raid_disks - conf->max_degraded,
+ &dd_idx,
+ &pd_idx,
+ conf);
+ last_sector = raid_bio->bi_sector + (raid_bio->bi_size>>9);
+
+ for (; logical_sector < last_sector;
+ logical_sector += STRIPE_SECTORS, scnt++) {
+
+ if (scnt < raid_bio->bi_hw_segments)
+ /* already done this stripe */
+ continue;
+
+ sh = get_active_stripe(conf, sector, conf->raid_disks, pd_idx, 1);
+
+ if (!sh) {
+ /* failed to get a stripe - must wait */
+ raid_bio->bi_hw_segments = scnt;
+ conf->retry_read_aligned = raid_bio;
+ return handled;
+ }
+
+ set_bit(R5_ReadError, &sh->dev[dd_idx].flags);
+ add_stripe_bio(sh, raid_bio, dd_idx, 0);
+ handle_stripe(sh, NULL);
+ release_stripe(sh);
+ handled++;
+ }
+ spin_lock_irq(&conf->device_lock);
+ remaining = --raid_bio->bi_phys_segments;
+ spin_unlock_irq(&conf->device_lock);
+ if (remaining == 0) {
+ int bytes = raid_bio->bi_size;
+
+ raid_bio->bi_size = 0;
+ raid_bio->bi_end_io(raid_bio, bytes,
+ test_bit(BIO_UPTODATE, &raid_bio->bi_flags)
+ ? 0 : -EIO);
+ }
+ if (atomic_dec_and_test(&conf->active_aligned_reads))
+ wake_up(&conf->wait_for_stripe);
+ return handled;
+}
+
+
+
/*
* This is our raid5 kernel thread.
*
@@ -2971,6 +3165,7 @@ static void raid5d (mddev_t *mddev)
spin_lock_irq(&conf->device_lock);
while (1) {
struct list_head *first;
+ struct bio *bio;
if (conf->seq_flush != conf->seq_write) {
int seq = conf->seq_flush;
@@ -2987,6 +3182,16 @@ static void raid5d (mddev_t *mddev)
!list_empty(&conf->delayed_list))
raid5_activate_delayed(conf);
+ while ((bio = remove_bio_from_retry(conf))) {
+ int ok;
+ spin_unlock_irq(&conf->device_lock);
+ ok = retry_aligned_read(conf, bio);
+ spin_lock_irq(&conf->device_lock);
+ if (!ok)
+ break;
+ handled++;
+ }
+
if (list_empty(&conf->handle_list))
break;
@@ -3174,6 +3379,7 @@ static int run(mddev_t *mddev)
INIT_LIST_HEAD(&conf->inactive_list);
atomic_set(&conf->active_stripes, 0);
atomic_set(&conf->preread_active_stripes, 0);
+ atomic_set(&conf->active_aligned_reads, 0);
PRINTK("raid5: run(%s) called.\n", mdname(mddev));
@@ -3320,6 +3526,8 @@ static int run(mddev_t *mddev)
mddev->array_size = mddev->size * (conf->previous_raid_disks -
conf->max_degraded);
+ blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
+
return 0;
abort:
if (conf) {
@@ -3694,7 +3902,8 @@ static void raid5_quiesce(mddev_t *mddev, int state)
spin_lock_irq(&conf->device_lock);
conf->quiesce = 1;
wait_event_lock_irq(conf->wait_for_stripe,
- atomic_read(&conf->active_stripes) == 0,
+ atomic_read(&conf->active_stripes) == 0 &&
+ atomic_read(&conf->active_aligned_reads) == 0,
conf->device_lock, /* nothing */);
spin_unlock_irq(&conf->device_lock);
break;
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 9f7e1fe..87410db 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -67,6 +67,7 @@ source "drivers/media/common/Kconfig"
config VIDEO_TUNER
tristate
+ depends on I2C
config VIDEO_BUF
tristate
@@ -82,6 +83,7 @@ config VIDEO_IR
config VIDEO_TVEEPROM
tristate
+ depends on I2C
config USB_DABUSB
tristate "DABUSB driver"
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index db75344..f51e02fe 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -1552,3 +1552,58 @@ IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_norwood);
+
+/* From reading the following remotes:
+ * Zenith Universal 7 / TV Mode 807 / VCR Mode 837
+ * Hauppauge (from NOVA-CI-s box product)
+ * This is a "middle of the road" approach, differences are noted
+ */
+IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = {
+ [ 0x00 ] = KEY_0,
+ [ 0x01 ] = KEY_1,
+ [ 0x02 ] = KEY_2,
+ [ 0x03 ] = KEY_3,
+ [ 0x04 ] = KEY_4,
+ [ 0x05 ] = KEY_5,
+ [ 0x06 ] = KEY_6,
+ [ 0x07 ] = KEY_7,
+ [ 0x08 ] = KEY_8,
+ [ 0x09 ] = KEY_9,
+ [ 0x0a ] = KEY_ENTER,
+ [ 0x0b ] = KEY_RED,
+ [ 0x0c ] = KEY_POWER, /* RADIO on Hauppauge */
+ [ 0x0d ] = KEY_MUTE,
+ [ 0x0f ] = KEY_A, /* TV on Hauppauge */
+ [ 0x10 ] = KEY_VOLUMEUP,
+ [ 0x11 ] = KEY_VOLUMEDOWN,
+ [ 0x14 ] = KEY_B,
+ [ 0x1c ] = KEY_UP,
+ [ 0x1d ] = KEY_DOWN,
+ [ 0x1e ] = KEY_OPTION, /* RESERVED on Hauppauge */
+ [ 0x1f ] = KEY_BREAK,
+ [ 0x20 ] = KEY_CHANNELUP,
+ [ 0x21 ] = KEY_CHANNELDOWN,
+ [ 0x22 ] = KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+ [ 0x24 ] = KEY_RESTART,
+ [ 0x25 ] = KEY_OK,
+ [ 0x26 ] = KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
+ [ 0x28 ] = KEY_ENTER, /* VCR mode on Zenith */
+ [ 0x29 ] = KEY_PAUSE,
+ [ 0x2b ] = KEY_RIGHT,
+ [ 0x2c ] = KEY_LEFT,
+ [ 0x2e ] = KEY_MENU, /* FULL SCREEN on Hauppauge */
+ [ 0x30 ] = KEY_SLOW,
+ [ 0x31 ] = KEY_PREVIOUS, /* VCR mode on Zenith */
+ [ 0x32 ] = KEY_REWIND,
+ [ 0x34 ] = KEY_FASTFORWARD,
+ [ 0x35 ] = KEY_PLAY,
+ [ 0x36 ] = KEY_STOP,
+ [ 0x37 ] = KEY_RECORD,
+ [ 0x38 ] = KEY_TUNER, /* TV/VCR on Zenith */
+ [ 0x3a ] = KEY_C,
+ [ 0x3c ] = KEY_EXIT,
+ [ 0x3d ] = KEY_POWER2,
+ [ 0x3e ] = KEY_TUNER,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old);
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index 5297a36..8c85efc 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -189,13 +189,21 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
saa7146_write(dev, I2C_TRANSFER, *dword);
dev->i2c_op = 1;
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
- wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
- if (signal_pending (current)) {
- /* a signal arrived */
- return -ERESTARTSYS;
+ timeout = HZ/100 + 1; /* 10ms */
+ timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
+ if (timeout == -ERESTARTSYS || dev->i2c_op) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+ if (timeout == -ERESTARTSYS)
+ /* a signal arrived */
+ return -ERESTARTSYS;
+
+ printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for end of xfer\n");
+ return -EIO;
}
status = saa7146_read(dev, I2C_STATUS);
} else {
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index a0dcd59..7987595 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,6 +9,7 @@ config DVB_B2C2_FLEXCOP
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_BCM3510 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
help
Support for the digital TV receiver chip made by B2C2 Inc. included in
Technisats PCI cards and USB boxes.
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index b8ba878..c2b35e3 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -14,7 +14,7 @@
#include "stv0297.h"
#include "mt312.h"
#include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
#include "dvb-pll.h"
/* lnb control */
@@ -303,12 +303,6 @@ static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct fir
return request_firmware(fw, name, fc->dev);
}
-static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
-{
- struct flexcop_device *fc = fe->dvb->priv;
- return lg_h06xf_pll_set(fe, &fc->i2c_adap, params);
-}
-
static struct lgdt330x_config air2pc_atsc_hd5000_config = {
.demod_address = 0x59,
.demod_chip = LGDT3303,
@@ -533,7 +527,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
/* try the air atsc 3nd generation (lgdt3303) */
if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC3;
- fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+ dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap);
info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
} else
/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index ae2ff5d..dd66b60 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -1,13 +1,13 @@
config DVB_BT8XX
tristate "BT8xx based PCI cards"
depends on DVB_CORE && PCI && I2C && VIDEO_BT848
- select DVB_PLL
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_SP887X if !DVB_FE_CUSTOMISE
select DVB_NXT6000 if !DVB_FE_CUSTOMISE
select DVB_CX24110 if !DVB_FE_CUSTOMISE
select DVB_OR51211 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
index 240ad08..50bc32a 100644
--- a/drivers/media/dvb/bt8xx/dst_ca.c
+++ b/drivers/media/dvb/bt8xx/dst_ca.c
@@ -480,7 +480,7 @@ static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message,
struct ca_msg *hw_buffer;
int result = 0;
- if ((hw_buffer = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+ if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
return -ENOMEM;
}
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 14e69a7..3e35931 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -34,7 +34,6 @@
#include "dvb_frontend.h"
#include "dvb-bt8xx.h"
#include "bt878.h"
-#include "dvb-pll.h"
static int debug;
@@ -568,12 +567,6 @@ static struct mt352_config digitv_alps_tded4_config = {
.demod_init = digitv_alps_tded4_demod_init,
};
-static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
-{
- struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
- return lg_h06xf_pll_set(fe, card->i2c_adapter, params);
-}
-
static struct lgdt330x_config tdvs_tua6034_config = {
.demod_address = 0x0e,
.demod_chip = LGDT3303,
@@ -616,7 +609,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
lgdt330x_reset(card);
card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
if (card->fe != NULL) {
- card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params;
+ dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter);
dprintk ("dvb_bt8xx: lgdt330x detected\n");
}
break;
@@ -664,7 +657,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
case BTTV_BOARD_TWINHAN_DST:
/* DST is not a frontend driver !!! */
- state = (struct dst_state *) kmalloc(sizeof (struct dst_state), GFP_KERNEL);
+ state = kmalloc(sizeof (struct dst_state), GFP_KERNEL);
if (!state) {
printk("dvb_bt8xx: No memory\n");
break;
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
index 4745a90..e75f417 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -37,7 +37,7 @@
#include "cx24110.h"
#include "or51211.h"
#include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
#include "zl10353.h"
struct dvb_bt8xx_card {
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 9123147..d64b96c 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -746,6 +746,7 @@ static void cinergyt2_query_rc (struct work_struct *work)
dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
input_report_key(cinergyt2->rc_input_dev,
cinergyt2->rc_input_event, 0);
+ input_sync(cinergyt2->rc_input_dev);
cinergyt2->rc_input_event = KEY_MAX;
}
cinergyt2->rc_last_code = ~0;
@@ -783,6 +784,7 @@ static void cinergyt2_query_rc (struct work_struct *work)
dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
input_report_key(cinergyt2->rc_input_dev,
cinergyt2->rc_input_event, 1);
+ input_sync(cinergyt2->rc_input_dev);
cinergyt2->rc_last_code = rc_events[n].value;
}
}
@@ -798,8 +800,9 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
{
struct input_dev *input_dev;
int i;
+ int err;
- cinergyt2->rc_input_dev = input_dev = input_allocate_device();
+ input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
@@ -817,7 +820,13 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
input_dev->keycodesize = 0;
input_dev->keycodemax = 0;
- input_register_device(cinergyt2->rc_input_dev);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ cinergyt2->rc_input_dev = input_dev;
schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
return 0;
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index a263b3f..ad52143 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -69,6 +69,8 @@ config DVB_USB_DIBUSB_MC
config DVB_USB_DIB0700
tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
depends on DVB_USB
+ select DVB_DIB7000P
+ select DVB_DIB7000M
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
@@ -96,6 +98,7 @@ config DVB_USB_CXUSB
depends on DVB_USB
select DVB_CX22702 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
help
@@ -157,6 +160,17 @@ config DVB_USB_NOVA_T_USB2
help
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
+config DVB_USB_TTUSB2
+ tristate "Pinnacle 400e DVB-S USB2.0 support"
+ depends on DVB_USB
+ select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
+ firmware protocol used by this module is similar to the one used by the
+ old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko.
+
config DVB_USB_DTT200U
tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
depends on DVB_USB
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index e239107..154d593 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -36,6 +36,9 @@ obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o
dvb-usb-cxusb-objs = cxusb.o
obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o
+dvb-usb-ttusb2-objs = ttusb2.o
+obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
+
dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index 2ed3eb6..a6c5f19 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -116,24 +116,24 @@ static struct dvb_usb_device_properties a800_properties = {
{
.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,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
-
- .frontend_attach = dibusb_dib3000mc_frontend_attach,
- .tuner_attach = dibusb_dib3000mc_tuner_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 7,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+
+ .frontend_attach = dibusb_dib3000mc_frontend_attach,
+ .tuner_attach = dibusb_dib3000mc_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 7,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
},
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 43f3906..15d12fc 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -14,12 +14,12 @@
* TODO: Use the cx25840-driver for the analogue part
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
- * Copyright (C) 2005 Michael Krufky (mkrufky@m1k.net)
+ * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
* Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
*
- * 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 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
*/
@@ -27,29 +27,29 @@
#include "cx22702.h"
#include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
#include "mt352.h"
#include "mt352_priv.h"
#include "zl10353.h"
/* debug */
int dvb_usb_cxusb_debug;
-module_param_named(debug,dvb_usb_cxusb_debug, int, 0644);
+module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static int cxusb_ctrl_msg(struct dvb_usb_device *d,
- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 sndbuf[1+wlen];
- memset(sndbuf,0,1+wlen);
+ memset(sndbuf, 0, 1+wlen);
sndbuf[0] = cmd;
- memcpy(&sndbuf[1],wbuf,wlen);
+ memcpy(&sndbuf[1], wbuf, wlen);
if (wo)
- dvb_usb_generic_write(d,sndbuf,1+wlen);
+ dvb_usb_generic_write(d, sndbuf, 1+wlen);
else
- dvb_usb_generic_rw(d,sndbuf,1+wlen,rbuf,rlen,0);
+ dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
return 0;
}
@@ -58,14 +58,14 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d,
static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
{
struct cxusb_state *st = d->priv;
- u8 o[2],i;
+ u8 o[2], i;
if (st->gpio_write_state[GPIO_TUNER] == onoff)
return;
o[0] = GPIO_TUNER;
o[1] = onoff;
- cxusb_ctrl_msg(d,CMD_GPIO_WRITE,o,2,&i,1);
+ cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
if (i != 0x01)
deb_info("gpio_write failed.\n");
@@ -74,7 +74,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
}
/* I2C */
-static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
@@ -89,12 +90,12 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
if (d->udev->descriptor.idVendor == USB_VID_MEDION)
switch (msg[i].addr) {
- case 0x63:
- cxusb_gpio_tuner(d,0);
- break;
- default:
- cxusb_gpio_tuner(d,1);
- break;
+ case 0x63:
+ cxusb_gpio_tuner(d, 0);
+ break;
+ default:
+ cxusb_gpio_tuner(d, 1);
+ break;
}
/* read request */
@@ -103,26 +104,27 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
obuf[0] = msg[i].len;
obuf[1] = msg[i+1].len;
obuf[2] = msg[i].addr;
- memcpy(&obuf[3],msg[i].buf,msg[i].len);
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
if (cxusb_ctrl_msg(d, CMD_I2C_READ,
- obuf, 3+msg[i].len,
- ibuf, 1+msg[i+1].len) < 0)
+ obuf, 3+msg[i].len,
+ ibuf, 1+msg[i+1].len) < 0)
break;
if (ibuf[0] != 0x08)
deb_i2c("i2c read may have failed\n");
- memcpy(msg[i+1].buf,&ibuf[1],msg[i+1].len);
+ memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
i++;
} else { /* write */
u8 obuf[2+msg[i].len], ibuf;
obuf[0] = msg[i].addr;
obuf[1] = msg[i].len;
- memcpy(&obuf[2],msg[i].buf,msg[i].len);
+ memcpy(&obuf[2], msg[i].buf, msg[i].len);
- if (cxusb_ctrl_msg(d,CMD_I2C_WRITE, obuf, 2+msg[i].len, &ibuf,1) < 0)
+ if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf,
+ 2+msg[i].len, &ibuf,1) < 0)
break;
if (ibuf != 0x08)
deb_i2c("i2c write may have failed\n");
@@ -324,16 +326,8 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe)
return 0;
}
-static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *fep)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- return lg_h06xf_pll_set(fe, &adap->dev->i2c_adap, fep);
-}
-
static struct cx22702_config cxusb_cx22702_config = {
.demod_address = 0x63,
-
.output_mode = CX22702_PARALLEL_OUTPUT,
};
@@ -374,31 +368,27 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
{
- adap->pll_addr = 0x61;
- adap->pll_desc = &dvb_pll_thomson_dtt7579;
- adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+ dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+ NULL, &dvb_pll_thomson_dtt7579);
return 0;
}
static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
{
- adap->pll_addr = 0x61;
- adap->pll_desc = &dvb_pll_lg_z201;
- adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+ dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201);
return 0;
}
static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
{
- adap->pll_addr = 0x60;
- adap->pll_desc = &dvb_pll_thomson_dtt7579;
- adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+ dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+ NULL, &dvb_pll_thomson_dtt7579);
return 0;
}
-static int cxusb_lgdt3303_tuner_attach(struct dvb_usb_adapter *adap)
+static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
{
- adap->fe->ops.tuner_ops.set_params = cxusb_lgh064f_tuner_set_params;
+ dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap);
return 0;
}
@@ -410,7 +400,8 @@ 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)
+ if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
+ &adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -423,7 +414,8 @@ 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)
+ if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
+ &adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -437,7 +429,8 @@ 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)
+ if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
+ &adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -450,8 +443,11 @@ 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))
+ 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))
return 0;
return -EIO;
@@ -463,7 +459,8 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
*/
#define BLUEBIRD_01_ID_OFFSET 6638
-static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const struct firmware *fw)
+static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
+ const struct firmware *fw)
{
if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
return -EINVAL;
@@ -471,10 +468,12 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const
if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
- fw->data[BLUEBIRD_01_ID_OFFSET + 2] = udev->descriptor.idProduct + 1;
- fw->data[BLUEBIRD_01_ID_OFFSET + 3] = udev->descriptor.idProduct >> 8;
+ fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
+ udev->descriptor.idProduct + 1;
+ fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
+ udev->descriptor.idProduct >> 8;
- return usb_cypress_load_firmware(udev,fw,CYPRESS_FX2);
+ return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
}
return -EINVAL;
@@ -488,7 +487,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
static int cxusb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+ const struct usb_device_id *id)
{
if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
@@ -502,20 +501,20 @@ static int cxusb_probe(struct usb_interface *intf,
}
static struct usb_device_id cxusb_table [] = {
- { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
- {} /* Terminating entry */
+ { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
+ {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -529,20 +528,20 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_cx22702_frontend_attach,
- .tuner_attach = cxusb_fmd1216me_tuner_attach,
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_cx22702_frontend_attach,
+ .tuner_attach = cxusb_fmd1216me_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
},
},
@@ -575,21 +574,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_lgdt3303_frontend_attach,
- .tuner_attach = cxusb_lgdt3303_tuner_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_lgdt3303_frontend_attach,
+ .tuner_attach = cxusb_lgh064f_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
},
},
@@ -627,20 +626,20 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_dee1601_frontend_attach,
- .tuner_attach = cxusb_dee1601_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_dee1601_frontend_attach,
+ .tuner_attach = cxusb_dee1601_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 5,
- .endpoint = 0x04,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .count = 5,
+ .endpoint = 0x04,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
},
},
@@ -686,21 +685,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
.num_adapters = 2,
.adapter = {
{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_mt352_frontend_attach,
- .tuner_attach = cxusb_lgz201_tuner_attach,
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_mt352_frontend_attach,
+ .tuner_attach = cxusb_lgz201_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 5,
- .endpoint = 0x04,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .count = 5,
+ .endpoint = 0x04,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -736,21 +735,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_mt352_frontend_attach,
- .tuner_attach = cxusb_dtt7579_tuner_attach,
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_mt352_frontend_attach,
+ .tuner_attach = cxusb_dtt7579_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 5,
- .endpoint = 0x04,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .count = 5,
+ .endpoint = 0x04,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
@@ -776,7 +775,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
- .disconnect = dvb_usb_device_exit,
+ .disconnect = dvb_usb_device_exit,
.id_table = cxusb_table,
};
@@ -802,7 +801,7 @@ module_init (cxusb_module_init);
module_exit (cxusb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
-MODULE_AUTHOR("Michael Krufky <mkrufky@m1k.net>");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
MODULE_VERSION("1.0-alpha");
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index ac84347..cda3ade 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -24,18 +24,23 @@ extern int dvb_usb_dib0700_debug;
#define REQUEST_I2C_WRITE 0x3
#define REQUEST_POLL_RC 0x4
#define REQUEST_JUMPRAM 0x8
+#define REQUEST_SET_CLOCK 0xB
#define REQUEST_SET_GPIO 0xC
#define REQUEST_ENABLE_VIDEO 0xF
// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
+#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
u8 channel_state;
u16 mt2060_if1[2];
+
+ u8 is_dib7000pc;
};
extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
+extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3);
extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw);
extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
extern struct i2c_algorithm dib0700_i2c_algo;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index dca6c69..6a4d150 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -135,14 +135,46 @@ struct i2c_algorithm dib0700_i2c_algo = {
int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold)
{
- u8 buf[3] = { REQUEST_SET_GPIO, 4, (GPIO_IN << 7) | (0 << 6) }; // GPIO4 is save - used for I2C
- *cold = usb_control_msg(udev, usb_sndctrlpipe(udev,0),
- buf[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, buf, 3, USB_CTRL_GET_TIMEOUT) != 3;
+ u8 b[16];
+ s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0),
+ REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
+
+ deb_info("FW GET_VERSION length: %d\n",ret);
+
+ *cold = ret <= 0;
deb_info("cold: %d\n", *cold);
return 0;
}
+static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
+ u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
+ u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
+{
+ u8 b[10];
+ b[0] = REQUEST_SET_CLOCK;
+ b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4);
+ b[2] = (pll_prediv >> 8) & 0xff; // MSB
+ b[3] = pll_prediv & 0xff; // LSB
+ b[4] = (pll_loopdiv >> 8) & 0xff; // MSB
+ b[5] = pll_loopdiv & 0xff; // LSB
+ b[6] = (free_div >> 8) & 0xff; // MSB
+ b[7] = free_div & 0xff; // LSB
+ b[8] = (dsuScaler >> 8) & 0xff; // MSB
+ b[9] = dsuScaler & 0xff; // LSB
+
+ return dib0700_ctrl_wr(d, b, 10);
+}
+
+int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
+{
+ switch (clk_MHz) {
+ case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
+ default: return -EINVAL;
+ }
+ return 0;
+}
+
static int dib0700_jumpram(struct usb_device *udev, u32 address)
{
int ret, actlen;
@@ -197,7 +229,7 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
/* start the firmware */
if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
info("firmware started successfully.");
- msleep(100);
+ msleep(500);
}
} else
ret = -EIO;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index e473bfe..2208757 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -9,6 +9,8 @@
#include "dib0700.h"
#include "dib3000mc.h"
+#include "dib7000m.h"
+#include "dib7000p.h"
#include "mt2060.h"
static int force_lna_activation;
@@ -95,37 +97,189 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
}
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
-/*
-static struct mt2060_config stk7000p_mt2060_config = {
- 0x60
+static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = {
+ BAND_UHF | BAND_VHF, // band_caps
+
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
+
+ 712, // inv_gain
+ 41, // time_stabiliz
+
+ 0, // alpha_level
+ 118, // thlock
+
+ 0, // wbd_inv
+ 4095, // wbd_ref
+ 0, // wbd_sel
+ 0, // wbd_alpha
+
+ 42598, // agc1_max
+ 17694, // agc1_min
+ 45875, // agc2_max
+ 2621, // agc2_min
+ 0, // agc1_pt1
+ 76, // agc1_pt2
+ 139, // agc1_pt3
+ 52, // agc1_slope1
+ 59, // agc1_slope2
+ 107, // agc2_pt1
+ 172, // agc2_pt2
+ 57, // agc2_slope1
+ 70, // agc2_slope2
+
+ 21, // alpha_mant
+ 25, // alpha_exp
+ 28, // beta_mant
+ 48, // beta_exp
+
+ 1, // perform_agc_softsplit
+ { 0, // split_min
+ 107, // split_max
+ 51800, // global_split_min
+ 24700 // global_split_max
+ },
+};
+
+static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = {
+ BAND_UHF | BAND_VHF,
+
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
+
+ 712, // inv_gain
+ 41, // time_stabiliz
+
+ 0, // alpha_level
+ 118, // thlock
+
+ 0, // wbd_inv
+ 4095, // wbd_ref
+ 0, // wbd_sel
+ 0, // wbd_alpha
+
+ 42598, // agc1_max
+ 16384, // agc1_min
+ 42598, // agc2_max
+ 0, // agc2_min
+
+ 0, // agc1_pt1
+ 137, // agc1_pt2
+ 255, // agc1_pt3
+
+ 0, // agc1_slope1
+ 255, // agc1_slope2
+
+ 0, // agc2_pt1
+ 0, // agc2_pt2
+
+ 0, // agc2_slope1
+ 41, // agc2_slope2
+
+ 15, // alpha_mant
+ 25, // alpha_exp
+
+ 28, // beta_mant
+ 48, // beta_exp
+
+ 0, // perform_agc_softsplit
+};
+
+static struct dibx000_bandwidth_config stk7700p_pll_config = {
+ 60000, 30000, // internal, sampling
+ 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
+ 0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
+ (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
+ 60258167, // ifreq
+ 20452225, // timf
+};
+
+static struct dib7000m_config stk7700p_dib7000m_config = {
+ .dvbt_mode = 1,
+ .output_mpeg2_in_188_bytes = 1,
+ .quartz_direct = 1,
+
+ .agc_config_count = 1,
+ .agc = &stk7700p_7000m_mt2060_agc_config,
+ .bw = &stk7700p_pll_config,
+
+ .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
+};
+
+static struct dib7000p_config stk7700p_dib7000p_config = {
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc = &stk7700p_7000p_mt2060_agc_config,
+ .bw = &stk7700p_pll_config,
+
+ .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
};
-*/
static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap)
{
+ struct dib0700_state *st = adap->dev->priv;
/* unless there is no real power management in DVB - we leave the device on GPIO6 */
- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10);
- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10);
- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10);
+
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50);
+
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10);
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100);
+
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ 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);
+ st->is_dib7000pc = 1;
+ } else
+ adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
-// adap->fe = dib7000m_attach(&adap->dev->i2c_adap, &stk7700p_dib7000m_config, 18);
- return 0;
+ return adap->fe == NULL ? -ENODEV : 0;
}
+static struct mt2060_config stk7700p_mt2060_config = {
+ 0x60
+};
+
static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
{
-// tun_i2c = dib7000m_get_tuner_i2c_master(adap->fe, 1);
-// return mt2060_attach(adap->fe, tun_i2c, &stk3000p_mt2060_config, if1);
- return 0;
+ struct dib0700_state *st = adap->dev->priv;
+ struct i2c_adapter *tun_i2c;
+
+ if (st->is_dib7000pc)
+ tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ else
+ tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+
+ return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
+ st->mt2060_if1[0]) == NULL ? -ENODEV : 0;
}
struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) },
+
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) },
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) },
+ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) },
+ { USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) },
+ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) },
+ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) },
+ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -167,20 +321,32 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 3,
+ .num_device_descs = 6,
.devices = {
{ "DiBcom STK7700P reference design",
- { &dib0700_usb_id_table[0], NULL },
+ { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
{ NULL },
},
{ "Hauppauge Nova-T Stick",
- { &dib0700_usb_id_table[3], NULL },
+ { &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL },
{ NULL },
},
{ "AVerMedia AVerTV DVB-T Volar",
- { &dib0700_usb_id_table[4], NULL },
+ { &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] },
{ NULL },
},
+ { "Compro Videomate U500",
+ { &dib0700_usb_id_table[6], NULL },
+ { NULL },
+ },
+ { "Uniwill STK7700P based (Hama and others)",
+ { &dib0700_usb_id_table[7], NULL },
+ { NULL },
+ },
+ { "Leadtek Winfast DTV Dongle (STK7700P based)",
+ { &dib0700_usb_id_table[8], NULL },
+ { NULL },
+ }
}
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -202,7 +368,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_device_descs = 1,
.devices = {
{ "Hauppauge Nova-T 500 Dual DVB-T",
- { &dib0700_usb_id_table[1], &dib0700_usb_id_table[2], NULL },
+ { &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL },
{ NULL },
},
}
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index 4fe363e..7a6ae8f 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -163,23 +163,23 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
- .streaming_ctrl = dibusb_streaming_ctrl,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mb_frontend_attach,
- .tuner_attach = dibusb_tuner_probe_and_attach,
+ .streaming_ctrl = dibusb_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mb_frontend_attach,
+ .tuner_attach = dibusb_tuner_probe_and_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -248,23 +248,23 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
.pid_filter_count = 16,
- .streaming_ctrl = dibusb_streaming_ctrl,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mb_frontend_attach,
- .tuner_attach = dibusb_tuner_probe_and_attach,
+ .streaming_ctrl = dibusb_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mb_frontend_attach,
+ .tuner_attach = dibusb_tuner_probe_and_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
},
},
@@ -312,22 +312,23 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
- .streaming_ctrl = dibusb2_0_streaming_ctrl,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mb_frontend_attach,
- .tuner_attach = dibusb_thomson_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mb_frontend_attach,
+ .tuner_attach = dibusb_thomson_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
}
},
@@ -369,22 +370,22 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
- .streaming_ctrl = dibusb2_0_streaming_ctrl,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mb_frontend_attach,
- .tuner_attach = dibusb_tuner_probe_and_attach,
- /* parameter for the MPEG2-data transfer */
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mb_frontend_attach,
+ .tuner_attach = dibusb_tuner_probe_and_attach,
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.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 a0fd37e..e7ea3e7 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -54,23 +54,23 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
{
.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,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mc_frontend_attach,
- .tuner_attach = dibusb_dib3000mc_tuner_attach,
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mc_frontend_attach,
+ .tuner_attach = dibusb_dib3000mc_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.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 8fb3437..4a198d4 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -274,20 +274,20 @@ static struct dvb_usb_device_properties digitv_properties = {
.num_adapters = 1,
.adapter = {
{
- .frontend_attach = digitv_frontend_attach,
- .tuner_attach = digitv_tuner_attach,
+ .frontend_attach = digitv_frontend_attach,
+ .tuner_attach = digitv_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
}
},
.identify_state = digitv_identify_state,
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index fa43a41..7dbe143 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -268,20 +268,20 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
- .streaming_ctrl = dtt200u_streaming_ctrl,
- .pid_filter = dtt200u_pid_filter,
- .frontend_attach = dtt200u_frontend_attach,
- /* parameter for the MPEG2-data transfer */
+ .streaming_ctrl = dtt200u_streaming_ctrl,
+ .pid_filter = dtt200u_pid_filter,
+ .frontend_attach = dtt200u_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
}
},
.power_ctrl = dtt200u_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 4d6b069..299382d 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -33,6 +33,7 @@
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+#define USB_VID_UNIWILL 0x1584
#define USB_VID_WIDEVIEW 0x14aa
/* Product IDs */
@@ -46,6 +47,7 @@
#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
+#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
@@ -53,7 +55,9 @@
#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
#define USB_PID_DIBCOM_STK7700P 0x1e14
+#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
+#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
@@ -97,7 +101,9 @@
#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941
#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
-#define USB_PID_AVERMEDIA_VOLAR 0x1234
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
+#define USB_PID_AVERMEDIA_VOLAR 0xa807
+#define USB_PID_AVERMEDIA_VOLAR_2 0xb808
#define USB_PID_NEBULA_DIGITV 0x0201
#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
@@ -110,8 +116,8 @@
#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
#define USB_PID_MEDION_MD95700 0x0932
#define USB_PID_KYE_DVB_T_COLD 0x701e
#define USB_PID_KYE_DVB_T_WARM 0x701f
@@ -125,7 +131,9 @@
#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
#define USB_PID_GENPIX_8PSK_COLD 0x0200
#define USB_PID_GENPIX_8PSK_WARM 0x0201
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index 794e447..19ff597 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -90,7 +90,9 @@ schedule:
int dvb_usb_remote_init(struct dvb_usb_device *d)
{
+ struct input_dev *input_dev;
int i;
+ int err;
if (d->props.rc_key_map == NULL ||
d->props.rc_query == NULL ||
@@ -100,23 +102,24 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
- d->rc_input_dev = input_allocate_device();
- if (!d->rc_input_dev)
+ input_dev = input_allocate_device();
+ if (!input_dev)
return -ENOMEM;
- d->rc_input_dev->evbit[0] = BIT(EV_KEY);
- d->rc_input_dev->keycodesize = sizeof(unsigned char);
- d->rc_input_dev->keycodemax = KEY_MAX;
- d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver";
- d->rc_input_dev->phys = d->rc_phys;
- usb_to_input_id(d->udev, &d->rc_input_dev->id);
- d->rc_input_dev->cdev.dev = &d->udev->dev;
+ input_dev->evbit[0] = BIT(EV_KEY);
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = KEY_MAX;
+ input_dev->name = "IR-receiver inside an USB DVB receiver";
+ input_dev->phys = d->rc_phys;
+ usb_to_input_id(d->udev, &input_dev->id);
+ input_dev->cdev.dev = &d->udev->dev;
/* set the bits for the keys */
deb_rc("key map size: %d\n", d->props.rc_key_map_size);
for (i = 0; i < d->props.rc_key_map_size; i++) {
- deb_rc("setting bit for event %d item %d\n",d->props.rc_key_map[i].event, i);
- set_bit(d->props.rc_key_map[i].event, d->rc_input_dev->keybit);
+ deb_rc("setting bit for event %d item %d\n",
+ d->props.rc_key_map[i].event, i);
+ set_bit(d->props.rc_key_map[i].event, input_dev->keybit);
}
/* Start the remote-control polling. */
@@ -124,10 +127,16 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
d->props.rc_interval = 100; /* default */
/* setting these two values to non-zero, we have to manage key repeats */
- d->rc_input_dev->rep[REP_PERIOD] = d->props.rc_interval;
- d->rc_input_dev->rep[REP_DELAY] = d->props.rc_interval + 150;
+ input_dev->rep[REP_PERIOD] = d->props.rc_interval;
+ input_dev->rep[REP_DELAY] = d->props.rc_interval + 150;
- input_register_device(d->rc_input_dev);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ d->rc_input_dev = input_dev;
INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 7375eb2..518d67f 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -194,19 +194,19 @@ static struct dvb_usb_device_properties gp8psk_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = gp8psk_streaming_ctrl,
- .frontend_attach = gp8psk_frontend_attach,
- /* parameter for the MPEG2-data transfer */
+ .streaming_ctrl = gp8psk_streaming_ctrl,
+ .frontend_attach = gp8psk_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x82,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
}
},
.power_ctrl = gp8psk_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index a58874c..d48622e 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -163,23 +163,23 @@ static struct dvb_usb_device_properties nova_t_properties = {
.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,
- .pid_filter = dibusb_pid_filter,
- .pid_filter_ctrl = dibusb_pid_filter_ctrl,
- .frontend_attach = dibusb_dib3000mc_frontend_attach,
- .tuner_attach = dibusb_dib3000mc_tuner_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 7,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .pid_filter = dibusb_pid_filter,
+ .pid_filter_ctrl = dibusb_pid_filter_ctrl,
+ .frontend_attach = dibusb_dib3000mc_frontend_attach,
+ .tuner_attach = dibusb_dib3000mc_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 7,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
}
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
new file mode 100644
index 0000000..95d2997
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -0,0 +1,270 @@
+/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
+ * (e.g. Pinnacle 400e DVB-S USB2.0).
+ *
+ * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes.
+ *
+ * TDA8263 + TDA10086
+ *
+ * I2C addresses:
+ * 0x08 - LNBP21PD - LNB power supply
+ * 0x0e - TDA10086 - Demodulator
+ * 0x50 - FX2 eeprom
+ * 0x60 - TDA8263 - Tuner
+ * 0x78 ???
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#define DVB_USB_LOG_PREFIX "ttusb2"
+#include "dvb-usb.h"
+
+#include "ttusb2.h"
+
+#include "tda826x.h"
+#include "tda10086.h"
+#include "lnbp21.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);
+
+struct ttusb2_state {
+ u8 id;
+};
+
+static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
+ u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ struct ttusb2_state *st = d->priv;
+ u8 s[wlen+4],r[64] = { 0 };
+ int ret = 0;
+
+ memset(s,0,wlen+4);
+
+ s[0] = 0xaa;
+ s[1] = ++st->id;
+ s[2] = cmd;
+ s[3] = wlen;
+ memcpy(&s[4],wbuf,wlen);
+
+ ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0);
+
+ if (ret != 0 ||
+ r[0] != 0x55 ||
+ r[1] != s[1] ||
+ r[2] != cmd ||
+ (rlen > 0 && r[3] != rlen)) {
+ warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]);
+ return -EIO;
+ }
+
+ if (rlen > 0)
+ memcpy(rbuf, &r[4], rlen);
+
+ 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;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num > 2)
+ 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);
+
+ obuf[0] = (msg[i].addr << 1) | read;
+ obuf[1] = msg[i].len;
+
+ /* read request */
+ if (read)
+ obuf[2] = msg[i+1].len;
+ else
+ obuf[2] = 0;
+
+ 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);
+ i++;
+ }
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 ttusb2_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ttusb2_i2c_algo = {
+ .master_xfer = ttusb2_i2c_xfer,
+ .functionality = ttusb2_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static int ttusb2_identify_state (struct usb_device *udev, struct
+ dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
+ return 0;
+}
+
+static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ u8 b = onoff;
+ ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0);
+ return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0);
+}
+
+
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x0e,
+ .invert = 0,
+};
+
+static int ttusb2_frontend_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) {
+ deb_info("TDA10086 attach failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ttusb2_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ if (dvb_attach(tda826x_attach, adap->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) {
+ deb_info("LNBP21 attach failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ttusb2_properties;
+
+static int ttusb2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return dvb_usb_device_init(intf,&ttusb2_properties,THIS_MODULE,NULL);
+}
+
+static struct usb_device_id ttusb2_table [] = {
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE (usb, ttusb2_table);
+
+static struct dvb_usb_device_properties ttusb2_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-pctv-400e-01.fw",
+
+ .size_of_priv = sizeof(struct ttusb2_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
+
+ .frontend_attach = ttusb2_frontend_attach,
+ .tuner_attach = ttusb2_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = ttusb2_power_ctrl,
+ .identify_state = ttusb2_identify_state,
+
+ .i2c_algo = &ttusb2_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "Pinnacle 400e DVB-S USB2.0",
+ { &ttusb2_table[0], NULL },
+ { NULL },
+ },
+ }
+};
+
+static struct usb_driver ttusb2_driver = {
+ .name = "dvb_usb_ttusb2",
+ .probe = ttusb2_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = ttusb2_table,
+};
+
+/* module stuff */
+static int __init ttusb2_module_init(void)
+{
+ int result;
+ if ((result = usb_register(&ttusb2_driver))) {
+ err("usb_register failed. Error number %d",result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit ttusb2_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&ttusb2_driver);
+}
+
+module_init (ttusb2_module_init);
+module_exit (ttusb2_module_exit);
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/dvb/dvb-usb/ttusb2.h
new file mode 100644
index 0000000..52a63af
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ttusb2.h
@@ -0,0 +1,70 @@
+/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
+ * (e.g. Pinnacle 400e DVB-S USB2.0).
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.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, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#ifndef _DVB_USB_TTUSB2_H_
+#define _DVB_USB_TTUSB2_H_
+
+/* TTUSB protocol
+ *
+ * always to messages (out/in)
+ * out message:
+ * 0xaa <id> <cmdbyte> <datalen> <data...>
+ *
+ * in message (complete block is always 0x40 bytes long)
+ * 0x55 <id> <cmdbyte> <datalen> <data...>
+ *
+ * id is incremented for each transaction
+ */
+
+#define CMD_DSP_DOWNLOAD 0x13
+/* out data: <byte>[28]
+ * last block must be empty */
+
+#define CMD_DSP_BOOT 0x14
+/* out data: nothing */
+
+#define CMD_POWER 0x15
+/* out data: <on=1/off=0> */
+
+#define CMD_LNB 0x16
+/* out data: <power=1> <18V=0,13V=1> <tone> <??=1> <??=1> */
+
+#define CMD_GET_VERSION 0x17
+/* in data: <version_byte>[5] */
+
+#define CMD_DISEQC 0x18
+/* out data: <master=0xff/burst=??> <cmdlen> <cmdbytes>[cmdlen] */
+
+#define CMD_PID_ENABLE 0x22
+/* out data: <index> <type: ts=1/sec=2> <pid msb> <pid lsb> */
+
+#define CMD_PID_DISABLE 0x23
+/* out data: <index> */
+
+#define CMD_FILTER_ENABLE 0x24
+/* out data: <index> <pid_idx> <filter>[12] <mask>[12] */
+
+#define CMD_FILTER_DISABLE 0x25
+/* out data: <index> */
+
+#define CMD_GET_DSP_VERSION 0x26
+/* in data: <version_byte>[28] */
+
+#define CMD_I2C_XFER 0x31
+/* out data: <addr << 1> <sndlen> <rcvlen> <data>[sndlen]
+ * in data: <addr << 1> <sndlen> <rcvlen> <data>[rcvlen] */
+
+#define CMD_I2C_BITRATE 0x32
+/* out data: <default=0> */
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index f9941ea..f77b48f 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -99,21 +99,21 @@ static struct dvb_usb_device_properties umt_properties = {
.num_adapters = 1,
.adapter = {
{
- .streaming_ctrl = dibusb2_0_streaming_ctrl,
- .frontend_attach = umt_mt352_frontend_attach,
- .tuner_attach = umt_tuner_attach,
+ .streaming_ctrl = dibusb2_0_streaming_ctrl,
+ .frontend_attach = umt_mt352_frontend_attach,
+ .tuner_attach = umt_tuner_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 20,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 512,
- }
- }
- },
+ .count = 20,
+ .endpoint = 0x06,
+ .u = {
+ .bulk = {
+ .buffersize = 512,
+ }
+ }
+ },
.size_of_priv = sizeof(struct dibusb_state),
}
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index 02bd61a..16533b3 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -275,22 +275,22 @@ static struct dvb_usb_device_properties vp702x_properties = {
.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
.streaming_ctrl = vp702x_streaming_ctrl,
- .frontend_attach = vp702x_frontend_attach,
+ .frontend_attach = vp702x_frontend_attach,
- /* parameter for the MPEG2-data transfer */
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 10,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
.size_of_priv = sizeof(struct vp702x_state),
}
- },
+ },
.read_mac_address = vp702x_read_mac_addr,
.rc_key_map = vp702x_rc_keys,
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index b4cf002..69a46b3 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -125,7 +125,25 @@ static struct dvb_usb_rc_key vp7045_rc_keys[] = {
{ 0x00, 0x00, KEY_TAB }, /* Tab */
{ 0x00, 0x48, KEY_INFO }, /* Preview */
{ 0x00, 0x04, KEY_LIST }, /* RecordList */
- { 0x00, 0x0f, KEY_TEXT } /* Teletext */
+ { 0x00, 0x0f, KEY_TEXT }, /* Teletext */
+ { 0x00, 0x41, KEY_PREVIOUSSONG },
+ { 0x00, 0x42, KEY_NEXTSONG },
+ { 0x00, 0x4b, KEY_UP },
+ { 0x00, 0x51, KEY_DOWN },
+ { 0x00, 0x4e, KEY_LEFT },
+ { 0x00, 0x52, KEY_RIGHT },
+ { 0x00, 0x4f, KEY_ENTER },
+ { 0x00, 0x13, KEY_CANCEL },
+ { 0x00, 0x4a, KEY_CLEAR },
+ { 0x00, 0x54, KEY_PRINT }, /* Capture */
+ { 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */
+ { 0x00, 0x08, KEY_VIDEO }, /* A/V */
+ { 0x00, 0x07, KEY_SLEEP }, /* Hibernate */
+ { 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */
+ { 0x00, 0x18, KEY_RED},
+ { 0x00, 0x53, KEY_GREEN},
+ { 0x00, 0x5e, KEY_YELLOW},
+ { 0x00, 0x5f, KEY_BLUE}
};
static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -217,18 +235,18 @@ static struct dvb_usb_device_properties vp7045_properties = {
.num_adapters = 1,
.adapter = {
{
- .frontend_attach = vp7045_frontend_attach,
- /* parameter for the MPEG2-data transfer */
+ .frontend_attach = vp7045_frontend_attach,
+ /* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
}
},
.power_ctrl = vp7045_power_ctrl,
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index aebb8d6f..af314bb 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -172,6 +172,22 @@ config DVB_DIB3000MC
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
+config DVB_DIB7000M
+ tristate "DiBcom 7000MA/MB/PA/PB/MC"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000P
+ tristate "DiBcom 7000PC"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
@@ -281,6 +297,14 @@ config DVB_TUNER_MT2060
help
A driver for the silicon IF tuner MT2060 from Microtune.
+config DVB_TUNER_LGH06XF
+ tristate "LG TDVS-H06xF ATSC tuner"
+ depends on DVB_CORE && I2C
+ select DVB_PLL
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the LG TDVS-H06xF ATSC tuner family.
+
comment "Miscellaneous devices"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index dce9cf0..3fa6e5d 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -13,6 +13,8 @@ obj-$(CONFIG_DVB_TDA8083) += tda8083.o
obj-$(CONFIG_DVB_L64781) += l64781.o
obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
obj-$(CONFIG_DVB_MT312) += mt312.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
@@ -37,3 +39,4 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
index 3561a77..5da6617 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -511,16 +511,11 @@ static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000
/* a channel for autosearch */
- reg = 0;
- if (chan->nfft == -1 && chan->guard == -1) reg = 7;
- if (chan->nfft == -1 && chan->guard != -1) reg = 2;
- if (chan->nfft != -1 && chan->guard == -1) reg = 3;
-
fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2;
fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2;
fchan.vit_hrch = 0; fchan.vit_select_hp = 1;
- dib3000mc_set_channel_cfg(state, &fchan, reg);
+ dib3000mc_set_channel_cfg(state, &fchan, 7);
reg = dib3000mc_read_word(state, 0);
dib3000mc_write_word(state, 0, reg | (1 << 8));
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
new file mode 100644
index 0000000..f5d40aa
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -0,0 +1,1191 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB7000M and
+ * first generation DiB7000P-demodulator-family.
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.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 the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000m.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M:"); printk(args); } } while (0)
+
+struct dib7000m_state {
+ struct dvb_frontend demod;
+ struct dib7000m_config cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+/* offset is 1 in case of the 7000MC */
+ u8 reg_offs;
+
+ u16 wbd_ref;
+
+ u8 current_band;
+ fe_bandwidth_t current_bandwidth;
+ struct dibx000_agc_config *current_agc;
+ u32 timf;
+
+ u16 revision;
+};
+
+enum dib7000m_power_mode {
+ DIB7000M_POWER_ALL = 0,
+
+ DIB7000M_POWER_NO,
+ DIB7000M_POWER_INTERF_ANALOG_AGC,
+ DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
+ DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD,
+ DIB7000M_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
+{
+ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode)
+{
+ int ret = 0;
+ u16 outreg, fifo_threshold, smo_mode,
+ sram = 0x0005; /* by default SRAM output is disabled */
+
+ outreg = 0;
+ fifo_threshold = 1792;
+ smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1);
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity)
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ else
+ sram |= 0x0c00;
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z: // disable
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ break;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5) ;
+
+ ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode);
+ ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */
+ ret |= dib7000m_write_word(state, 1795, outreg);
+ ret |= dib7000m_write_word(state, 1805, sram);
+
+ return ret;
+}
+
+static int dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode)
+{
+ /* by default everything is going to be powered off */
+ u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff;
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB7000M_POWER_ALL:
+ reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000;
+ break;
+
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
+ case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
+ break;
+
+ case DIB7000M_POWER_INTERF_ANALOG_AGC:
+ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
+ reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000;
+ break;
+
+ case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD:
+ reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000;
+ break;
+ case DIB7000M_POWER_NO:
+ break;
+ }
+
+ /* always power down unused parts */
+ if (!state->cfg.mobile_mode)
+ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
+
+ /* P_sdio_select_clk = 0 on MC */
+ if (state->revision != 0x4000)
+ reg_906 <<= 1;
+
+ dib7000m_write_word(state, 903, reg_903);
+ dib7000m_write_word(state, 904, reg_904);
+ dib7000m_write_word(state, 905, reg_905);
+ dib7000m_write_word(state, 906, reg_906);
+
+ return 0;
+}
+
+static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no)
+{
+ int ret = 0;
+ u16 reg_913 = dib7000m_read_word(state, 913),
+ reg_914 = dib7000m_read_word(state, 914);
+
+ switch (no) {
+ case DIBX000_SLOW_ADC_ON:
+ reg_914 |= (1 << 1) | (1 << 0);
+ ret |= dib7000m_write_word(state, 914, reg_914);
+ reg_914 &= ~(1 << 1);
+ break;
+
+ case DIBX000_SLOW_ADC_OFF:
+ reg_914 |= (1 << 1) | (1 << 0);
+ break;
+
+ case DIBX000_ADC_ON:
+ if (state->revision == 0x4000) { // workaround for PA/MA
+ // power-up ADC
+ dib7000m_write_word(state, 913, 0);
+ dib7000m_write_word(state, 914, reg_914 & 0x3);
+ // power-down bandgag
+ dib7000m_write_word(state, 913, (1 << 15));
+ dib7000m_write_word(state, 914, reg_914 & 0x3);
+ }
+
+ reg_913 &= 0x0fff;
+ reg_914 &= 0x0003;
+ break;
+
+ case DIBX000_ADC_OFF: // leave the VBG voltage on
+ reg_913 |= (1 << 14) | (1 << 13) | (1 << 12);
+ reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+ break;
+
+ case DIBX000_VBG_ENABLE:
+ reg_913 &= ~(1 << 15);
+ break;
+
+ case DIBX000_VBG_DISABLE:
+ reg_913 |= (1 << 15);
+ break;
+
+ default:
+ break;
+ }
+
+// dprintk("-D- 913: %x, 914: %x\n", reg_913, reg_914);
+
+ ret |= dib7000m_write_word(state, 913, reg_913);
+ ret |= dib7000m_write_word(state, 914, reg_914);
+
+ return ret;
+}
+
+static int dib7000m_set_bandwidth(struct dvb_frontend *demod, u8 bw_idx)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ u32 timf;
+
+ // store the current bandwidth for later use
+ state->current_bandwidth = bw_idx;
+
+ if (state->timf == 0) {
+ dprintk("-D- Using default timf\n");
+ timf = state->cfg.bw->timf;
+ } else {
+ dprintk("-D- Using updated timf\n");
+ timf = state->timf;
+ }
+
+ timf = timf * (BW_INDEX_TO_KHZ(bw_idx) / 100) / 80;
+
+ dib7000m_write_word(state, 23, (timf >> 16) & 0xffff);
+ dib7000m_write_word(state, 24, (timf ) & 0xffff);
+
+ return 0;
+}
+
+static int dib7000m_sad_calib(struct dib7000m_state *state)
+{
+
+/* internal */
+// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+ dib7000m_write_word(state, 929, (0 << 1) | (0 << 0));
+ dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096
+
+ /* do the calibration */
+ dib7000m_write_word(state, 929, (1 << 0));
+ dib7000m_write_word(state, 929, (0 << 0));
+
+ msleep(1);
+
+ return 0;
+}
+
+static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw)
+{
+ dib7000m_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+ dib7000m_write_word(state, 19, (bw->internal*1000) & 0xffff);
+ dib7000m_write_word(state, 21, (bw->ifreq >> 16) & 0xffff);
+ dib7000m_write_word(state, 22, bw->ifreq & 0xffff);
+
+ dib7000m_write_word(state, 928, bw->sad_cfg);
+}
+
+static void dib7000m_reset_pll(struct dib7000m_state *state)
+{
+ const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+ u16 reg_907,reg_910;
+
+ /* default */
+ reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) |
+ (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) |
+ (bw->enable_refdiv << 1) | (0 << 0);
+ reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset;
+
+ // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value)
+ // this is only working only for 30 MHz crystals
+ if (!state->cfg.quartz_direct) {
+ reg_910 |= (1 << 5); // forcing the predivider to 1
+
+ // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2)
+ if(state->cfg.input_clk_is_div_2)
+ reg_907 |= (16 << 9);
+ else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary
+ reg_907 |= (8 << 9);
+ } else {
+ reg_907 |= (bw->pll_ratio & 0x3f) << 9;
+ reg_910 |= (bw->pll_prediv << 5);
+ }
+
+ dib7000m_write_word(state, 910, reg_910); // pll cfg
+ dib7000m_write_word(state, 907, reg_907); // clk cfg0
+ dib7000m_write_word(state, 908, 0x0006); // clk_cfg1
+
+ dib7000m_reset_pll_common(state, bw);
+}
+
+static void dib7000mc_reset_pll(struct dib7000m_state *state)
+{
+ const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+
+ // clk_cfg0
+ dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0));
+
+ // clk_cfg1
+ //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) |
+ dib7000m_write_word(state, 908, (0 << 14) | (3 << 12) |(0 << 11) |
+ (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) |
+ (bw->pll_bypass << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0));
+
+ // smpl_cfg
+ dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7));
+
+ dib7000m_reset_pll_common(state, bw);
+}
+
+static int dib7000m_reset_gpio(struct dib7000m_state *st)
+{
+ /* reset the GPIOs */
+ dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",
+ st->cfg.gpio_dir, st->cfg.gpio_val,st->cfg.gpio_pwm_pos);
+
+ dib7000m_write_word(st, 773, st->cfg.gpio_dir);
+ dib7000m_write_word(st, 774, st->cfg.gpio_val);
+
+ /* TODO 782 is P_gpio_od */
+
+ dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos);
+
+ dib7000m_write_word(st, 780, st->cfg.pwm_freq_div);
+ return 0;
+}
+
+static int dib7000m_demod_reset(struct dib7000m_state *state)
+{
+ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+ /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */
+ dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+ /* restart all parts */
+ dib7000m_write_word(state, 898, 0xffff);
+ dib7000m_write_word(state, 899, 0xffff);
+ dib7000m_write_word(state, 900, 0xff0f);
+ dib7000m_write_word(state, 901, 0xfffc);
+
+ dib7000m_write_word(state, 898, 0);
+ dib7000m_write_word(state, 899, 0);
+ dib7000m_write_word(state, 900, 0);
+ dib7000m_write_word(state, 901, 0);
+
+ if (state->revision == 0x4000)
+ dib7000m_reset_pll(state);
+ else
+ dib7000mc_reset_pll(state);
+
+ if (dib7000m_reset_gpio(state) != 0)
+ dprintk("-E- GPIO reset was not successful.\n");
+
+ if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ dprintk("-E- OUTPUT_MODE could not be resetted.\n");
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) );
+
+ dib7000m_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+
+ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+ dib7000m_sad_calib(state);
+ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF);
+
+ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static void dib7000m_restart_agc(struct dib7000m_state *state)
+{
+ // P_restart_iqc & P_restart_agc
+ dib7000m_write_word(state, 898, 0x0c00);
+ dib7000m_write_word(state, 898, 0x0000);
+}
+
+static int dib7000m_agc_soft_split(struct dib7000m_state *state)
+{
+ u16 agc,split_offset;
+
+ if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0)
+ return 0;
+
+ // n_agc_global
+ agc = dib7000m_read_word(state, 390);
+
+ if (agc > state->current_agc->split.min_thres)
+ split_offset = state->current_agc->split.min;
+ else if (agc < state->current_agc->split.max_thres)
+ split_offset = state->current_agc->split.max;
+ else
+ split_offset = state->current_agc->split.max *
+ (agc - state->current_agc->split.min_thres) /
+ (state->current_agc->split.max_thres - state->current_agc->split.min_thres);
+
+ dprintk("AGC split_offset: %d\n",split_offset);
+
+ // P_agc_force_split and P_agc_split_offset
+ return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset);
+}
+
+static int dib7000m_update_lna(struct dib7000m_state *state)
+{
+ int i;
+ u16 dyn_gain;
+
+ // when there is no LNA to program return immediatly
+ if (state->cfg.update_lna == NULL)
+ return 0;
+
+ msleep(60);
+ for (i = 0; i < 20; i++) {
+ // read dyn_gain here (because it is demod-dependent and not tuner)
+ dyn_gain = dib7000m_read_word(state, 390);
+
+ dprintk("agc global: %d\n", dyn_gain);
+
+ if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+ dib7000m_restart_agc(state);
+ msleep(60);
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
+{
+ struct dibx000_agc_config *agc = NULL;
+ int i;
+ if (state->current_band == band)
+ return;
+ state->current_band = band;
+
+ for (i = 0; i < state->cfg.agc_config_count; i++)
+ if (state->cfg.agc[i].band_caps & band) {
+ agc = &state->cfg.agc[i];
+ break;
+ }
+
+ if (agc == NULL) {
+ dprintk("-E- No valid AGC configuration found for band 0x%02x\n",band);
+ return;
+ }
+
+ state->current_agc = agc;
+
+ /* AGC */
+ dib7000m_write_word(state, 72 , agc->setup);
+ dib7000m_write_word(state, 73 , agc->inv_gain);
+ dib7000m_write_word(state, 74 , agc->time_stabiliz);
+ dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock);
+
+ // Demod AGC loop configuration
+ dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp);
+ dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp);
+
+ dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+ /* AGC continued */
+ if (state->wbd_ref != 0)
+ dib7000m_write_word(state, 102, state->wbd_ref);
+ else // use default
+ dib7000m_write_word(state, 102, agc->wbd_ref);
+
+ dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+ dib7000m_write_word(state, 104, agc->agc1_max);
+ dib7000m_write_word(state, 105, agc->agc1_min);
+ dib7000m_write_word(state, 106, agc->agc2_max);
+ dib7000m_write_word(state, 107, agc->agc2_min);
+ dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+ dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+ if (state->revision > 0x4000) { // settings for the MC
+ dib7000m_write_word(state, 71, agc->agc1_pt3);
+// dprintk("-D- 929: %x %d %d\n",
+// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel);
+ dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2));
+ } else {
+ // wrong default values
+ u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 };
+ for (i = 0; i < 9; i++)
+ dib7000m_write_word(state, 88 + i, b[i]);
+ }
+}
+
+static void dib7000m_update_timf_freq(struct dib7000m_state *state)
+{
+ u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437);
+ state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+ dib7000m_write_word(state, 23, (u16) (timf >> 16));
+ dib7000m_write_word(state, 24, (u16) (timf & 0xffff));
+ dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000m_set_channel(struct dib7000m_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+ u16 value, est[4];
+
+ dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->RF_kHz));
+
+ /* nfft, guard, qam, alpha */
+ dib7000m_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+ dib7000m_write_word(state, 5, (seq << 4));
+
+ /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+ value = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+ if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+ value |= (ch->vit_code_rate_hp << 1);
+ else
+ value |= (ch->vit_code_rate_lp << 1);
+ dib7000m_write_word(state, 267 + state->reg_offs, value);
+
+ /* offset loop parameters */
+
+ /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */
+ dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80);
+
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */
+ dib7000m_write_word(state, 32, (0 << 4) | 0x3);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */
+ dib7000m_write_word(state, 33, (0 << 4) | 0x5);
+
+ /* P_dvsy_sync_wait */
+ switch (ch->nfft) {
+ case 1: value = 256; break;
+ case 2: value = 128; break;
+ case 0:
+ default: value = 64; break;
+ }
+ value *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+ value <<= 4;
+
+ /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */
+ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+ if (ch->intlv_native || state->revision > 0x4000)
+ value |= (1 << 2) | (2 << 0);
+ else
+ value |= 0;
+ dib7000m_write_word(state, 266 + state->reg_offs, value);
+
+ /* channel estimation fine configuration */
+ switch (ch->nqam) {
+ case 2:
+ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
+ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
+ break;
+ case 1:
+ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
+ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
+ break;
+ default:
+ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
+ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
+ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
+ break;
+ }
+ for (value = 0; value < 4; value++)
+ dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]);
+
+ // set power-up level: interf+analog+AGC
+ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC);
+ dib7000m_set_adc_state(state, DIBX000_ADC_ON);
+
+ msleep(7);
+
+ //AGC initialization
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 1);
+
+ dib7000m_restart_agc(state);
+
+ // wait AGC rough lock time
+ msleep(5);
+
+ dib7000m_update_lna(state);
+ dib7000m_agc_soft_split(state);
+
+ // wait AGC accurate lock time
+ msleep(7);
+
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 0);
+
+ // set power-up level: autosearch
+ dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD);
+}
+
+static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ struct dibx000_ofdm_channel auto_ch;
+ int ret = 0;
+ u32 value;
+
+ INIT_OFDM_CHANNEL(&auto_ch);
+ auto_ch.RF_kHz = ch->RF_kHz;
+ auto_ch.Bw = ch->Bw;
+ auto_ch.nqam = 2;
+ auto_ch.guard = 0;
+ auto_ch.nfft = 1;
+ auto_ch.vit_alpha = 1;
+ auto_ch.vit_select_hp = 1;
+ auto_ch.vit_code_rate_hp = 2;
+ auto_ch.vit_code_rate_lp = 3;
+ auto_ch.vit_hrch = 0;
+ auto_ch.intlv_native = 1;
+
+ dib7000m_set_channel(state, &auto_ch, 7);
+
+ // always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+ value = 30 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+ ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time
+ value = 100 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+ ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time
+ value = 500 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+ ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time
+
+ // start search
+ value = dib7000m_read_word(state, 0);
+ ret |= dib7000m_write_word(state, 0, value | (1 << 9));
+
+ /* clear n_irq_pending */
+ if (state->revision == 0x4000)
+ dib7000m_write_word(state, 1793, 0);
+ else
+ dib7000m_read_word(state, 537);
+
+ ret |= dib7000m_write_word(state, 0, (u16) value);
+
+ return ret;
+}
+
+static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg)
+{
+ u16 irq_pending = dib7000m_read_word(state, reg);
+
+ if (irq_pending & 0x1) { // failed
+ dprintk("#\n");
+ return 1;
+ }
+
+ if (irq_pending & 0x2) { // succeeded
+ dprintk("!\n");
+ return 2;
+ }
+ return 0; // still pending
+}
+
+static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ if (state->revision == 0x4000)
+ return dib7000m_autosearch_irq(state, 1793);
+ else
+ return dib7000m_autosearch_irq(state, 537);
+}
+
+static int dib7000m_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ int ret = 0;
+ u16 value;
+
+ // we are already tuned - just resuming from suspend
+ if (ch != NULL)
+ dib7000m_set_channel(state, ch, 0);
+ else
+ return -EINVAL;
+
+ // restart demod
+ ret |= dib7000m_write_word(state, 898, 0x4000);
+ ret |= dib7000m_write_word(state, 898, 0x0000);
+ msleep(45);
+
+ ret |= dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD);
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ // never achieved a lock with that bandwidth so far - wait for timfreq to update
+ if (state->timf == 0)
+ msleep(200);
+
+ //dump_reg(state);
+ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+ value = (6 << 8) | 0x80;
+ switch (ch->nfft) {
+ case 0: value |= (7 << 12); break;
+ case 1: value |= (9 << 12); break;
+ case 2: value |= (8 << 12); break;
+ }
+ ret |= dib7000m_write_word(state, 26, value);
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+ value = (0 << 4);
+ switch (ch->nfft) {
+ case 0: value |= 0x6; break;
+ case 1: value |= 0x8; break;
+ case 2: value |= 0x7; break;
+ }
+ ret |= dib7000m_write_word(state, 32, value);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+ value = (0 << 4);
+ switch (ch->nfft) {
+ case 0: value |= 0x6; break;
+ case 1: value |= 0x8; break;
+ case 2: value |= 0x7; break;
+ }
+ ret |= dib7000m_write_word(state, 33, value);
+
+ // we achieved a lock - it's time to update the osc freq
+ if ((dib7000m_read_word(state, 535) >> 6) & 0x1)
+ dib7000m_update_timf_freq(state);
+
+ return ret;
+}
+
+static int dib7000m_init(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ int ret = 0;
+ u8 o = state->reg_offs;
+
+ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+ if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
+ dprintk("-E- could not start Slow ADC\n");
+
+ if (state->cfg.dvbt_mode)
+ dib7000m_write_word(state, 1796, 0x0); // select DVB-T output
+
+ if (state->cfg.mobile_mode)
+ ret |= dib7000m_write_word(state, 261 + o, 2);
+ else
+ ret |= dib7000m_write_word(state, 224 + o, 1);
+
+ ret |= dib7000m_write_word(state, 173 + o, 0);
+ ret |= dib7000m_write_word(state, 174 + o, 0);
+ ret |= dib7000m_write_word(state, 175 + o, 0);
+ ret |= dib7000m_write_word(state, 176 + o, 0);
+ ret |= dib7000m_write_word(state, 177 + o, 0);
+ ret |= dib7000m_write_word(state, 178 + o, 0);
+ ret |= dib7000m_write_word(state, 179 + o, 0);
+ ret |= dib7000m_write_word(state, 180 + o, 0);
+
+ // P_corm_thres Lock algorithms configuration
+ ret |= dib7000m_write_word(state, 26, 0x6680);
+
+ // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on
+ ret |= dib7000m_write_word(state, 170 + o, 0x0410);
+ // P_fft_nb_to_cut
+ ret |= dib7000m_write_word(state, 182 + o, 8192);
+ // P_pha3_thres
+ ret |= dib7000m_write_word(state, 195 + o, 0x0ccd);
+ // P_cti_use_cpe, P_cti_use_prog
+ ret |= dib7000m_write_word(state, 196 + o, 0);
+ // P_cspu_regul, P_cspu_win_cut
+ ret |= dib7000m_write_word(state, 205 + o, 0x200f);
+ // P_adp_regul_cnt
+ ret |= dib7000m_write_word(state, 214 + o, 0x023d);
+ // P_adp_noise_cnt
+ ret |= dib7000m_write_word(state, 215 + o, 0x00a4);
+ // P_adp_regul_ext
+ ret |= dib7000m_write_word(state, 216 + o, 0x00a4);
+ // P_adp_noise_ext
+ ret |= dib7000m_write_word(state, 217 + o, 0x7ff0);
+ // P_adp_fil
+ ret |= dib7000m_write_word(state, 218 + o, 0x3ccc);
+
+ // P_2d_byp_ti_num
+ ret |= dib7000m_write_word(state, 226 + o, 0);
+
+ // P_fec_*
+ ret |= dib7000m_write_word(state, 281 + o, 0x0010);
+ // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ ret |= dib7000m_write_word(state, 294 + o,0x0062);
+
+ // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ...
+ if(state->cfg.tuner_is_baseband)
+ ret |= dib7000m_write_word(state, 36, 0x0755);
+ else
+ ret |= dib7000m_write_word(state, 36, 0x1f55);
+
+ // auto search configuration
+ ret |= dib7000m_write_word(state, 2, 0x0004);
+ ret |= dib7000m_write_word(state, 3, 0x1000);
+ ret |= dib7000m_write_word(state, 4, 0x0814);
+ ret |= dib7000m_write_word(state, 6, 0x001b);
+ ret |= dib7000m_write_word(state, 7, 0x7740);
+ ret |= dib7000m_write_word(state, 8, 0x005b);
+ ret |= dib7000m_write_word(state, 9, 0x8d80);
+ ret |= dib7000m_write_word(state, 10, 0x01c9);
+ ret |= dib7000m_write_word(state, 11, 0xc380);
+ ret |= dib7000m_write_word(state, 12, 0x0000);
+ ret |= dib7000m_write_word(state, 13, 0x0080);
+ ret |= dib7000m_write_word(state, 14, 0x0000);
+ ret |= dib7000m_write_word(state, 15, 0x0090);
+ ret |= dib7000m_write_word(state, 16, 0x0001);
+ ret |= dib7000m_write_word(state, 17, 0xd4c0);
+ ret |= dib7000m_write_word(state, 263 + o,0x0001);
+
+ // P_divclksel=3 P_divbitsel=1
+ if (state->revision == 0x4000)
+ dib7000m_write_word(state, 909, (3 << 10) | (1 << 6));
+ else
+ dib7000m_write_word(state, 909, (3 << 4) | 1);
+
+ // Tuner IO bank: max drive (14mA)
+ ret |= dib7000m_write_word(state, 912 ,0x2c8a);
+
+ ret |= dib7000m_write_word(state, 1817, 1);
+
+ return ret;
+}
+
+static int dib7000m_sleep(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ dib7000m_set_output_mode(st, OUTMODE_HIGH_Z);
+ return dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY) |
+ dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) |
+ dib7000m_set_adc_state(st, DIBX000_ADC_OFF);
+}
+
+static int dib7000m_identify(struct dib7000m_state *state)
+{
+ u16 value;
+ if ((value = dib7000m_read_word(state, 896)) != 0x01b3) {
+ dprintk("-E- DiB7000M: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ state->revision = dib7000m_read_word(state, 897);
+ if (state->revision != 0x4000 &&
+ state->revision != 0x4001 &&
+ state->revision != 0x4002) {
+ dprintk("-E- DiB7000M: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ /* protect this driver to be used with 7000PC */
+ if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) {
+ dprintk("-E- DiB7000M: this driver does not work with DiB7000PC\n");
+ return -EREMOTEIO;
+ }
+
+ switch (state->revision) {
+ case 0x4000: dprintk("-I- found DiB7000MA/PA/MB/PB\n"); break;
+ case 0x4001: state->reg_offs = 1; dprintk("-I- found DiB7000HC\n"); break;
+ case 0x4002: state->reg_offs = 1; dprintk("-I- found DiB7000MC\n"); break;
+ }
+
+ return 0;
+}
+
+
+static int dib7000m_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 tps = dib7000m_read_word(state,480);
+
+ fep->inversion = INVERSION_AUTO;
+
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+ switch ((tps >> 8) & 0x3) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+ }
+
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+
+ switch ((tps >> 14) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
+
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+ }
+
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
+
+ /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */
+
+ return 0;
+}
+
+static int dib7000m_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
+
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib7000m_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, fep);
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 800, found;
+
+ dib7000m_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib7000m_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib7000m_get_frontend(fe, fep);
+ FEP2DIB(fep, &ch);
+ }
+
+ /* make this a config parameter */
+ dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib7000m_tune(fe, &ch);
+}
+
+static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 lock = dib7000m_read_word(state, 535);
+
+ *stat = 0;
+
+ if (lock & 0x8000)
+ *stat |= FE_HAS_SIGNAL;
+ if (lock & 0x3000)
+ *stat |= FE_HAS_CARRIER;
+ if (lock & 0x0100)
+ *stat |= FE_HAS_VITERBI;
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527);
+ return 0;
+}
+
+static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ *unc = dib7000m_read_word(state, 534);
+ return 0;
+}
+
+static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 val = dib7000m_read_word(state, 390);
+ *strength = 65535 - val;
+ return 0;
+}
+
+static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ *snr = 0x0000;
+ return 0;
+}
+
+static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void dib7000m_release(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ dibx000_exit_i2c_master(&st->i2c_master);
+ kfree(st);
+}
+
+struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000m_get_i2c_master);
+
+int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[])
+{
+ struct dib7000m_state st = { .i2c_adap = i2c };
+ int k = 0;
+ u8 new_addr = 0;
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st.cfg = cfg[k];
+
+ /* designated i2c address */
+ new_addr = (0x40 + k) << 1;
+ st.i2c_addr = new_addr;
+ if (dib7000m_identify(&st) != 0) {
+ st.i2c_addr = default_addr;
+ if (dib7000m_identify(&st) != 0) {
+ dprintk("DiB7000M #%d: not identified\n", k);
+ return -EIO;
+ }
+ }
+
+ /* start diversity to pull_down div_str - just for i2c-enumeration */
+ dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+ dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output
+
+ /* set new i2c address and force divstart */
+ dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ st.cfg = cfg[k];
+ st.i2c_addr = (0x40 + k) << 1;
+
+ // unforce divstr
+ dib7000m_write_word(&st,1794, st.i2c_addr << 2);
+
+ /* deactivate div - it was just for i2c-enumeration */
+ dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dib7000m_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000m_ops;
+struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)
+{
+ struct dvb_frontend *demod;
+ struct dib7000m_state *st;
+ st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config));
+ st->i2c_adap = i2c_adap;
+ st->i2c_addr = i2c_addr;
+
+ demod = &st->demod;
+ demod->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));
+
+ if (dib7000m_identify(st) != 0)
+ goto error;
+
+ if (st->revision == 0x4000)
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr);
+ else
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr);
+
+ dib7000m_demod_reset(st);
+
+ return demod;
+
+error:
+ kfree(st);
+ return NULL;
+}
+EXPORT_SYMBOL(dib7000m_attach);
+
+static struct dvb_frontend_ops dib7000m_ops = {
+ .info = {
+ .name = "DiBcom 7000MA/MB/PA/PB/MC",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dib7000m_release,
+
+ .init = dib7000m_init,
+ .sleep = dib7000m_sleep,
+
+ .set_frontend = dib7000m_set_frontend,
+ .get_tune_settings = dib7000m_fe_get_tune_settings,
+ .get_frontend = dib7000m_get_frontend,
+
+ .read_status = dib7000m_read_status,
+ .read_ber = dib7000m_read_ber,
+ .read_signal_strength = dib7000m_read_signal_strength,
+ .read_snr = dib7000m_read_snr,
+ .read_ucblocks = dib7000m_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h
new file mode 100644
index 0000000..597e9cc
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.h
@@ -0,0 +1,51 @@
+#ifndef DIB7000M_H
+#define DIB7000M_H
+
+#include "dibx000_common.h"
+
+struct dib7000m_config {
+ u8 dvbt_mode;
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ u8 mobile_mode;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ u8 agc_config_count;
+ struct dibx000_agc_config *agc;
+
+ struct dibx000_bandwidth_config *bw;
+
+#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ u16 pwm_freq_div;
+
+ u8 quartz_direct;
+
+ u8 input_clk_is_div_2;
+
+ int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000M_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg);
+extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+
+/* TODO
+extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
new file mode 100644
index 0000000..0349a4b
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -0,0 +1,1019 @@
+/*
+ * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC).
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.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 the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000p.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P:"); printk(args); } } while (0)
+
+struct dib7000p_state {
+ struct dvb_frontend demod;
+ struct dib7000p_config cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+ u16 wbd_ref;
+
+ u8 current_band;
+ fe_bandwidth_t current_bandwidth;
+ struct dibx000_agc_config *current_agc;
+ u32 timf;
+
+ u16 gpio_dir;
+ u16 gpio_val;
+};
+
+enum dib7000p_power_mode {
+ DIB7000P_POWER_ALL = 0,
+ DIB7000P_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
+{
+ u8 wb[2] = { reg >> 8, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
+{
+ int ret = 0;
+ u16 outreg, fifo_threshold, smo_mode;
+
+ outreg = 0;
+ fifo_threshold = 1792;
+ smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity)
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ else
+ outreg = (1 << 11);
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z: // disable
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ break;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5) ;
+
+ ret |= dib7000p_write_word(state, 235, smo_mode);
+ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */
+ ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */
+
+ return ret;
+}
+
+static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode)
+{
+ /* by default everything is powered off */
+ u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003,
+ reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff);
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB7000P_POWER_ALL:
+ reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff;
+ break;
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
+ case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
+ break;
+/* TODO following stuff is just converted from the dib7000-driver - check when is used what */
+ }
+
+ dib7000p_write_word(state, 774, reg_774);
+ dib7000p_write_word(state, 775, reg_775);
+ dib7000p_write_word(state, 776, reg_776);
+ dib7000p_write_word(state, 899, reg_899);
+ dib7000p_write_word(state, 1280, reg_1280);
+
+ return 0;
+}
+
+static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no)
+{
+ u16 reg_908 = dib7000p_read_word(state, 908),
+ reg_909 = dib7000p_read_word(state, 909);
+
+ switch (no) {
+ case DIBX000_SLOW_ADC_ON:
+ reg_909 |= (1 << 1) | (1 << 0);
+ dib7000p_write_word(state, 909, reg_909);
+ reg_909 &= ~(1 << 1);
+ break;
+
+ case DIBX000_SLOW_ADC_OFF:
+ reg_909 |= (1 << 1) | (1 << 0);
+ break;
+
+ case DIBX000_ADC_ON:
+ reg_908 &= 0x0fff;
+ reg_909 &= 0x0003;
+ break;
+
+ case DIBX000_ADC_OFF: // leave the VBG voltage on
+ reg_908 |= (1 << 14) | (1 << 13) | (1 << 12);
+ reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+ break;
+
+ case DIBX000_VBG_ENABLE:
+ reg_908 &= ~(1 << 15);
+ break;
+
+ case DIBX000_VBG_DISABLE:
+ reg_908 |= (1 << 15);
+ break;
+
+ default:
+ break;
+ }
+
+// dprintk("908: %x, 909: %x\n", reg_908, reg_909);
+
+ dib7000p_write_word(state, 908, reg_908);
+ dib7000p_write_word(state, 909, reg_909);
+}
+
+static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u32 timf;
+
+ // store the current bandwidth for later use
+ state->current_bandwidth = BW_Idx;
+
+ if (state->timf == 0) {
+ dprintk("-D- Using default timf\n");
+ timf = state->cfg.bw->timf;
+ } else {
+ dprintk("-D- Using updated timf\n");
+ timf = state->timf;
+ }
+
+ timf = timf * (BW_INDEX_TO_KHZ(BW_Idx) / 100) / 80;
+
+ dprintk("timf: %d\n",timf);
+
+ dib7000p_write_word(state, 23, (timf >> 16) & 0xffff);
+ dib7000p_write_word(state, 24, (timf ) & 0xffff);
+
+ return 0;
+}
+
+static int dib7000p_sad_calib(struct dib7000p_state *state)
+{
+/* internal */
+// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+ dib7000p_write_word(state, 73, (0 << 1) | (0 << 0));
+ dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096
+
+ /* do the calibration */
+ dib7000p_write_word(state, 73, (1 << 0));
+ dib7000p_write_word(state, 73, (0 << 0));
+
+ msleep(1);
+
+ return 0;
+}
+
+static void dib7000p_reset_pll(struct dib7000p_state *state)
+{
+ struct dibx000_bandwidth_config *bw = &state->cfg.bw[0];
+
+ dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset);
+ dib7000p_write_word(state, 900, ((bw->pll_ratio & 0x3f) << 9) | (bw->pll_bypass << 15) | (bw->modulo << 7) | (bw->ADClkSrc << 6) |
+ (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0));
+
+ dib7000p_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+ dib7000p_write_word(state, 19, (bw->internal*1000 ) & 0xffff);
+ dib7000p_write_word(state, 21, (bw->ifreq >> 16) & 0xffff);
+ dib7000p_write_word(state, 22, (bw->ifreq ) & 0xffff);
+
+ dib7000p_write_word(state, 72, bw->sad_cfg);
+}
+
+static int dib7000p_reset_gpio(struct dib7000p_state *st)
+{
+ /* reset the GPIOs */
+ dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos);
+
+ dib7000p_write_word(st, 1029, st->gpio_dir);
+ dib7000p_write_word(st, 1030, st->gpio_val);
+
+ /* TODO 1031 is P_gpio_od */
+
+ dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos);
+
+ dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div);
+ return 0;
+}
+
+static int dib7000p_demod_reset(struct dib7000p_state *state)
+{
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+
+ dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+ /* restart all parts */
+ dib7000p_write_word(state, 770, 0xffff);
+ dib7000p_write_word(state, 771, 0xffff);
+ dib7000p_write_word(state, 772, 0x001f);
+ dib7000p_write_word(state, 898, 0x0003);
+ /* except i2c, sdio, gpio - control interfaces */
+ dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) );
+
+ dib7000p_write_word(state, 770, 0);
+ dib7000p_write_word(state, 771, 0);
+ dib7000p_write_word(state, 772, 0);
+ dib7000p_write_word(state, 898, 0);
+ dib7000p_write_word(state, 1280, 0);
+
+ /* default */
+ dib7000p_reset_pll(state);
+
+ if (dib7000p_reset_gpio(state) != 0)
+ dprintk("-E- GPIO reset was not successful.\n");
+
+ if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ dprintk("-E- OUTPUT_MODE could not be resetted.\n");
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) );
+
+ dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static void dib7000p_restart_agc(struct dib7000p_state *state)
+{
+ // P_restart_iqc & P_restart_agc
+ dib7000p_write_word(state, 770, 0x0c00);
+ dib7000p_write_word(state, 770, 0x0000);
+}
+
+static void dib7000p_update_lna(struct dib7000p_state *state)
+{
+ int i;
+ u16 dyn_gain;
+
+ // when there is no LNA to program return immediatly
+ if (state->cfg.update_lna == NULL)
+ return;
+
+ for (i = 0; i < 5; i++) {
+ // read dyn_gain here (because it is demod-dependent and not tuner)
+ dyn_gain = dib7000p_read_word(state, 394);
+
+ if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+ dib7000p_restart_agc(state);
+ msleep(5);
+ } else
+ break;
+ }
+}
+
+static void dib7000p_pll_clk_cfg(struct dib7000p_state *state)
+{
+ u16 tmp = 0;
+ tmp = dib7000p_read_word(state, 903);
+ dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll
+ tmp = dib7000p_read_word(state, 900);
+ dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock
+}
+
+static void dib7000p_update_timf_freq(struct dib7000p_state *state)
+{
+ u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428);
+ state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+ dib7000p_write_word(state, 23, (u16) (timf >> 16));
+ dib7000p_write_word(state, 24, (u16) (timf & 0xffff));
+ dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000p_set_channel(struct dib7000p_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+ u16 tmp, est[4]; // reg_26, reg_32, reg_33, reg_187, reg_188, reg_189, reg_190, reg_207, reg_208;
+
+ /* nfft, guard, qam, alpha */
+ dib7000p_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+ dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */
+
+ /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+ tmp = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+ if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+ tmp |= (ch->vit_code_rate_hp << 1);
+ else
+ tmp |= (ch->vit_code_rate_lp << 1);
+ dib7000p_write_word(state, 208, tmp);
+
+ /* P_dvsy_sync_wait */
+ switch (ch->nfft) {
+ case 1: tmp = 256; break;
+ case 2: tmp = 128; break;
+ case 0:
+ default: tmp = 64; break;
+ }
+ tmp *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+ tmp <<= 4;
+
+ /* deactive the possibility of diversity reception if extended interleave */
+ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+ if (ch->intlv_native || ch->nfft == 1)
+ tmp |= (1 << 2) | (2 << 0);
+ dib7000p_write_word(state, 207, tmp);
+
+ dib7000p_write_word(state, 26, 0x6680); // timf(6xxx)
+ dib7000p_write_word(state, 29, 0x1273); // isi inh1273 on1073
+ dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3)
+ dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5)
+
+ /* channel estimation fine configuration */
+ switch (ch->nqam) {
+ case 2:
+ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
+ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
+ break;
+ case 1:
+ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
+ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
+ break;
+ default:
+ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
+ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
+ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
+ break;
+ }
+ for (tmp = 0; tmp < 4; tmp++)
+ dib7000p_write_word(state, 187 + tmp, est[tmp]);
+
+ // set power-up level: interf+analog+AGC
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ dib7000p_set_adc_state(state, DIBX000_ADC_ON);
+ dib7000p_pll_clk_cfg(state);
+ msleep(7);
+
+ // AGC initialization
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 1);
+
+ dib7000p_restart_agc(state);
+
+ // wait AGC rough lock time
+ msleep(5);
+
+ dib7000p_update_lna(state);
+
+ // wait AGC accurate lock time
+ msleep(7);
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 0);
+}
+
+static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ struct dibx000_ofdm_channel auto_ch;
+ u32 value;
+
+ INIT_OFDM_CHANNEL(&auto_ch);
+ auto_ch.RF_kHz = ch->RF_kHz;
+ auto_ch.Bw = ch->Bw;
+ auto_ch.nqam = 2;
+ auto_ch.guard = 0;
+ auto_ch.nfft = 1;
+ auto_ch.vit_alpha = 1;
+ auto_ch.vit_select_hp = 1;
+ auto_ch.vit_code_rate_hp = 2;
+ auto_ch.vit_code_rate_lp = 3;
+ auto_ch.vit_hrch = 0;
+ auto_ch.intlv_native = 1;
+
+ dib7000p_set_channel(state, &auto_ch, 7);
+
+ // always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+ value = 30 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+ dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time
+ value = 100 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+ dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time
+ value = 500 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+ dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time
+
+ value = dib7000p_read_word(state, 0);
+ dib7000p_write_word(state, 0, (1 << 9) | value);
+ dib7000p_read_word(state, 1284);
+ dib7000p_write_word(state, 0, (u16) value);
+
+ return 0;
+}
+
+static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u16 irq_pending = dib7000p_read_word(state, 1284);
+
+ if (irq_pending & 0x1) // failed
+ return 1;
+
+ if (irq_pending & 0x2) // succeeded
+ return 2;
+
+ return 0; // still pending
+}
+
+static int dib7000p_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u16 tmp = 0;
+
+ if (ch != NULL)
+ dib7000p_set_channel(state, ch, 0);
+ else
+ return -EINVAL;
+
+ // restart demod
+ dib7000p_write_word(state, 770, 0x4000);
+ dib7000p_write_word(state, 770, 0x0000);
+ msleep(45);
+
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ // never achieved a lock with that bandwidth so far - wait for osc-freq to update
+ if (state->timf == 0)
+ msleep(200);
+
+ /* offset loop parameters */
+
+ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+ tmp = (6 << 8) | 0x80;
+ switch (ch->nfft) {
+ case 0: tmp |= (7 << 12); break;
+ case 1: tmp |= (9 << 12); break;
+ case 2: tmp |= (8 << 12); break;
+ }
+ dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+ tmp = (0 << 4);
+ switch (ch->nfft) {
+ case 0: tmp |= 0x6; break;
+ case 1: tmp |= 0x8; break;
+ case 2: tmp |= 0x7; break;
+ }
+ dib7000p_write_word(state, 32, tmp);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+ tmp = (0 << 4);
+ switch (ch->nfft) {
+ case 0: tmp |= 0x6; break;
+ case 1: tmp |= 0x8; break;
+ case 2: tmp |= 0x7; break;
+ }
+ dib7000p_write_word(state, 33, tmp);
+
+ tmp = dib7000p_read_word(state,509);
+ if (!((tmp >> 6) & 0x1)) {
+ /* restart the fec */
+ tmp = dib7000p_read_word(state,771);
+ dib7000p_write_word(state, 771, tmp | (1 << 1));
+ dib7000p_write_word(state, 771, tmp);
+ msleep(10);
+ tmp = dib7000p_read_word(state,509);
+ }
+
+ // we achieved a lock - it's time to update the osc freq
+ if ((tmp >> 6) & 0x1)
+ dib7000p_update_timf_freq(state);
+
+ return 0;
+}
+
+static int dib7000p_init(struct dvb_frontend *demod)
+{
+ struct dibx000_agc_config *agc;
+ struct dib7000p_state *state = demod->demodulator_priv;
+ int ret = 0;
+
+ // Demodulator default configuration
+ agc = state->cfg.agc;
+
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+
+ /* AGC */
+ ret |= dib7000p_write_word(state, 75 , agc->setup );
+ ret |= dib7000p_write_word(state, 76 , agc->inv_gain );
+ ret |= dib7000p_write_word(state, 77 , agc->time_stabiliz );
+ ret |= dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock);
+
+ // Demod AGC loop configuration
+ ret |= dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp);
+ ret |= dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp);
+
+ /* AGC continued */
+ dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+ if (state->wbd_ref != 0)
+ ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref);
+ else
+ ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref);
+
+ ret |= dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+
+ ret |= dib7000p_write_word(state, 107, agc->agc1_max);
+ ret |= dib7000p_write_word(state, 108, agc->agc1_min);
+ ret |= dib7000p_write_word(state, 109, agc->agc2_max);
+ ret |= dib7000p_write_word(state, 110, agc->agc2_min);
+ ret |= dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+ ret |= dib7000p_write_word(state, 112, agc->agc1_pt3);
+ ret |= dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ ret |= dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ ret |= dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+ /* disable power smoothing */
+ ret |= dib7000p_write_word(state, 145, 0);
+ ret |= dib7000p_write_word(state, 146, 0);
+ ret |= dib7000p_write_word(state, 147, 0);
+ ret |= dib7000p_write_word(state, 148, 0);
+ ret |= dib7000p_write_word(state, 149, 0);
+ ret |= dib7000p_write_word(state, 150, 0);
+ ret |= dib7000p_write_word(state, 151, 0);
+ ret |= dib7000p_write_word(state, 152, 0);
+
+ // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26
+ ret |= dib7000p_write_word(state, 26 ,0x6680);
+
+ // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16
+ ret |= dib7000p_write_word(state, 142,0x0410);
+ // P_fft_freq_dir=1, P_fft_nb_to_cut=0
+ ret |= dib7000p_write_word(state, 154,1 << 13);
+ // P_pha3_thres, default 0x3000
+ ret |= dib7000p_write_word(state, 168,0x0ccd);
+ // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010
+ //ret |= dib7000p_write_word(state, 169,0x0010);
+ // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005
+ ret |= dib7000p_write_word(state, 183,0x200f);
+ // P_adp_regul_cnt=573, default: 410
+ ret |= dib7000p_write_word(state, 187,0x023d);
+ // P_adp_noise_cnt=
+ ret |= dib7000p_write_word(state, 188,0x00a4);
+ // P_adp_regul_ext
+ ret |= dib7000p_write_word(state, 189,0x00a4);
+ // P_adp_noise_ext
+ ret |= dib7000p_write_word(state, 190,0x7ff0);
+ // P_adp_fil
+ ret |= dib7000p_write_word(state, 191,0x3ccc);
+
+ ret |= dib7000p_write_word(state, 222,0x0010);
+ // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ ret |= dib7000p_write_word(state, 235,0x0062);
+
+ // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ...
+ if(state->cfg.tuner_is_baseband)
+ ret |= dib7000p_write_word(state, 36,0x0755);
+ else
+ ret |= dib7000p_write_word(state, 36,0x1f55);
+
+ // auto search configuration
+ ret |= dib7000p_write_word(state, 2 ,0x0004);
+ ret |= dib7000p_write_word(state, 3 ,0x1000);
+
+ /* Equal Lock */
+ ret |= dib7000p_write_word(state, 4 ,0x0814);
+
+ ret |= dib7000p_write_word(state, 6 ,0x001b);
+ ret |= dib7000p_write_word(state, 7 ,0x7740);
+ ret |= dib7000p_write_word(state, 8 ,0x005b);
+ ret |= dib7000p_write_word(state, 9 ,0x8d80);
+ ret |= dib7000p_write_word(state, 10 ,0x01c9);
+ ret |= dib7000p_write_word(state, 11 ,0xc380);
+ ret |= dib7000p_write_word(state, 12 ,0x0000);
+ ret |= dib7000p_write_word(state, 13 ,0x0080);
+ ret |= dib7000p_write_word(state, 14 ,0x0000);
+ ret |= dib7000p_write_word(state, 15 ,0x0090);
+ ret |= dib7000p_write_word(state, 16 ,0x0001);
+ ret |= dib7000p_write_word(state, 17 ,0xd4c0);
+
+ // P_clk_cfg1
+ ret |= dib7000p_write_word(state, 901, 0x0006);
+
+ // P_divclksel=3 P_divbitsel=1
+ ret |= dib7000p_write_word(state, 902, (3 << 10) | (1 << 6));
+
+ // Tuner IO bank: max drive (14mA) + divout pads max drive
+ ret |= dib7000p_write_word(state, 905, 0x2c8e);
+
+ ret |= dib7000p_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+ dib7000p_sad_calib(state);
+
+ return ret;
+}
+
+static int dib7000p_sleep(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+}
+
+static int dib7000p_identify(struct dib7000p_state *st)
+{
+ u16 value;
+ dprintk("-I- DiB7000PC: checking demod on I2C address: %d (%x)\n",
+ st->i2c_addr, st->i2c_addr);
+
+ if ((value = dib7000p_read_word(st, 768)) != 0x01b3) {
+ dprintk("-E- DiB7000PC: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ if ((value = dib7000p_read_word(st, 769)) != 0x4000) {
+ dprintk("-E- DiB7000PC: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+
+static int dib7000p_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 tps = dib7000p_read_word(state,463);
+
+ fep->inversion = INVERSION_AUTO;
+
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+ switch ((tps >> 8) & 0x3) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+ }
+
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+
+ switch ((tps >> 14) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
+
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+ }
+
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
+
+ /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */
+
+ return 0;
+}
+
+static int dib7000p_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
+
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib7000p_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, fep);
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 800, found;
+
+ dib7000p_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib7000p_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib7000p_get_frontend(fe, fep);
+ FEP2DIB(fep, &ch);
+ }
+
+ /* make this a config parameter */
+ dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib7000p_tune(fe, &ch);
+}
+
+static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 lock = dib7000p_read_word(state, 509);
+
+ *stat = 0;
+
+ if (lock & 0x8000)
+ *stat |= FE_HAS_SIGNAL;
+ if (lock & 0x3000)
+ *stat |= FE_HAS_CARRIER;
+ if (lock & 0x0100)
+ *stat |= FE_HAS_VITERBI;
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501);
+ return 0;
+}
+
+static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ *unc = dib7000p_read_word(state, 506);
+ return 0;
+}
+
+static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val = dib7000p_read_word(state, 394);
+ *strength = 65535 - val;
+ return 0;
+}
+
+static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ *snr = 0x0000;
+ return 0;
+}
+
+static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void dib7000p_release(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *st = demod->demodulator_priv;
+ dibx000_exit_i2c_master(&st->i2c_master);
+ kfree(st);
+}
+
+int dib7000pc_detection(struct i2c_adapter *i2c_adap)
+{
+ u8 tx[2], rx[2];
+ struct i2c_msg msg[2] = {
+ { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 },
+ { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 },
+ };
+
+ tx[0] = 0x03;
+ tx[1] = 0x00;
+
+ if (i2c_transfer(i2c_adap, msg, 2) == 2)
+ if (rx[0] == 0x01 && rx[1] == 0xb3) {
+ dprintk("-D- DiB7000PC detected\n");
+ return 1;
+ }
+
+ msg[0].addr = msg[1].addr = 0x40;
+
+ if (i2c_transfer(i2c_adap, msg, 2) == 2)
+ if (rx[0] == 0x01 && rx[1] == 0xb3) {
+ dprintk("-D- DiB7000PC detected\n");
+ return 1;
+ }
+
+ dprintk("-D- DiB7000PC not detected\n");
+ return 0;
+}
+EXPORT_SYMBOL(dib7000pc_detection);
+
+struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib7000p_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000p_get_i2c_master);
+
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
+{
+ struct dib7000p_state st = { .i2c_adap = i2c };
+ int k = 0;
+ u8 new_addr = 0;
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st.cfg = cfg[k];
+
+ /* designated i2c address */
+ new_addr = (0x40 + k) << 1;
+ st.i2c_addr = new_addr;
+ if (dib7000p_identify(&st) != 0) {
+ st.i2c_addr = default_addr;
+ if (dib7000p_identify(&st) != 0) {
+ dprintk("DiB7000P #%d: not identified\n", k);
+ return -EIO;
+ }
+ }
+
+ /* start diversity to pull_down div_str - just for i2c-enumeration */
+ dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+ /* set new i2c address and force divstart */
+ dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ st.cfg = cfg[k];
+ st.i2c_addr = (0x40 + k) << 1;
+
+ // unforce divstr
+ dib7000p_write_word(&st, 1285, st.i2c_addr << 2);
+
+ /* deactivate div - it was just for i2c-enumeration */
+ dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dib7000p_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000p_ops;
+struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
+{
+ struct dvb_frontend *demod;
+ struct dib7000p_state *st;
+ st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config));
+ st->i2c_adap = i2c_adap;
+ st->i2c_addr = i2c_addr;
+ st->gpio_val = cfg->gpio_val;
+ st->gpio_dir = cfg->gpio_dir;
+
+ demod = &st->demod;
+ demod->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+
+ if (dib7000p_identify(st) != 0)
+ goto error;
+
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
+
+ dib7000p_demod_reset(st);
+
+ return demod;
+
+error:
+ kfree(st);
+ return NULL;
+}
+EXPORT_SYMBOL(dib7000p_attach);
+
+static struct dvb_frontend_ops dib7000p_ops = {
+ .info = {
+ .name = "DiBcom 7000PC",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dib7000p_release,
+
+ .init = dib7000p_init,
+ .sleep = dib7000p_sleep,
+
+ .set_frontend = dib7000p_set_frontend,
+ .get_tune_settings = dib7000p_fe_get_tune_settings,
+ .get_frontend = dib7000p_get_frontend,
+
+ .read_status = dib7000p_read_status,
+ .read_ber = dib7000p_read_ber,
+ .read_signal_strength = dib7000p_read_signal_strength,
+ .read_snr = dib7000p_read_snr,
+ .read_ucblocks = dib7000p_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
new file mode 100644
index 0000000..79465cf
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -0,0 +1,46 @@
+#ifndef DIB7000P_H
+#define DIB7000P_H
+
+#include "dibx000_common.h"
+
+struct dib7000p_config {
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ struct dibx000_agc_config *agc;
+ struct dibx000_bandwidth_config *bw;
+
+#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ u16 pwm_freq_div;
+
+ u8 quartz_direct;
+
+ int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000P_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
+extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+
+/* TODO
+extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000p_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000p_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index bb0c65f..a1df604 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -32,6 +32,13 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
#define BAND_LBAND 0x01
#define BAND_UHF 0x02
#define BAND_VHF 0x04
+#define BAND_SBAND 0x08
+#define BAND_FM 0x10
+
+#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \
+ (freq_kHz) <= 250000 ? BAND_VHF : \
+ (freq_kHz) <= 863000 ? BAND_UHF : \
+ (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND )
struct dibx000_agc_config {
/* defines the capabilities of this AGC-setting - using the BAND_-defines*/
@@ -129,6 +136,7 @@ enum dibx000_adc_states {
/* I hope I can get rid of the following kludge in the near future */
struct dibx000_ofdm_channel {
+ u32 RF_kHz;
u8 Bw;
s16 nfft;
s16 guard;
@@ -138,9 +146,11 @@ struct dibx000_ofdm_channel {
s16 vit_alpha;
s16 vit_code_rate_hp;
s16 vit_code_rate_lp;
+ u8 intlv_native;
};
#define FEP2DIB(fep,ch) \
+ (ch)->RF_kHz = (fep)->frequency / 1000; \
(ch)->Bw = (fep)->u.ofdm.bandwidth; \
(ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \
(ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \
@@ -149,7 +159,8 @@ struct dibx000_ofdm_channel {
(ch)->vit_select_hp = 1; \
(ch)->vit_alpha = 1; \
(ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \
- (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP;
+ (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; \
+ (ch)->intlv_native = 1;
#define INIT_OFDM_CHANNEL(ch) do {\
(ch)->Bw = 0; \
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index b7e7108..62de760 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -472,14 +472,14 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
desc->name, div, buf[0], buf[1], buf[2], buf[3]);
- return 0;
+ // calculate the frequency we set it to
+ return (div * desc->entries[i].stepsize) - desc->entries[i].offset;
}
EXPORT_SYMBOL(dvb_pll_configure);
static int dvb_pll_release(struct dvb_frontend *fe)
{
- if (fe->tuner_priv)
- kfree(fe->tuner_priv);
+ kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
return 0;
}
@@ -489,7 +489,8 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
struct dvb_pll_priv *priv = fe->tuner_priv;
u8 buf[4];
struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
int i;
int result;
@@ -517,16 +518,16 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
return 0;
}
-static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int dvb_pll_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
u8 buf[4];
struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
int result;
- u32 div;
- int i;
- u32 bandwidth = 0;
+ u32 bandwidth = 0, frequency = 0;
if (priv->i2c == NULL)
return -EINVAL;
@@ -536,8 +537,11 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
bandwidth = params->u.ofdm.bandwidth;
}
- if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0)
+ if ((result = dvb_pll_configure(priv->pll_desc, buf,
+ params->frequency, bandwidth)) < 0)
return result;
+ else
+ frequency = result;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -545,26 +549,19 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
return result;
}
- // calculate the frequency we set it to
- for (i = 0; i < priv->pll_desc->count; i++) {
- if (params->frequency > priv->pll_desc->entries[i].limit)
- continue;
- break;
- }
- div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
- priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+ priv->frequency = frequency;
priv->bandwidth = bandwidth;
return 0;
}
-static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len)
+static int dvb_pll_calc_regs(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params,
+ u8 *buf, int buf_len)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
int result;
- u32 div;
- int i;
- u32 bandwidth = 0;
+ u32 bandwidth = 0, frequency = 0;
if (buf_len < 5)
return -EINVAL;
@@ -574,18 +571,15 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parame
bandwidth = params->u.ofdm.bandwidth;
}
- if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0)
+ if ((result = dvb_pll_configure(priv->pll_desc, buf+1,
+ params->frequency, bandwidth)) < 0)
return result;
+ else
+ frequency = result;
+
buf[0] = priv->pll_i2c_address;
- // calculate the frequency we set it to
- for (i = 0; i < priv->pll_desc->count; i++) {
- if (params->frequency > priv->pll_desc->entries[i].limit)
- continue;
- break;
- }
- div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
- priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+ priv->frequency = frequency;
priv->bandwidth = bandwidth;
return 5;
@@ -614,10 +608,13 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = {
.get_bandwidth = dvb_pll_get_bandwidth,
};
-struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc)
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
+ struct i2c_adapter *i2c,
+ struct dvb_pll_desc *desc)
{
u8 b1 [] = { 0 };
- struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 };
+ struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD,
+ .buf = b1, .len = 1 };
struct dvb_pll_priv *priv = NULL;
int ret;
@@ -640,7 +637,9 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struc
priv->i2c = i2c;
priv->pll_desc = desc;
- memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops));
+ memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
strncpy(fe->ops.tuner_ops.info.name, desc->name, 128);
fe->ops.tuner_ops.info.frequency_min = desc->min;
fe->ops.tuner_ops.info.frequency_min = desc->max;
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index ed5ac5a..681186a 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -48,7 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316;
extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
- u32 freq, int bandwidth);
+ u32 freq, int bandwidth);
/**
* Attach a dvb-pll to the supplied frontend structure.
@@ -59,6 +59,9 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
* @param desc dvb_pll_desc to use.
* @return Frontend pointer on success, NULL on failure
*/
-extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc);
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ struct dvb_pll_desc *desc);
#endif
diff --git a/drivers/media/dvb/frontends/lg_h06xf.h b/drivers/media/dvb/frontends/lg_h06xf.h
deleted file mode 100644
index 754d51d..0000000
--- a/drivers/media/dvb/frontends/lg_h06xf.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF
- *
- * 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 _LG_H06XF_H_
-#define _LG_H06XF_H_
-#include "dvb-pll.h"
-
-static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap,
- struct dvb_frontend_parameters* params)
-{
- u8 buf[4];
- struct i2c_msg msg = { .addr = 0x61, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
- int err;
-
- dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "lg_h06xf: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
-
- /* Set the Auxiliary Byte. */
- buf[0] = buf[2];
- buf[0] &= ~0x20;
- buf[0] |= 0x18;
- buf[1] = 0x50;
- msg.len = 2;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "lg_h06xf: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
-
- return 0;
-}
-#endif
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 9a35470..68aad0f 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -31,9 +31,6 @@
* Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
* pcHDTV HD5500
*
- * TODO:
- * signal strength always returns 0.
- *
*/
#include <linux/kernel.h>
@@ -46,9 +43,13 @@
#include <asm/byteorder.h>
#include "dvb_frontend.h"
+#include "dvb_math.h"
#include "lgdt330x_priv.h"
#include "lgdt330x.h"
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
@@ -68,6 +69,7 @@ struct lgdt330x_state
/* Demodulator private data */
fe_modulation_t current_modulation;
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
@@ -302,10 +304,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe,
static u8 lgdt3303_8vsb_44_data[] = {
0x04, 0x00,
0x0d, 0x40,
- 0x0e, 0x87,
- 0x0f, 0x8e,
- 0x10, 0x01,
- 0x47, 0x8b };
+ 0x0e, 0x87,
+ 0x0f, 0x8e,
+ 0x10, 0x01,
+ 0x47, 0x8b };
/*
* Array of byte pairs <address, value>
@@ -435,9 +437,6 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
/* Test signal does not exist flag */
/* as well as the AGC lock flag. */
*status |= FE_HAS_SIGNAL;
- } else {
- /* Without a signal all other status bits are meaningless */
- return 0;
}
/*
@@ -500,9 +499,6 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
/* Test input signal does not exist flag */
/* as well as the AGC lock flag. */
*status |= FE_HAS_SIGNAL;
- } else {
- /* Without a signal all other status bits are meaningless */
- return 0;
}
/* Carrier Recovery Lock Status Register */
@@ -543,151 +539,150 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+/* Calculate SNR estimation (scaled by 2^24)
+
+ 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+ equations from LGDT3303 datasheet. VSB is the same between the '02
+ and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
+ that has QAM information could verify?
+
+ For 8-VSB: (two ways, take your pick)
+ LGDT3302:
+ SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+ LGDT3303:
+ SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+ LGDT3302 & LGDT3303:
+ SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
+ For 64-QAM:
+ SNR = 10 * log10( 688128 / MSEQAM)
+ For 256-QAM:
+ SNR = 10 * log10( 696320 / MSEQAM)
+
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - intlog10(MSE))
+ Where for 256-QAM, c = log10(696320) * 2^24, and so on. */
+
+static u32 calculate_snr(u32 mse, u32 c)
{
- /* not directly available. */
- *strength = 0;
- return 0;
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
{
-#ifdef SNR_IN_DB
- /*
- * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
- * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
- * respectively. The following tables are built on these formulas.
- * The usual definition is SNR = 20 log10(signal/noise)
- * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
- *
- * This table is a an ordered list of noise values computed by the
- * formula from the spec sheet such that the index into the table
- * starting at 43 or 45 is the SNR value in db. There are duplicate noise
- * value entries at the beginning because the SNR varies more than
- * 1 db for a change of 1 digit in noise at very small values of noise.
- *
- * Examples from SNR_EQ table:
- * noise SNR
- * 0 43
- * 1 42
- * 2 39
- * 3 37
- * 4 36
- * 5 35
- * 6 34
- * 7 33
- * 8 33
- * 9 32
- * 10 32
- * 11 31
- * 12 31
- * 13 30
- */
-
- static const u32 SNR_EQ[] =
- { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7,
- 9, 11, 13, 17, 21, 26, 33, 41, 52, 65,
- 81, 102, 129, 162, 204, 257, 323, 406, 511, 644,
- 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433,
- 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323,
- 80978, 101945, 128341, 161571, 203406, 256073, 0x40000
- };
-
- static const u32 SNR_PH[] =
- { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8,
- 10, 12, 15, 19, 23, 29, 37, 46, 58, 73,
- 91, 115, 144, 182, 229, 288, 362, 456, 574, 722,
- 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216,
- 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151,
- 90833, 114351, 143960, 181235, 228161, 0x080000
- };
-
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- static u32 snr_db; /* index into SNR_EQ[] */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- /* read both equalizer and phase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Equalizer Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -15.61 to +41.58 */
noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
-
- /*
- * Look up noise value in table.
- * A better search algorithm could be used...
- * watch out there are duplicate entries.
- */
- for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
- if (noise < SNR_EQ[snr_db]) {
- *snr = 43 - snr_db;
- break;
- }
- }
- } else {
- /* Phase Tracker Mean-Square Error Register for QAM */
- noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
-
- /* Look up noise value in table. */
- for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
- if (noise < SNR_PH[snr_db]) {
- *snr = 45 - snr_db;
- break;
- }
- }
- }
+ c = 69765745; /* log10(25*24^2)*2^24 */
#else
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
-
- /* read both equalizer and pase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Phase Tracker Mean-Square Error Register for VSB */
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = ((buf[0] & 3) << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
-#endif
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
{
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- if (state->current_modulation == VSB_8) {
-
- i2c_read_demod_bytes(state, 0x6e, buf, 5);
- /* Phase Tracker Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -16.12 to +44.08 */
+ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = (buf[0] << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
+
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u16 snr;
+ int ret;
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h
index 59b7c5b..38c7669 100644
--- a/drivers/media/dvb/frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb/frontends/lgdt330x_priv.h
@@ -51,14 +51,19 @@ enum I2C_REG {
AGC_RFIF_ACC2= 0x3b,
AGC_STATUS= 0x3f,
SYNC_STATUS_VSB= 0x43,
- EQPH_ERR0= 0x47,
- EQ_ERR1= 0x48,
- EQ_ERR2= 0x49,
- PH_ERR1= 0x4a,
- PH_ERR2= 0x4b,
DEMUX_CONTROL= 0x66,
+ LGDT3302_EQPH_ERR0= 0x47,
+ LGDT3302_EQ_ERR1= 0x48,
+ LGDT3302_EQ_ERR2= 0x49,
+ LGDT3302_PH_ERR1= 0x4a,
+ LGDT3302_PH_ERR2= 0x4b,
LGDT3302_PACKET_ERR_COUNTER1= 0x6a,
LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
+ LGDT3303_EQPH_ERR0= 0x6e,
+ LGDT3303_EQ_ERR1= 0x6f,
+ LGDT3303_EQ_ERR2= 0x70,
+ LGDT3303_PH_ERR1= 0x71,
+ LGDT3303_PH_ERR2= 0x72,
LGDT3303_PACKET_ERR_COUNTER1= 0x8b,
LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
};
diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c
new file mode 100644
index 0000000..2202d0c
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.c
@@ -0,0 +1,134 @@
+/*
+ * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
+ *
+ * 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 "dvb-pll.h"
+#include "lgh06xf.h"
+
+#define LG_H06XF_PLL_I2C_ADDR 0x61
+
+struct lgh06xf_priv {
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int lgh06xf_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int lgh06xf_set_params(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params)
+{
+ struct lgh06xf_priv *priv = fe->tuner_priv;
+ u8 buf[4];
+ struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+ u32 frequency;
+ int result;
+
+ if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf,
+ params->frequency, 0)) < 0)
+ return result;
+ else
+ frequency = result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgh06xf: %s error "
+ "(addr %02x <- %02x, result = %i)\n",
+ __FUNCTION__, buf[0], buf[1], result);
+ if (result < 0)
+ return result;
+ else
+ return -EREMOTEIO;
+ }
+
+ /* Set the Auxiliary Byte. */
+ buf[0] = buf[2];
+ buf[0] &= ~0x20;
+ buf[0] |= 0x18;
+ buf[1] = 0x50;
+ msg.len = 2;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgh06xf: %s error "
+ "(addr %02x <- %02x, result = %i)\n",
+ __FUNCTION__, buf[0], buf[1], result);
+ if (result < 0)
+ return result;
+ else
+ return -EREMOTEIO;
+ }
+
+ priv->frequency = frequency;
+
+ return 0;
+}
+
+static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct lgh06xf_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops lgh06xf_tuner_ops = {
+ .release = lgh06xf_release,
+ .set_params = lgh06xf_set_params,
+ .get_frequency = lgh06xf_get_frequency,
+};
+
+struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ struct lgh06xf_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
+ sizeof(fe->ops.tuner_ops.info.name));
+
+ fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
+ fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+
+EXPORT_SYMBOL(lgh06xf_attach);
+
+MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
+MODULE_AUTHOR("Michael Krufky");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h
new file mode 100644
index 0000000..510b4be
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.h
@@ -0,0 +1,35 @@
+/*
+ * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF
+ *
+ * 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 _LGH06XF_H_
+#define _LGH06XF_H_
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE))
+extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_TUNER_LGH06XF */
+
+#endif /* _LGH06XF_H_ */
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index d20ab30..5a3a6e5 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
+#include "dvb_math.h"
#include "dvb_frontend.h"
#include "dvb-pll.h"
#include "or51132.h"
@@ -62,6 +63,7 @@ struct or51132_state
/* Demodulator private data */
fe_modulation_t current_modulation;
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
@@ -465,124 +467,128 @@ static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
- 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480,
- 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
- 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
- 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
- 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
- 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
- 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
- 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
- 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
- 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
- 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
- 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
- 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
- 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
- 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
- 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
- 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
- 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
- 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
- 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
+/* Calculate SNR estimation (scaled by 2^24)
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+ 8-VSB SNR and QAM equations from Oren datasheets
-static unsigned int i20Log10(unsigned short val)
-{
- unsigned int rntval = 100;
- unsigned int tmp = val;
- unsigned int exp = 1;
+ For 8-VSB:
+ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K
+
+ Where K = 0 if NTSC rejection filter is OFF; and
+ K = 3 if NTSC rejection filter is ON
+
+ For QAM64:
+ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 )
- while(tmp > 100) {tmp /= 100; exp++;}
+ For QAM256:
+ SNR[dB] = 10 * log10(907832426.314266 / MSE^2 )
- val = (2 * val)/denom[exp];
- if (exp > 1) rntval = 2000*exp;
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+ Where for QAM256, c = log10(907832426.314266) * 2^24
+ and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */
- rntval += i100x20log10[val];
- return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = 2*intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
-static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct or51132_state* state = fe->demodulator_priv;
- unsigned char rec_buf[2];
- unsigned char snd_buf[2];
- u8 rcvr_stat;
- u16 snr_equ;
- u32 signal_strength;
- int usK;
+ u8 rec_buf[2];
+ u8 snd_buf[2];
+ u32 noise;
+ u32 c;
+ u32 usK;
+ /* Register is same for VSB or QAM firmware */
snd_buf[0]=0x04;
snd_buf[1]=0x02; /* SNR after Equalizer */
msleep(30); /* 30ms */
if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_status write error\n");
- return -1;
+ printk(KERN_WARNING "or51132: snr write error\n");
+ return -EREMOTEIO;
}
msleep(30); /* 30ms */
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_status read error\n");
- return -1;
+ printk(KERN_WARNING "or51132: snr read error\n");
+ return -EREMOTEIO;
}
- snr_equ = rec_buf[0] | (rec_buf[1] << 8);
- dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+ noise = rec_buf[0] | (rec_buf[1] << 8);
+ dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
- /* Receiver Status */
+ /* Read status, contains modulation type for QAM_AUTO and
+ NTSC filter for VSB */
snd_buf[0]=0x04;
- snd_buf[1]=0x00;
+ snd_buf[1]=0x00; /* Status register */
msleep(30); /* 30ms */
if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n");
- return -1;
+ printk(KERN_WARNING "or51132: status write error\n");
+ return -EREMOTEIO;
}
msleep(30); /* 30ms */
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n");
- return -1;
+ printk(KERN_WARNING "or51132: status read error\n");
+ return -EREMOTEIO;
}
- dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]);
- rcvr_stat = rec_buf[1];
- usK = (rcvr_stat & 0x10) ? 3 : 0;
- /* The value reported back from the frontend will be FFFF=100% 0000=0% */
- signal_strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
- if (signal_strength > 0xffff)
- *strength = 0xffff;
- else
- *strength = signal_strength;
- dprintk("read_signal_strength %i\n",*strength);
+ usK = 0;
+ switch (rec_buf[0]) {
+ case 0x06:
+ usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+ /* Fall through to QAM64 case */
+ case 0x43:
+ c = 150204167;
+ break;
+ case 0x45:
+ c = 150290396;
+ break;
+ default:
+ printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+ return -EREMOTEIO;
+ }
+ dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
+ rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+
+ /* Calculate SNR using noise, c, and NTSC rejection correction */
+ state->snr = calculate_snr(noise, c) - usK;
+ *snr = (state->snr) >> 16;
+
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
-static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
- struct or51132_state* state = fe->demodulator_priv;
- unsigned char rec_buf[2];
- unsigned char snd_buf[2];
- u16 snr_equ;
-
- snd_buf[0]=0x04;
- snd_buf[1]=0x02; /* SNR after Equalizer */
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_snr write error\n");
- return -1;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_snr dvr read error\n");
- return -1;
- }
- snr_equ = rec_buf[0] | (rec_buf[1] << 8);
- dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ u16 snr;
+ int ret;
- *snr = 0xFFFF - snr_equ;
- dprintk("read_snr %i\n",*snr);
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 2bf124b..048d7cf 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -39,6 +39,7 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
+#include "dvb_math.h"
#include "dvb_frontend.h"
#include "or51211.h"
@@ -63,6 +64,7 @@ struct or51211_state {
/* Demodulator private data */
u8 initialized:1;
+ u32 snr; /* Result of last SNR claculation */
/* Tuner private data */
u32 current_frequency;
@@ -292,107 +294,81 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
- 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480,
- 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
- 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
- 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
- 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
- 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
- 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
- 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
- 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
- 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
- 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
- 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
- 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
- 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
- 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
- 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
- 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
- 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
- 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
- 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
-
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+/* Calculate SNR estimation (scaled by 2^24)
-static unsigned int i20Log10(unsigned short val)
-{
- unsigned int rntval = 100;
- unsigned int tmp = val;
- unsigned int exp = 1;
+ 8-VSB SNR equation from Oren datasheets
- while(tmp > 100) {tmp /= 100; exp++;}
+ For 8-VSB:
+ SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
- val = (2 * val)/denom[exp];
- if (exp > 1) rntval = 2000*exp;
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+ Where for 8-VSB, c = log10(219037.9454) * 2^24 */
- rntval += i100x20log10[val];
- return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = 2*intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
-static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct or51211_state* state = fe->demodulator_priv;
u8 rec_buf[2];
- u8 snd_buf[4];
- u8 snr_equ;
- u32 signal_strength;
+ u8 snd_buf[3];
/* SNR after Equalizer */
snd_buf[0] = 0x04;
snd_buf[1] = 0x00;
snd_buf[2] = 0x04;
- snd_buf[3] = 0x00;
if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
- printk(KERN_WARNING "or51211: read_status write error\n");
+ printk(KERN_WARNING "%s: error writing snr reg\n",
+ __FUNCTION__);
return -1;
}
- msleep(3);
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51211: read_status read error\n");
+ printk(KERN_WARNING "%s: read_status read error\n",
+ __FUNCTION__);
return -1;
}
- snr_equ = rec_buf[0] & 0xff;
- /* The value reported back from the frontend will be FFFF=100% 0000=0% */
- signal_strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
- if (signal_strength > 0xffff)
- *strength = 0xffff;
- else
- *strength = signal_strength;
- dprintk("read_signal_strength %i\n",*strength);
+ state->snr = calculate_snr(rec_buf[0], 89599047);
+ *snr = (state->snr) >> 16;
+
+ dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __FUNCTION__, rec_buf[0],
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
-static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
- struct or51211_state* state = fe->demodulator_priv;
- u8 rec_buf[2];
- u8 snd_buf[4];
-
- /* SNR after Equalizer */
- snd_buf[0] = 0x04;
- snd_buf[1] = 0x00;
- snd_buf[2] = 0x04;
- snd_buf[3] = 0x00;
-
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
- printk(KERN_WARNING "or51211: read_status write error\n");
- return -1;
- }
- msleep(3);
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51211: read_status read error\n");
- return -1;
- }
- *snr = rec_buf[0] & 0xff;
-
- dprintk("read_snr %i\n",*snr);
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
+ u16 snr;
+ int ret;
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 11e0dca..00e4bcd 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -648,18 +648,24 @@ static int tda10046_init(struct dvb_frontend* fe)
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
break;
- case TDA10046_AGC_TDA827X:
+ case TDA10046_AGC_TDA827X_GP11:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
break;
- case TDA10046_AGC_TDA827X_GPL:
+ case TDA10046_AGC_TDA827X_GP00:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
break;
+ case TDA10046_AGC_TDA827X_GP01:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
+ tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
+ tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
+ tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities
+ break;
}
tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index 605ad2d..ec502d7 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -35,8 +35,9 @@ enum tda10046_agc {
TDA10046_AGC_DEFAULT, /* original configuration */
TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */
TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */
- TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */
- TDA10046_AGC_TDA827X_GPL, /* same as above, but GPIOs 0 */
+ TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */
+ TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */
+ TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/
};
enum tda10046_if {
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
index 3aa45eb..67415c9 100644
--- a/drivers/media/dvb/frontends/tda8083.c
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -262,12 +262,29 @@ static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
if (sync & 0x10)
*status |= FE_HAS_SYNC;
+ if (sync & 0x20) /* frontend can not lock */
+ *status |= FE_TIMEDOUT;
+
if ((sync & 0x1f) == 0x1f)
*status |= FE_HAS_LOCK;
return 0;
}
+static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda8083_state* state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+
+ if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf))))
+ return ret;
+
+ *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
struct tda8083_state* state = fe->demodulator_priv;
@@ -288,6 +305,17 @@ static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
return 0;
}
+static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda8083_state* state = fe->demodulator_priv;
+
+ *ucblocks = tda8083_readreg(state, 0x0f);
+ if (*ucblocks == 0xff)
+ *ucblocks = 0xffffffff;
+
+ return 0;
+}
+
static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
struct tda8083_state* state = fe->demodulator_priv;
@@ -440,6 +468,8 @@ static struct dvb_frontend_ops tda8083_ops = {
.read_status = tda8083_read_status,
.read_signal_strength = tda8083_read_signal_strength,
.read_snr = tda8083_read_snr,
+ .read_ber = tda8083_read_ber,
+ .read_ucblocks = tda8083_read_ucblocks,
.diseqc_send_master_cmd = tda8083_send_diseqc_msg,
.diseqc_send_burst = tda8083_diseqc_send_burst,
diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c
index 34815b0..79f971d 100644
--- a/drivers/media/dvb/frontends/tda826x.c
+++ b/drivers/media/dvb/frontends/tda826x.c
@@ -42,8 +42,7 @@ struct tda826x_priv {
static int tda826x_release(struct dvb_frontend *fe)
{
- if (fe->tuner_priv)
- kfree(fe->tuner_priv);
+ kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
return 0;
}
@@ -133,18 +132,21 @@ struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2
{
struct tda826x_priv *priv = NULL;
u8 b1 [] = { 0, 0 };
- struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 };
+ struct i2c_msg msg[2] = {
+ { .addr = addr, .flags = 0, .buf = NULL, .len = 0 },
+ { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }
+ };
int ret;
dprintk("%s:\n", __FUNCTION__);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- ret = i2c_transfer (i2c, &msg, 1);
+ ret = i2c_transfer (i2c, msg, 2);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- if (ret != 1)
+ if (ret != 2)
return NULL;
if (!(b1[1] & 0x80))
return NULL;
diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c
index 8855439..6ba0029 100644
--- a/drivers/media/dvb/frontends/tua6100.c
+++ b/drivers/media/dvb/frontends/tua6100.c
@@ -43,8 +43,7 @@ struct tua6100_priv {
static int tua6100_release(struct dvb_frontend *fe)
{
- if (fe->tuner_priv)
- kfree(fe->tuner_priv);
+ kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
return 0;
}
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
index 8e4ce10..ffda71d 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/dvb/pluto2/pluto2.c
@@ -650,7 +650,7 @@ static int __devinit pluto2_probe(struct pci_dev *pdev,
/* dvb */
ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, THIS_MODULE, &pdev->dev);
if (ret < 0)
- goto err_i2c_bit_del_bus;
+ goto err_i2c_del_adapter;
dvb_adapter = &pluto->dvb_adapter;
@@ -712,8 +712,8 @@ err_dvb_dmx_release:
dvb_dmx_release(dvbdemux);
err_dvb_unregister_adapter:
dvb_unregister_adapter(dvb_adapter);
-err_i2c_bit_del_bus:
- i2c_bit_del_bus(&pluto->i2c_adap);
+err_i2c_del_adapter:
+ i2c_del_adapter(&pluto->i2c_adap);
err_pluto_hw_exit:
pluto_hw_exit(pluto);
err_free_irq:
@@ -748,7 +748,7 @@ static void __devexit pluto2_remove(struct pci_dev *pdev)
dvb_dmxdev_release(&pluto->dmxdev);
dvb_dmx_release(dvbdemux);
dvb_unregister_adapter(dvb_adapter);
- i2c_bit_del_bus(&pluto->i2c_adap);
+ i2c_del_adapter(&pluto->i2c_adap);
pluto_hw_exit(pluto);
free_irq(pdev->irq, pluto);
pci_iounmap(pdev, pluto->io_mem);
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 95531a6..eec7ccf 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -92,6 +92,7 @@ config DVB_BUDGET_CI
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select VIDEO_IR
help
Support for simple SAA7146 based DVB cards
(so called Budget- or Nova-PCI cards) without onboard
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index bba23bc..366c137 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -2828,7 +2828,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension av7110_extension = {
.name = "dvb",
- .flags = SAA7146_I2C_SHORT_DELAY,
+ .flags = SAA7146_USE_I2C_IRQ,
.module = THIS_MODULE,
.pci_tbl = &pci_tbl[0],
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
index d54bbcd..e4544ea 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -48,7 +48,8 @@ static void av7110_emit_keyup(unsigned long data)
if (!data || !test_bit(data, input_dev->key))
return;
- input_event(input_dev, EV_KEY, data, !!0);
+ input_report_key(input_dev, data, 0);
+ input_sync(input_dev);
}
@@ -115,14 +116,17 @@ static void av7110_emit_key(unsigned long parm)
del_timer(&keyup_timer);
if (keyup_timer.data != keycode || new_toggle != old_toggle) {
delay_timer_finished = 0;
- input_event(input_dev, EV_KEY, keyup_timer.data, !!0);
- input_event(input_dev, EV_KEY, keycode, !0);
- } else
- if (delay_timer_finished)
- input_event(input_dev, EV_KEY, keycode, 2);
+ input_event(input_dev, EV_KEY, keyup_timer.data, 0);
+ input_event(input_dev, EV_KEY, keycode, 1);
+ input_sync(input_dev);
+ } else if (delay_timer_finished) {
+ input_event(input_dev, EV_KEY, keycode, 2);
+ input_sync(input_dev);
+ }
} else {
delay_timer_finished = 0;
- input_event(input_dev, EV_KEY, keycode, !0);
+ input_event(input_dev, EV_KEY, keycode, 1);
+ input_sync(input_dev);
}
keyup_timer.expires = jiffies + UP_TIMEOUT;
@@ -211,6 +215,7 @@ static void ir_handler(struct av7110 *av7110, u32 ircom)
int __devinit av7110_ir_init(struct av7110 *av7110)
{
static struct proc_dir_entry *e;
+ int err;
if (av_cnt >= sizeof av_list/sizeof av_list[0])
return -ENOSPC;
@@ -231,7 +236,11 @@ int __devinit av7110_ir_init(struct av7110 *av7110)
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
input_register_keys();
- input_register_device(input_dev);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
input_dev->timer.function = input_repeat_key;
e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 2235ff8..89ab4b5 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -360,7 +360,7 @@ static int ciintf_init(struct budget_av *budget_av)
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
/* Enable DEBI pins */
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+ saa7146_write(saa, MC1, MASK_27 | MASK_11);
/* register CI interface */
budget_av->ca.owner = THIS_MODULE;
@@ -386,7 +386,7 @@ static int ciintf_init(struct budget_av *budget_av)
return 0;
error:
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ saa7146_write(saa, MC1, MASK_27);
return result;
}
@@ -403,7 +403,7 @@ static void ciintf_deinit(struct budget_av *budget_av)
dvb_ca_en50221_release(&budget_av->ca);
/* disable DEBI pins */
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ saa7146_write(saa, MC1, MASK_27);
}
@@ -655,6 +655,10 @@ static struct tda10021_config philips_cu1216_config = {
.demod_address = 0x0c,
};
+static struct tda10021_config philips_cu1216_config_altaddress = {
+ .demod_address = 0x0d,
+};
+
@@ -831,7 +835,7 @@ static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe,
return -EINVAL;
rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf,
- params->frequency, 0);
+ params->frequency, 0);
if(rc < 0) return rc;
if (fe->ops.i2c_gate_ctrl)
@@ -914,6 +918,7 @@ static u8 read_pwm(struct budget_av *budget_av)
#define SUBID_DVBS_TV_STAR_CI 0x0016
#define SUBID_DVBS_EASYWATCH_1 0x001a
#define SUBID_DVBS_EASYWATCH 0x001e
+#define SUBID_DVBC_EASYWATCH 0x002a
#define SUBID_DVBC_KNC1 0x0020
#define SUBID_DVBC_KNC1_PLUS 0x0021
#define SUBID_DVBC_CINERGY1200 0x1156
@@ -947,11 +952,15 @@ static void frontend_init(struct budget_av *budget_av)
/* Enable / PowerON Frontend */
saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+ /* Wait for PowerON */
+ msleep(100);
+
/* additional setup necessary for the PLUS cards */
switch (saa->pci->subsystem_device) {
case SUBID_DVBS_KNC1_PLUS:
case SUBID_DVBC_KNC1_PLUS:
case SUBID_DVBT_KNC1_PLUS:
+ case SUBID_DVBC_EASYWATCH:
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
break;
}
@@ -1006,10 +1015,15 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_KNC1:
case SUBID_DVBC_KNC1_PLUS:
case SUBID_DVBC_CINERGY1200:
+ case SUBID_DVBC_EASYWATCH:
budget_av->reinitialise_demod = 1;
fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
+ if (fe == NULL)
+ fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress,
+ &budget_av->budget.i2c_adap,
+ read_pwm(budget_av));
if (fe) {
budget_av->tda10021_poclkp = 1;
budget_av->tda10021_set_frontend = fe->ops.set_frontend;
@@ -1242,6 +1256,7 @@ MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
@@ -1260,6 +1275,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
+ MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
@@ -1277,7 +1293,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget_av",
- .flags = SAA7146_I2C_SHORT_DELAY,
+ .flags = SAA7146_USE_I2C_IRQ,
.pci_tbl = pci_tbl,
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index cd5ec48..f2066b4 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -37,6 +37,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/spinlock.h>
+#include <media/ir-common.h>
#include "dvb_ca_en50221.h"
#include "stv0299.h"
@@ -72,162 +73,218 @@
#define SLOTSTATUS_READY 8
#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+/* Milliseconds during which key presses are regarded as key repeat and during
+ * which the debounce logic is active
+ */
+#define IR_REPEAT_TIMEOUT 350
+
+/* RC5 device wildcard */
+#define IR_DEVICE_ANY 255
+
+/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
+ * this setting allows the superflous sequences to be ignored
+ */
+static int debounce = 0;
+module_param(debounce, int, 0644);
+MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
+
+static int rc5_device = -1;
+module_param(rc5_device, int, 0644);
+MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
+
+static int ir_debug = 0;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+struct budget_ci_ir {
+ struct input_dev *dev;
+ struct tasklet_struct msp430_irq_tasklet;
+ char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
+ char phys[32];
+ struct ir_input_state state;
+ int rc5_device;
+};
+
struct budget_ci {
struct budget budget;
- struct input_dev *input_dev;
- struct tasklet_struct msp430_irq_tasklet;
struct tasklet_struct ciintf_irq_tasklet;
int slot_status;
int ci_irq;
struct dvb_ca_en50221 ca;
- char ir_dev_name[50];
+ struct budget_ci_ir ir;
u8 tuner_pll_address; /* used for philips_tdm1316l configs */
};
-/* from reading the following remotes:
- Zenith Universal 7 / TV Mode 807 / VCR Mode 837
- Hauppauge (from NOVA-CI-s box product)
- i've taken a "middle of the road" approach and note the differences
-*/
-static u16 key_map[64] = {
- /* 0x0X */
- KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
- KEY_9,
- KEY_ENTER,
- KEY_RED,
- KEY_POWER, /* RADIO on Hauppauge */
- KEY_MUTE,
- 0,
- KEY_A, /* TV on Hauppauge */
- /* 0x1X */
- KEY_VOLUMEUP, KEY_VOLUMEDOWN,
- 0, 0,
- KEY_B,
- 0, 0, 0, 0, 0, 0, 0,
- KEY_UP, KEY_DOWN,
- KEY_OPTION, /* RESERVED on Hauppauge */
- KEY_BREAK,
- /* 0x2X */
- KEY_CHANNELUP, KEY_CHANNELDOWN,
- KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
- 0, KEY_RESTART, KEY_OK,
- KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
- 0,
- KEY_ENTER, /* VCR mode on Zenith */
- KEY_PAUSE,
- 0,
- KEY_RIGHT, KEY_LEFT,
- 0,
- KEY_MENU, /* FULL SCREEN on Hauppauge */
- 0,
- /* 0x3X */
- KEY_SLOW,
- KEY_PREVIOUS, /* VCR mode on Zenith */
- KEY_REWIND,
- 0,
- KEY_FASTFORWARD,
- KEY_PLAY, KEY_STOP,
- KEY_RECORD,
- KEY_TUNER, /* TV/VCR on Zenith */
- 0,
- KEY_C,
- 0,
- KEY_EXIT,
- KEY_POWER2,
- KEY_TUNER, /* VCR mode on Zenith */
- 0,
-};
-
-static void msp430_ir_debounce(unsigned long data)
+static void msp430_ir_keyup(unsigned long data)
{
- struct input_dev *dev = (struct input_dev *) data;
-
- if (dev->rep[0] == 0 || dev->rep[0] == ~0) {
- input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
- return;
- }
-
- dev->rep[0] = 0;
- dev->timer.expires = jiffies + HZ * 350 / 1000;
- add_timer(&dev->timer);
- input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */
+ struct budget_ci_ir *ir = (struct budget_ci_ir *) data;
+ ir_input_nokey(ir->dev, &ir->state);
}
static void msp430_ir_interrupt(unsigned long data)
{
struct budget_ci *budget_ci = (struct budget_ci *) data;
- struct input_dev *dev = budget_ci->input_dev;
- unsigned int code =
- ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+ struct input_dev *dev = budget_ci->ir.dev;
+ static int bounces = 0;
+ int device;
+ int toggle;
+ static int prev_toggle = -1;
+ static u32 ir_key;
+ u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+
+ /*
+ * The msp430 chip can generate two different bytes, command and device
+ *
+ * type1: X1CCCCCC, C = command bits (0 - 63)
+ * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
+ *
+ * More than one command byte may be generated before the device byte
+ * Only when we have both, a correct keypress is generated
+ */
+
+ /* Is this a RC5 command byte? */
+ if (command & 0x40) {
+ if (ir_debug)
+ printk("budget_ci: received command byte 0x%02x\n", command);
+ ir_key = command & 0x3f;
+ return;
+ }
- if (code & 0x40) {
- code &= 0x3f;
+ /* It's a RC5 device byte */
+ if (ir_debug)
+ printk("budget_ci: received device byte 0x%02x\n", command);
+ device = command & 0x1f;
+ toggle = command & 0x20;
- if (timer_pending(&dev->timer)) {
- if (code == dev->repeat_key) {
- ++dev->rep[0];
- return;
- }
- del_timer(&dev->timer);
- input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
- }
+ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device)
+ return;
- if (!key_map[code]) {
- printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code);
- return;
- }
+ /* Ignore repeated key sequences if requested */
+ if (toggle == prev_toggle && ir_key == dev->repeat_key &&
+ bounces > 0 && timer_pending(&dev->timer)) {
+ if (ir_debug)
+ printk("budget_ci: debounce logic ignored IR command\n");
+ bounces--;
+ return;
+ }
+ prev_toggle = toggle;
- /* initialize debounce and repeat */
- dev->repeat_key = code;
- /* Zenith remote _always_ sends 2 sequences */
- dev->rep[0] = ~0;
- /* 350 milliseconds */
- dev->timer.expires = jiffies + HZ * 350 / 1000;
- /* MAKE */
- input_event(dev, EV_KEY, key_map[code], !0);
- add_timer(&dev->timer);
+ /* Are we still waiting for a keyup event? */
+ if (del_timer(&dev->timer))
+ ir_input_nokey(dev, &budget_ci->ir.state);
+
+ /* Generate keypress */
+ if (ir_debug)
+ printk("budget_ci: generating keypress 0x%02x\n", ir_key);
+ ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8)));
+
+ /* Do we want to delay the keyup event? */
+ if (debounce) {
+ bounces = debounce;
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
+ } else {
+ ir_input_nokey(dev, &budget_ci->ir.state);
}
}
static int msp430_ir_init(struct budget_ci *budget_ci)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
- struct input_dev *input_dev;
- int i;
+ struct input_dev *input_dev = budget_ci->ir.dev;
+ int error;
+
+ budget_ci->ir.dev = input_dev = input_allocate_device();
+ if (!input_dev) {
+ printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
+ "Budget-CI dvb ir receiver %s", saa->name);
+ snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys),
+ "pci-%s/ir0", pci_name(saa->pci));
- budget_ci->input_dev = input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
+ input_dev->name = budget_ci->ir.name;
- sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
+ input_dev->phys = budget_ci->ir.phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->id.version = 1;
+ if (saa->pci->subsystem_vendor) {
+ input_dev->id.vendor = saa->pci->subsystem_vendor;
+ input_dev->id.product = saa->pci->subsystem_device;
+ } else {
+ input_dev->id.vendor = saa->pci->vendor;
+ input_dev->id.product = saa->pci->device;
+ }
+ input_dev->cdev.dev = &saa->pci->dev;
- input_dev->name = budget_ci->ir_dev_name;
+ /* Select keymap and address */
+ switch (budget_ci->budget.dev->pci->subsystem_device) {
+ case 0x100c:
+ case 0x100f:
+ case 0x1010:
+ case 0x1011:
+ case 0x1012:
+ case 0x1017:
+ /* The hauppauge keymap is a superset of these remotes */
+ ir_input_init(input_dev, &budget_ci->ir.state,
+ IR_TYPE_RC5, ir_codes_hauppauge_new);
+
+ if (rc5_device < 0)
+ budget_ci->ir.rc5_device = 0x1f;
+ else
+ budget_ci->ir.rc5_device = rc5_device;
+ break;
+ default:
+ /* unknown remote */
+ ir_input_init(input_dev, &budget_ci->ir.state,
+ IR_TYPE_RC5, ir_codes_budget_ci_old);
+
+ if (rc5_device < 0)
+ budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+ else
+ budget_ci->ir.rc5_device = rc5_device;
+ break;
+ }
- set_bit(EV_KEY, input_dev->evbit);
- for (i = 0; i < ARRAY_SIZE(key_map); i++)
- if (key_map[i])
- set_bit(key_map[i], input_dev->keybit);
+ /* initialise the key-up debounce timeout handler */
+ input_dev->timer.function = msp430_ir_keyup;
+ input_dev->timer.data = (unsigned long) &budget_ci->ir;
- input_register_device(budget_ci->input_dev);
+ error = input_register_device(input_dev);
+ if (error) {
+ printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+ goto out2;
+ }
- input_dev->timer.function = msp430_ir_debounce;
+ tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
+ (unsigned long) budget_ci);
- saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
+ SAA7146_IER_ENABLE(saa, MASK_06);
saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
return 0;
+
+out2:
+ input_free_device(input_dev);
+out1:
+ return error;
}
static void msp430_ir_deinit(struct budget_ci *budget_ci)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
- struct input_dev *dev = budget_ci->input_dev;
+ struct input_dev *dev = budget_ci->ir.dev;
- saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
+ SAA7146_IER_DISABLE(saa, MASK_06);
saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+ tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
- if (del_timer(&dev->timer))
- input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+ if (del_timer(&dev->timer)) {
+ ir_input_nokey(dev, &budget_ci->ir.state);
+ input_sync(dev);
+ }
input_unregister_device(dev);
}
@@ -428,7 +485,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
// enable DEBI pins
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+ saa7146_write(saa, MC1, MASK_27 | MASK_11);
// test if it is there
ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
@@ -480,7 +537,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
} else {
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
}
- saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+ SAA7146_IER_ENABLE(saa, MASK_03);
}
// enable interface
@@ -502,7 +559,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
return 0;
error:
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ saa7146_write(saa, MC1, MASK_27);
return result;
}
@@ -512,7 +569,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
// disable CI interrupts
if (budget_ci->ci_irq) {
- saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+ SAA7146_IER_DISABLE(saa, MASK_03);
saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
tasklet_kill(&budget_ci->ciintf_irq_tasklet);
}
@@ -530,7 +587,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
dvb_ca_en50221_release(&budget_ci->ca);
// disable DEBI pins
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ saa7146_write(saa, MC1, MASK_27);
}
static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
@@ -540,7 +597,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
if (*isr & MASK_06)
- tasklet_schedule(&budget_ci->msp430_irq_tasklet);
+ tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet);
if (*isr & MASK_10)
ttpci_budget_irq10_handler(dev, isr);
@@ -835,7 +892,7 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struc
band = 1;
} else if (tuner_frequency < 200000000) {
cp = 6;
- band = 1;
+ band = 2;
} else if (tuner_frequency < 290000000) {
cp = 3;
band = 2;
@@ -1083,24 +1140,23 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
struct budget_ci *budget_ci;
int err;
- if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL)))
- return -ENOMEM;
+ budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL);
+ if (!budget_ci) {
+ err = -ENOMEM;
+ goto out1;
+ }
dprintk(2, "budget_ci: %p\n", budget_ci);
- budget_ci->budget.ci_present = 0;
-
dev->ext_priv = budget_ci;
- if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) {
- kfree(budget_ci);
- return err;
- }
-
- tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
- (unsigned long) budget_ci);
+ err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+ if (err)
+ goto out2;
- msp430_ir_init(budget_ci);
+ err = msp430_ir_init(budget_ci);
+ if (err)
+ goto out3;
ciintf_init(budget_ci);
@@ -1110,6 +1166,13 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
ttpci_budget_init_hooks(&budget_ci->budget);
return 0;
+
+out3:
+ ttpci_budget_deinit(&budget_ci->budget);
+out2:
+ kfree(budget_ci);
+out1:
+ return err;
}
static int budget_ci_detach(struct saa7146_dev *dev)
@@ -1120,16 +1183,13 @@ static int budget_ci_detach(struct saa7146_dev *dev)
if (budget_ci->budget.ci_present)
ciintf_deinit(budget_ci);
+ msp430_ir_deinit(budget_ci);
if (budget_ci->budget.dvb_frontend) {
dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
}
err = ttpci_budget_deinit(&budget_ci->budget);
- tasklet_kill(&budget_ci->msp430_irq_tasklet);
-
- msp430_ir_deinit(budget_ci);
-
// disable frontend and CI interface
saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
@@ -1162,7 +1222,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget_ci dvb",
- .flags = SAA7146_I2C_SHORT_DELAY,
+ .flags = SAA7146_USE_I2C_IRQ,
.module = THIS_MODULE,
.pci_tbl = &pci_tbl[0],
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index 56f1c80..9268a82 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -555,7 +555,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget dvb",
- .flags = SAA7146_I2C_SHORT_DELAY,
+ .flags = SAA7146_USE_I2C_IRQ,
.module = THIS_MODULE,
.pci_tbl = pci_tbl,
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index 10b121a..bd6e7ba 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -238,6 +238,7 @@ static void ttusb_dec_handle_irq( struct urb *urb)
* for now lets report each signal as a key down and up*/
dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]);
input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1);
+ input_sync(dec->rc_input_dev);
input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0);
input_sync(dec->rc_input_dev);
}
@@ -1187,11 +1188,12 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
struct input_dev *input_dev;
u8 b[] = { 0x00, 0x01 };
int i;
+ int err;
usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
- dec->rc_input_dev = input_dev = input_allocate_device();
+ input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
@@ -1205,8 +1207,13 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
for (i = 0; i < ARRAY_SIZE(rc_keys); i++)
set_bit(rc_keys[i], input_dev->keybit);
- input_register_device(input_dev);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+ dec->rc_input_dev = input_dev;
if (usb_submit_urb(dec->irq_urb, GFP_KERNEL))
printk("%s: usb_submit_urb failed\n",__FUNCTION__);
/* enable irq pipe */
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
index 42f39a8..a6fb1d6 100644
--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
+++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
@@ -195,7 +195,7 @@ struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* conf
struct ttusbdecfe_state* state = NULL;
/* allocate memory for the internal state */
- state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+ state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
if (state == NULL)
return NULL;
@@ -215,7 +215,7 @@ struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* conf
struct ttusbdecfe_state* state = NULL;
/* allocate memory for the internal state */
- state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+ state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index b8fde5c..29a11c1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -184,6 +184,14 @@ config VIDEO_KS0127
To compile this driver as a module, choose M here: the
module will be called ks0127.
+config VIDEO_OV7670
+ tristate "OmniVision OV7670 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV7670 VGA camera. It currently only works with the M88ALP01
+ controller.
+
config VIDEO_SAA7110
tristate "Philips SAA7110 video decoder"
depends on VIDEO_V4L1 && I2C
@@ -567,18 +575,6 @@ config VIDEO_ZORAN_AVS6EYES
help
Support for the AverMedia 6 Eyes video surveillance card.
-config VIDEO_ZR36120
- tristate "Zoran ZR36120/36125 Video For Linux"
- depends on PCI && I2C && VIDEO_V4L1 && BROKEN
- help
- Support for ZR36120/ZR36125 based frame grabber/overlay boards.
- This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV,
- and Buster boards. Please read the material in
- <file:Documentation/video4linux/zr36120.txt> for more information.
-
- To compile this driver as a module, choose M here: the
- module will be called zr36120.
-
config VIDEO_MEYE
tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
depends on PCI && SONYPI && VIDEO_V4L1
@@ -670,6 +666,15 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
+config VIDEO_CAFE_CCIC
+ tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_OV7670
+ ---help---
+ This is a video4linux2 driver for the Marvell 88ALP01 integrated
+ CMOS camera controller. This is the controller found on first-
+ generation OLPC systems.
+
#
# USB Multimedia device configuration
#
@@ -681,6 +686,8 @@ source "drivers/media/video/pvrusb2/Kconfig"
source "drivers/media/video/em28xx/Kconfig"
+source "drivers/media/video/usbvision/Kconfig"
+
source "drivers/media/video/usbvideo/Kconfig"
source "drivers/media/video/et61x251/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index af57abc..9b1f3f0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -2,7 +2,6 @@
# Makefile for the video capture/playback device drivers.
#
-zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o
zr36067-objs := zoran_procfs.o zoran_device.o \
zoran_driver.o zoran_card.o
tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \
@@ -23,7 +22,6 @@ obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
-obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
@@ -64,6 +62,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
@@ -92,6 +91,9 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+
obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_SE401) += se401.o
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 6e1ddad..3c8e474 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -1793,7 +1793,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
memset(i,0,sizeof(*i));
i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
- i->audioset = 0;
+ i->audioset = 1;
if (i->index == bttv_tvcards[btv->c.type].tuner) {
sprintf(i->name, "Television");
i->type = V4L2_INPUT_TYPE_TUNER;
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 70de6c9..62b8730 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -479,11 +479,7 @@ int __devexit fini_bttv_i2c(struct bttv *btv)
if (0 != btv->i2c_rc)
return 0;
- if (btv->use_i2c_hw) {
- return i2c_del_adapter(&btv->c.i2c_adap);
- } else {
- return i2c_bit_del_bus(&btv->c.i2c_adap);
- }
+ return i2c_del_adapter(&btv->c.i2c_adap);
}
/*
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 933d6db..cbc012f 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -259,24 +259,59 @@ static void bttv_rc5_timer_keyup(unsigned long data)
/* ---------------------------------------------------------------------- */
+static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir)
+{
+ if (ir->polling) {
+ init_timer(&ir->timer);
+ ir->timer.function = bttv_input_timer;
+ ir->timer.data = (unsigned long)btv;
+ ir->timer.expires = jiffies + HZ;
+ add_timer(&ir->timer);
+ } else if (ir->rc5_gpio) {
+ /* set timer_end for code completion */
+ init_timer(&ir->timer_end);
+ ir->timer_end.function = bttv_rc5_timer_end;
+ ir->timer_end.data = (unsigned long)ir;
+
+ init_timer(&ir->timer_keyup);
+ ir->timer_keyup.function = bttv_rc5_timer_keyup;
+ ir->timer_keyup.data = (unsigned long)ir;
+ }
+}
+
+static void bttv_ir_stop(struct bttv *btv)
+{
+ if (btv->remote->polling) {
+ del_timer_sync(&btv->remote->timer);
+ flush_scheduled_work();
+ }
+
+ if (btv->remote->rc5_gpio) {
+ u32 gpio;
+
+ del_timer_sync(&btv->remote->timer_end);
+ flush_scheduled_work();
+
+ gpio = bttv_gpio_read(&btv->c);
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ }
+}
+
int bttv_input_init(struct bttv *btv)
{
struct bttv_ir *ir;
IR_KEYTAB_TYPE *ir_codes = NULL;
struct input_dev *input_dev;
int ir_type = IR_TYPE_OTHER;
+ int err = -ENOMEM;
if (!btv->has_remote)
return -ENODEV;
ir = kzalloc(sizeof(*ir),GFP_KERNEL);
input_dev = input_allocate_device();
- if (!ir || !input_dev) {
- kfree(ir);
- input_free_device(input_dev);
- return -ENOMEM;
- }
- memset(ir,0,sizeof(*ir));
+ if (!ir || !input_dev)
+ goto err_out_free;
/* detect & configure */
switch (btv->c.type) {
@@ -348,10 +383,9 @@ 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);
- kfree(ir);
- input_free_device(input_dev);
- return -ENODEV;
+ dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type);
+ err = -ENODEV;
+ goto err_out_free;
}
if (ir->rc5_gpio) {
@@ -389,32 +423,26 @@ int bttv_input_init(struct bttv *btv)
input_dev->cdev.dev = &btv->c.pci->dev;
btv->remote = ir;
- if (ir->polling) {
- init_timer(&ir->timer);
- ir->timer.function = bttv_input_timer;
- ir->timer.data = (unsigned long)btv;
- ir->timer.expires = jiffies + HZ;
- add_timer(&ir->timer);
- } else if (ir->rc5_gpio) {
- /* set timer_end for code completion */
- init_timer(&ir->timer_end);
- ir->timer_end.function = bttv_rc5_timer_end;
- ir->timer_end.data = (unsigned long)ir;
-
- init_timer(&ir->timer_keyup);
- ir->timer_keyup.function = bttv_rc5_timer_keyup;
- ir->timer_keyup.data = (unsigned long)ir;
- }
+ bttv_ir_start(btv, ir);
/* all done */
- input_register_device(btv->remote->dev);
- printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys);
+ err = input_register_device(btv->remote->dev);
+ if (err)
+ goto err_out_stop;
/* the remote isn't as bouncy as a keyboard */
ir->dev->rep[REP_DELAY] = repeat_delay;
ir->dev->rep[REP_PERIOD] = repeat_period;
return 0;
+
+ err_out_stop:
+ bttv_ir_stop(btv);
+ btv->remote = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
}
void bttv_input_fini(struct bttv *btv)
@@ -422,22 +450,7 @@ void bttv_input_fini(struct bttv *btv)
if (btv->remote == NULL)
return;
- if (btv->remote->polling) {
- del_timer_sync(&btv->remote->timer);
- flush_scheduled_work();
- }
-
-
- if (btv->remote->rc5_gpio) {
- u32 gpio;
-
- del_timer_sync(&btv->remote->timer_end);
- flush_scheduled_work();
-
- gpio = bttv_gpio_read(&btv->c);
- bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
- }
-
+ bttv_ir_stop(btv);
input_unregister_device(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h
new file mode 100644
index 0000000..b2c22a0
--- /dev/null
+++ b/drivers/media/video/cafe_ccic-regs.h
@@ -0,0 +1,160 @@
+/*
+ * Register definitions for the m88alp01 camera interface. Offsets in bytes
+ * as given in the spec.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#define REG_Y0BAR 0x00
+#define REG_Y1BAR 0x04
+#define REG_Y2BAR 0x08
+/* ... */
+
+#define REG_IMGPITCH 0x24 /* Image pitch register */
+#define IMGP_YP_SHFT 2 /* Y pitch params */
+#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */
+#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */
+#define IMGP_UVP_MASK 0x3ffc0000
+#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */
+#define IRQ_EOF0 0x00000001 /* End of frame 0 */
+#define IRQ_EOF1 0x00000002 /* End of frame 1 */
+#define IRQ_EOF2 0x00000004 /* End of frame 2 */
+#define IRQ_SOF0 0x00000008 /* Start of frame 0 */
+#define IRQ_SOF1 0x00000010 /* Start of frame 1 */
+#define IRQ_SOF2 0x00000020 /* Start of frame 2 */
+#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */
+#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */
+#define IRQ_TWSIR 0x00020000 /* TWSI read */
+#define IRQ_TWSIE 0x00040000 /* TWSI error */
+#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
+#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
+#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
+#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */
+#define REG_IRQSTAT 0x30 /* IRQ status / clear */
+
+#define REG_IMGSIZE 0x34 /* Image size */
+#define IMGSZ_V_MASK 0x1fff0000
+#define IMGSZ_V_SHIFT 16
+#define IMGSZ_H_MASK 0x00003fff
+#define REG_IMGOFFSET 0x38 /* IMage offset */
+
+#define REG_CTRL0 0x3c /* Control 0 */
+#define C0_ENABLE 0x00000001 /* Makes the whole thing go */
+
+/* Mask for all the format bits */
+#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */
+
+/* RGB ordering */
+#define C0_RGB4_RGBX 0x00000000
+#define C0_RGB4_XRGB 0x00000004
+#define C0_RGB4_BGRX 0x00000008
+#define C0_RGB4_XBGR 0x0000000c
+#define C0_RGB5_RGGB 0x00000000
+#define C0_RGB5_GRBG 0x00000004
+#define C0_RGB5_GBRG 0x00000008
+#define C0_RGB5_BGGR 0x0000000c
+
+/* Spec has two fields for DIN and DOUT, but they must match, so
+ combine them here. */
+#define C0_DF_YUV 0x00000000 /* Data is YUV */
+#define C0_DF_RGB 0x000000a0 /* ... RGB */
+#define C0_DF_BAYER 0x00000140 /* ... Bayer */
+/* 8-8-8 must be missing from the below - ask */
+#define C0_RGBF_565 0x00000000
+#define C0_RGBF_444 0x00000800
+#define C0_RGB_BGR 0x00001000 /* Blue comes first */
+#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */
+#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */
+#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */
+/* Think that 420 packed must be 111 - ask */
+#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */
+#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */
+#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */
+#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */
+#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */
+#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */
+#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
+#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
+/* Bayer bits 18,19 if needed */
+#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
+#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
+#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
+#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
+#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
+#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
+#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+
+
+#define REG_CTRL1 0x40 /* Control 1 */
+#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */
+#define C1_ALPHA_SHFT 20
+#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */
+#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */
+#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */
+#define C1_DMAB_MASK 0x06000000
+#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */
+#define C1_PWRDWN 0x10000000 /* Power down */
+
+#define REG_CLKCTRL 0x88 /* Clock control */
+#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */
+
+#define REG_GPR 0xb4 /* General purpose register. This
+ controls inputs to the power and reset
+ pins on the OV7670 used with OLPC;
+ other deployments could differ. */
+#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */
+#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */
+#define GPR_C1 0x00000002 /* Control 1 value */
+/*
+ * Control 0 is wired to reset on OLPC machines. For ov7x sensors,
+ * it is active low, for 0v6x, instead, it's active high. What
+ * fun.
+ */
+#define GPR_C0 0x00000001 /* Control 0 value */
+
+#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */
+#define TWSIC0_EN 0x00000001 /* TWSI enable */
+#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */
+#define TWSIC0_SID 0x000003fc /* Slave ID */
+#define TWSIC0_SID_SHIFT 2
+#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */
+#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */
+#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */
+
+#define REG_TWSIC1 0xbc /* TWSI control 1 */
+#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */
+#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */
+#define TWSIC1_ADDR_SHIFT 16
+#define TWSIC1_READ 0x01000000 /* Set for read op */
+#define TWSIC1_WSTAT 0x02000000 /* Write status */
+#define TWSIC1_RVALID 0x04000000 /* Read data valid */
+#define TWSIC1_ERROR 0x08000000 /* Something screwed up */
+
+
+#define REG_UBAR 0xc4 /* Upper base address register */
+
+/*
+ * Here's the weird global control registers which are said to live
+ * way up here.
+ */
+#define REG_GL_CSR 0x3004 /* Control/status register */
+#define GCSR_SRS 0x00000001 /* SW Reset set */
+#define GCSR_SRC 0x00000002 /* SW Reset clear */
+#define GCSR_MRS 0x00000004 /* Master reset set */
+#define GCSR_MRC 0x00000008 /* HW Reset clear */
+#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */
+#define REG_GL_IMASK 0x300c /* Interrupt mask register */
+#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */
+
+#define REG_LEN REG_GL_IMASK + 4
+
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
new file mode 100644
index 0000000..e347c7e
--- /dev/null
+++ b/drivers/media/video/cafe_ccic.c
@@ -0,0 +1,2228 @@
+/*
+ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
+ * multifunction chip. Currently works with the Omnivision OV7670
+ * sensor.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "cafe_ccic-regs.h"
+
+#define CAFE_VERSION 0x000001
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Video");
+
+/*
+ * Internal DMA buffer management. Since the controller cannot do S/G I/O,
+ * we must have physically contiguous buffers to bring frames into.
+ * These parameters control how many buffers we use, whether we
+ * allocate them at load time (better chance of success, but nails down
+ * memory) or when somebody tries to use the camera (riskier), and,
+ * for load-time allocation, how big they should be.
+ *
+ * The controller can cycle through three buffers. We could use
+ * more by flipping pointers around, but it probably makes little
+ * sense.
+ */
+
+#define MAX_DMA_BUFS 3
+static int alloc_bufs_at_load = 0;
+module_param(alloc_bufs_at_load, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_load,
+ "Non-zero value causes DMA buffers to be allocated at module "
+ "load time. This increases the chances of successfully getting "
+ "those buffers, but at the cost of nailing down the memory from "
+ "the outset.");
+
+static int n_dma_bufs = 3;
+module_param(n_dma_bufs, uint, 0644);
+MODULE_PARM_DESC(n_dma_bufs,
+ "The number of DMA buffers to allocate. Can be either two "
+ "(saves memory, makes timing tighter) or three.");
+
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */
+module_param(dma_buf_size, uint, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+ "The size of the allocated DMA buffers. If actual operating "
+ "parameters require larger buffers, an attempt to reallocate "
+ "will be made.");
+
+static int min_buffers = 1;
+module_param(min_buffers, uint, 0644);
+MODULE_PARM_DESC(min_buffers,
+ "The minimum number of streaming I/O buffers we are willing "
+ "to work with.");
+
+static int max_buffers = 10;
+module_param(max_buffers, uint, 0644);
+MODULE_PARM_DESC(max_buffers,
+ "The maximum number of streaming I/O buffers an application "
+ "will be allowed to allocate. These buffers are big and live "
+ "in vmalloc space.");
+
+static int flip = 0;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+ "If set, the sensor will be instructed to flip the image "
+ "vertically.");
+
+
+enum cafe_state {
+ S_NOTREADY, /* Not yet initialized */
+ S_IDLE, /* Just hanging around */
+ S_FLAKED, /* Some sort of problem */
+ S_SINGLEREAD, /* In read() */
+ S_SPECREAD, /* Speculative read (for future read()) */
+ S_STREAMING /* Streaming data */
+};
+
+/*
+ * Tracking of streaming I/O buffers.
+ */
+struct cafe_sio_buffer {
+ struct list_head list;
+ struct v4l2_buffer v4lbuf;
+ char *buffer; /* Where it lives in kernel space */
+ int mapcount;
+ struct cafe_camera *cam;
+};
+
+/*
+ * A description of one of our devices.
+ * Locking: controlled by s_mutex. Certain fields, however, require
+ * the dev_lock spinlock; they are marked as such by comments.
+ * dev_lock is also required for access to device registers.
+ */
+struct cafe_camera
+{
+ enum cafe_state state;
+ unsigned long flags; /* Buffer status, mainly (dev_lock) */
+ int users; /* How many open FDs */
+ struct file *owner; /* Who has data access (v4l2) */
+
+ /*
+ * Subsystem structures.
+ */
+ struct pci_dev *pdev;
+ struct video_device v4ldev;
+ struct i2c_adapter i2c_adapter;
+ struct i2c_client *sensor;
+
+ unsigned char __iomem *regs;
+ struct list_head dev_list; /* link to other devices */
+
+ /* DMA buffers */
+ unsigned int nbufs; /* How many are alloc'd */
+ int next_buf; /* Next to consume (dev_lock) */
+ unsigned int dma_buf_size; /* allocated size */
+ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */
+ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
+ unsigned int specframes; /* Unconsumed spec frames (dev_lock) */
+ unsigned int sequence; /* Frame sequence number */
+ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */
+
+ /* Streaming buffers */
+ unsigned int n_sbufs; /* How many we have */
+ struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */
+ struct list_head sb_avail; /* Available for data (we own) (dev_lock) */
+ struct list_head sb_full; /* With data (user space owns) (dev_lock) */
+ struct tasklet_struct s_tasklet;
+
+ /* Current operating parameters */
+ enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */
+ struct v4l2_pix_format pix_format;
+
+ /* Locks */
+ struct mutex s_mutex; /* Access to this structure */
+ spinlock_t dev_lock; /* Access to device */
+
+ /* Misc */
+ wait_queue_head_t smbus_wait; /* Waiting on i2c events */
+ wait_queue_head_t iowait; /* Waiting on frame data */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct dentry *dfs_regs;
+ struct dentry *dfs_cam_regs;
+#endif
+};
+
+/*
+ * Status flags. Always manipulated with bit operations.
+ */
+#define CF_BUF0_VALID 0 /* Buffers valid - first three */
+#define CF_BUF1_VALID 1
+#define CF_BUF2_VALID 2
+#define CF_DMA_ACTIVE 3 /* A frame is incoming */
+#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
+
+
+
+/*
+ * Start over with DMA buffers - dev_lock needed.
+ */
+static void cafe_reset_buffers(struct cafe_camera *cam)
+{
+ int i;
+
+ cam->next_buf = -1;
+ for (i = 0; i < cam->nbufs; i++)
+ clear_bit(i, &cam->flags);
+ cam->specframes = 0;
+}
+
+static inline int cafe_needs_config(struct cafe_camera *cam)
+{
+ return test_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+static void cafe_set_config_needed(struct cafe_camera *cam, int needed)
+{
+ if (needed)
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ else
+ clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+ dev_dbg(&(cam)->pdev->dev, fmt, ##arg);
+
+
+/* ---------------------------------------------------------------------*/
+/*
+ * We keep a simple list of known devices to search at open time.
+ */
+static LIST_HEAD(cafe_dev_list);
+static DEFINE_MUTEX(cafe_dev_list_lock);
+
+static void cafe_add_dev(struct cafe_camera *cam)
+{
+ mutex_lock(&cafe_dev_list_lock);
+ list_add_tail(&cam->dev_list, &cafe_dev_list);
+ mutex_unlock(&cafe_dev_list_lock);
+}
+
+static void cafe_remove_dev(struct cafe_camera *cam)
+{
+ mutex_lock(&cafe_dev_list_lock);
+ list_del(&cam->dev_list);
+ mutex_unlock(&cafe_dev_list_lock);
+}
+
+static struct cafe_camera *cafe_find_dev(int minor)
+{
+ struct cafe_camera *cam;
+
+ mutex_lock(&cafe_dev_list_lock);
+ list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+ if (cam->v4ldev.minor == minor)
+ goto done;
+ }
+ cam = NULL;
+ done:
+ mutex_unlock(&cafe_dev_list_lock);
+ return cam;
+}
+
+
+static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev)
+{
+ struct cafe_camera *cam;
+
+ mutex_lock(&cafe_dev_list_lock);
+ list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+ if (cam->pdev == pdev)
+ goto done;
+ }
+ cam = NULL;
+ done:
+ mutex_unlock(&cafe_dev_list_lock);
+ return cam;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Device register I/O
+ */
+static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg,
+ unsigned int val)
+{
+ iowrite32(val, cam->regs + reg);
+}
+
+static inline unsigned int cafe_reg_read(struct cafe_camera *cam,
+ unsigned int reg)
+{
+ return ioread32(cam->regs + reg);
+}
+
+
+static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg,
+ unsigned int val, unsigned int mask)
+{
+ unsigned int v = cafe_reg_read(cam, reg);
+
+ v = (v & ~mask) | (val & mask);
+ cafe_reg_write(cam, reg, v);
+}
+
+static inline void cafe_reg_clear_bit(struct cafe_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ cafe_reg_write_mask(cam, reg, 0, val);
+}
+
+static inline void cafe_reg_set_bit(struct cafe_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ cafe_reg_write_mask(cam, reg, val, val);
+}
+
+
+
+/* -------------------------------------------------------------------- */
+/*
+ * The I2C/SMBUS interface to the camera itself starts here. The
+ * controller handles SMBUS itself, presenting a relatively simple register
+ * interface; all we have to do is to tell it where to route the data.
+ */
+#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */
+
+static int cafe_smbus_write_done(struct cafe_camera *cam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ c1 = cafe_reg_read(cam, REG_TWSIC1);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
+}
+
+static int cafe_smbus_write_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 value)
+{
+ unsigned int rval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvell sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ cafe_reg_write(cam, REG_TWSIC0, rval);
+ (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
+ rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ cafe_reg_write(cam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ msleep(2); /* Required or things flake */
+
+ wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
+ CAFE_SMBUS_TIMEOUT);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ rval = cafe_reg_read(cam, REG_TWSIC1);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+ if (rval & TWSIC1_WSTAT) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
+ command, value);
+ return -EIO;
+ }
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
+ command, value);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+
+static int cafe_smbus_read_done(struct cafe_camera *cam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ c1 = cafe_reg_read(cam, REG_TWSIC1);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
+}
+
+
+
+static int cafe_smbus_read_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 *value)
+{
+ unsigned int rval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvel sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ cafe_reg_write(cam, REG_TWSIC0, rval);
+ (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
+ rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ cafe_reg_write(cam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+ wait_event_timeout(cam->smbus_wait,
+ cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ rval = cafe_reg_read(cam, REG_TWSIC1);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
+ return -EIO;
+ }
+ if (! (rval & TWSIC1_RVALID)) {
+ cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
+ command);
+ return -EIO;
+ }
+ *value = rval & 0xff;
+ return 0;
+}
+
+/*
+ * Perform a transfer over SMBUS. This thing is called under
+ * the i2c bus lock, so we shouldn't race with ourselves...
+ */
+static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char rw, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct cafe_camera *cam = i2c_get_adapdata(adapter);
+ int ret = -EINVAL;
+
+ /*
+ * Refuse to talk to anything but OV cam chips. We should
+ * never even see an attempt to do so, but one never knows.
+ */
+ if (cam->sensor && addr != cam->sensor->addr) {
+ cam_err(cam, "funky smbus addr %d\n", addr);
+ return -EINVAL;
+ }
+ /*
+ * This interface would appear to only do byte data ops. OK
+ * it can do word too, but the cam chip has no use for that.
+ */
+ if (size != I2C_SMBUS_BYTE_DATA) {
+ cam_err(cam, "funky xfer size %d\n", size);
+ return -EINVAL;
+ }
+
+ if (rw == I2C_SMBUS_WRITE)
+ ret = cafe_smbus_write_data(cam, addr, command, data->byte);
+ else if (rw == I2C_SMBUS_READ)
+ ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
+ return ret;
+}
+
+
+static void cafe_smbus_enable_irq(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+static u32 cafe_smbus_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+static struct i2c_algorithm cafe_smbus_algo = {
+ .smbus_xfer = cafe_smbus_xfer,
+ .functionality = cafe_smbus_func
+};
+
+/* Somebody is on the bus */
+static int cafe_cam_init(struct cafe_camera *cam);
+static void cafe_ctlr_stop_dma(struct cafe_camera *cam);
+static void cafe_ctlr_power_down(struct cafe_camera *cam);
+
+static int cafe_smbus_attach(struct i2c_client *client)
+{
+ struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+
+ /*
+ * Don't talk to chips we don't recognize.
+ */
+ if (client->driver->id == I2C_DRIVERID_OV7670) {
+ cam->sensor = client;
+ return cafe_cam_init(cam);
+ }
+ return -EINVAL;
+}
+
+static int cafe_smbus_detach(struct i2c_client *client)
+{
+ struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+
+ if (cam->sensor == client) {
+ cafe_ctlr_stop_dma(cam);
+ cafe_ctlr_power_down(cam);
+ cam_err(cam, "lost the sensor!\n");
+ cam->sensor = NULL; /* Bummer, no camera */
+ cam->state = S_NOTREADY;
+ }
+ return 0;
+}
+
+static int cafe_smbus_setup(struct cafe_camera *cam)
+{
+ struct i2c_adapter *adap = &cam->i2c_adapter;
+ int ret;
+
+ cafe_smbus_enable_irq(cam);
+ adap->id = I2C_HW_SMBUS_CAFE;
+ adap->class = I2C_CLASS_CAM_DIGITAL;
+ adap->owner = THIS_MODULE;
+ adap->client_register = cafe_smbus_attach;
+ adap->client_unregister = cafe_smbus_detach;
+ adap->algo = &cafe_smbus_algo;
+ strcpy(adap->name, "cafe_ccic");
+ i2c_set_adapdata(adap, cam);
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+ return ret;
+}
+
+static void cafe_smbus_shutdown(struct cafe_camera *cam)
+{
+ i2c_del_adapter(&cam->i2c_adapter);
+}
+
+
+/* ------------------------------------------------------------------- */
+/*
+ * Deal with the controller.
+ */
+
+/*
+ * Do everything we think we need to have the interface operating
+ * according to the desired format.
+ */
+static void cafe_ctlr_dma(struct cafe_camera *cam)
+{
+ /*
+ * Store the first two Y buffers (we aren't supporting
+ * planar formats for now, so no UV bufs). Then either
+ * set the third if it exists, or tell the controller
+ * to just use two.
+ */
+ cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
+ cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
+ if (cam->nbufs > 2) {
+ cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
+ cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ }
+ else
+ cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */
+}
+
+static void cafe_ctlr_image(struct cafe_camera *cam)
+{
+ int imgsz;
+ struct v4l2_pix_format *fmt = &cam->pix_format;
+
+ imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
+ (fmt->bytesperline & IMGSZ_H_MASK);
+ cafe_reg_write(cam, REG_IMGSIZE, imgsz);
+ cafe_reg_write(cam, REG_IMGOFFSET, 0);
+ /* YPITCH just drops the last two bits */
+ cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
+ IMGP_YP_MASK);
+ /*
+ * Tell the controller about the image format we are using.
+ */
+ switch (cam->pix_format.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ cafe_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
+ C0_DF_MASK);
+ break;
+
+ case V4L2_PIX_FMT_RGB444:
+ cafe_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+ C0_DF_MASK);
+ /* Alpha value? */
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ cafe_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+ C0_DF_MASK);
+ break;
+
+ default:
+ cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
+ break;
+ }
+ /*
+ * Make sure it knows we want to use hsync/vsync.
+ */
+ cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
+ C0_SIFM_MASK);
+}
+
+
+/*
+ * Configure the controller for operation; caller holds the
+ * device mutex.
+ */
+static int cafe_ctlr_configure(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_ctlr_dma(cam);
+ cafe_ctlr_image(cam);
+ cafe_set_config_needed(cam, 0);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+static void cafe_ctlr_irq_enable(struct cafe_camera *cam)
+{
+ /*
+ * Clear any pending interrupts, since we do not
+ * expect to have I/O active prior to enabling.
+ */
+ cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
+ cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+static void cafe_ctlr_irq_disable(struct cafe_camera *cam)
+{
+ cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+/*
+ * Make the controller start grabbing images. Everything must
+ * be set up before doing this.
+ */
+static void cafe_ctlr_start(struct cafe_camera *cam)
+{
+ /* set_bit performs a read, so no other barrier should be
+ needed here */
+ cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void cafe_ctlr_stop(struct cafe_camera *cam)
+{
+ cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void cafe_ctlr_init(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /*
+ * Added magic to bring up the hardware on the B-Test board
+ */
+ cafe_reg_write(cam, 0x3038, 0x8);
+ cafe_reg_write(cam, 0x315c, 0x80008);
+ /*
+ * Go through the dance needed to wake the device up.
+ * Note that these registers are global and shared
+ * with the NAND and SD devices. Interaction between the
+ * three still needs to be examined.
+ */
+ cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
+ cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
+ cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+ mdelay(5); /* FIXME revisit this */
+ cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
+ cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
+ /*
+ * Make sure it's not powered down.
+ */
+ cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ /*
+ * Turn off the enable bit. It sure should be off anyway,
+ * but it's good to be sure.
+ */
+ cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+ /*
+ * Mask all interrupts.
+ */
+ cafe_reg_write(cam, REG_IRQMASK, 0);
+ /*
+ * Clock the sensor appropriately. Controller clock should
+ * be 48MHz, sensor "typical" value is half that.
+ */
+ cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Stop the controller, and don't return until we're really sure that no
+ * further DMA is going on.
+ */
+static void cafe_ctlr_stop_dma(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ /*
+ * Theory: stop the camera controller (whether it is operating
+ * or not). Delay briefly just in case we race with the SOF
+ * interrupt, then wait until no DMA is active.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_ctlr_stop(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ mdelay(1);
+ wait_event_timeout(cam->iowait,
+ !test_bit(CF_DMA_ACTIVE, &cam->flags), HZ);
+ if (test_bit(CF_DMA_ACTIVE, &cam->flags))
+ cam_err(cam, "Timeout waiting for DMA to end\n");
+ /* This would be bad news - what now? */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cam->state = S_IDLE;
+ cafe_ctlr_irq_disable(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/*
+ * Power up and down.
+ */
+static void cafe_ctlr_power_up(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ /*
+ * Put the sensor into operational mode (assumes OLPC-style
+ * wiring). Control 0 is reset - set to 1 to operate.
+ * Control 1 is power down, set to 0 to operate.
+ */
+ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+ mdelay(1); /* Marvell says 1ms will do it */
+ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+ mdelay(1); /* Enough? */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+static void cafe_ctlr_power_down(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+ cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * Communications with the sensor.
+ */
+
+static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg)
+{
+ struct i2c_client *sc = cam->sensor;
+ int ret;
+
+ if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL)
+ return -EINVAL;
+ ret = sc->driver->command(sc, cmd, arg);
+ if (ret == -EPERM) /* Unsupported command */
+ return 0;
+ return ret;
+}
+
+static int __cafe_cam_reset(struct cafe_camera *cam)
+{
+ int zero = 0;
+ return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero);
+}
+
+/*
+ * We have found the sensor on the i2c. Let's try to have a
+ * conversation.
+ */
+static int cafe_cam_init(struct cafe_camera *cam)
+{
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->state != S_NOTREADY)
+ cam_warn(cam, "Cam init with device in funky state %d",
+ cam->state);
+ ret = __cafe_cam_reset(cam);
+ if (ret)
+ goto out;
+ ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
+ if (ret)
+ goto out;
+// if (cam->sensor->addr != OV7xx0_SID) {
+ if (cam->sensor_type != V4L2_IDENT_OV7670) {
+ cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
+ ret = -EINVAL;
+ goto out;
+ }
+/* Get/set parameters? */
+ ret = 0;
+ cam->state = S_IDLE;
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Configure the sensor to match the parameters we have. Caller should
+ * hold s_mutex
+ */
+static int cafe_cam_set_flip(struct cafe_camera *cam)
+{
+ struct v4l2_control ctrl;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = V4L2_CID_VFLIP;
+ ctrl.value = flip;
+ return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl);
+}
+
+
+static int cafe_cam_configure(struct cafe_camera *cam)
+{
+ struct v4l2_format fmt;
+ int ret, zero = 0;
+
+ if (cam->state != S_IDLE)
+ return -EINVAL;
+ fmt.fmt.pix = cam->pix_format;
+ ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero);
+ if (ret == 0)
+ ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
+ /*
+ * OV7670 does weird things if flip is set *before* format...
+ */
+ ret += cafe_cam_set_flip(cam);
+ return ret;
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * DMA buffer management. These functions need s_mutex held.
+ */
+
+/* FIXME: this is inefficient as hell, since dma_alloc_coherent just
+ * does a get_free_pages() call, and we waste a good chunk of an orderN
+ * allocation. Should try to allocate the whole set in one chunk.
+ */
+static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime)
+{
+ int i;
+
+ cafe_set_config_needed(cam, 1);
+ if (loadtime)
+ cam->dma_buf_size = dma_buf_size;
+ else
+ cam->dma_buf_size = cam->pix_format.sizeimage;
+ if (n_dma_bufs > 3)
+ n_dma_bufs = 3;
+
+ cam->nbufs = 0;
+ for (i = 0; i < n_dma_bufs; i++) {
+ cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev,
+ cam->dma_buf_size, cam->dma_handles + i,
+ GFP_KERNEL);
+ if (cam->dma_bufs[i] == NULL) {
+ cam_warn(cam, "Failed to allocate DMA buffer\n");
+ break;
+ }
+ /* For debug, remove eventually */
+ memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size);
+ (cam->nbufs)++;
+ }
+
+ switch (cam->nbufs) {
+ case 1:
+ dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
+ cam->dma_bufs[0], cam->dma_handles[0]);
+ cam->nbufs = 0;
+ case 0:
+ cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
+ return -ENOMEM;
+
+ case 2:
+ if (n_dma_bufs > 2)
+ cam_warn(cam, "Will limp along with only 2 buffers\n");
+ break;
+ }
+ return 0;
+}
+
+static void cafe_free_dma_bufs(struct cafe_camera *cam)
+{
+ int i;
+
+ for (i = 0; i < cam->nbufs; i++) {
+ dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
+ cam->dma_bufs[i], cam->dma_handles[i]);
+ cam->dma_bufs[i] = NULL;
+ }
+ cam->nbufs = 0;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Here starts the V4L2 interface code.
+ */
+
+/*
+ * Read an image from the device.
+ */
+static ssize_t cafe_deliver_buffer(struct cafe_camera *cam,
+ char __user *buffer, size_t len, loff_t *pos)
+{
+ int bufno;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ if (cam->next_buf < 0) {
+ cam_err(cam, "deliver_buffer: No next buffer\n");
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return -EIO;
+ }
+ bufno = cam->next_buf;
+ clear_bit(bufno, &cam->flags);
+ if (++(cam->next_buf) >= cam->nbufs)
+ cam->next_buf = 0;
+ if (! test_bit(cam->next_buf, &cam->flags))
+ cam->next_buf = -1;
+ cam->specframes = 0;
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+ if (len > cam->pix_format.sizeimage)
+ len = cam->pix_format.sizeimage;
+ if (copy_to_user(buffer, cam->dma_bufs[bufno], len))
+ return -EFAULT;
+ (*pos) += len;
+ return len;
+}
+
+/*
+ * Get everything ready, and start grabbing frames.
+ */
+static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state)
+{
+ int ret;
+ unsigned long flags;
+
+ /*
+ * Configuration. If we still don't have DMA buffers,
+ * make one last, desperate attempt.
+ */
+ if (cam->nbufs == 0)
+ if (cafe_alloc_dma_bufs(cam, 0))
+ return -ENOMEM;
+
+ if (cafe_needs_config(cam)) {
+ cafe_cam_configure(cam);
+ ret = cafe_ctlr_configure(cam);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Turn it loose.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cafe_reset_buffers(cam);
+ cafe_ctlr_irq_enable(cam);
+ cam->state = state;
+ cafe_ctlr_start(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+
+static ssize_t cafe_v4l_read(struct file *filp,
+ char __user *buffer, size_t len, loff_t *pos)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret;
+
+ /*
+ * Perhaps we're in speculative read mode and already
+ * have data?
+ */
+ mutex_lock(&cam->s_mutex);
+ if (cam->state == S_SPECREAD) {
+ if (cam->next_buf >= 0) {
+ ret = cafe_deliver_buffer(cam, buffer, len, pos);
+ if (ret != 0)
+ goto out_unlock;
+ }
+ } else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) {
+ ret = -EIO;
+ goto out_unlock;
+ } else if (cam->state != S_IDLE) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /*
+ * v4l2: multiple processes can open the device, but only
+ * one gets to grab data from it.
+ */
+ if (cam->owner && cam->owner != filp) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ cam->owner = filp;
+
+ /*
+ * Do setup if need be.
+ */
+ if (cam->state != S_SPECREAD) {
+ ret = cafe_read_setup(cam, S_SINGLEREAD);
+ if (ret)
+ goto out_unlock;
+ }
+ /*
+ * Wait for something to happen. This should probably
+ * be interruptible (FIXME).
+ */
+ wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ);
+ if (cam->next_buf < 0) {
+ cam_err(cam, "read() operation timed out\n");
+ cafe_ctlr_stop_dma(cam);
+ ret = -EIO;
+ goto out_unlock;
+ }
+ /*
+ * Give them their data and we should be done.
+ */
+ ret = cafe_deliver_buffer(cam, buffer, len, pos);
+
+ out_unlock:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+
+
+
+
+
+/*
+ * Streaming I/O support.
+ */
+
+
+
+static int cafe_vidioc_streamon(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret = -EINVAL;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ goto out;
+ mutex_lock(&cam->s_mutex);
+ if (cam->state != S_IDLE || cam->n_sbufs == 0)
+ goto out_unlock;
+
+ cam->sequence = 0;
+ ret = cafe_read_setup(cam, S_STREAMING);
+
+ out_unlock:
+ mutex_unlock(&cam->s_mutex);
+ out:
+ return ret;
+}
+
+
+static int cafe_vidioc_streamoff(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret = -EINVAL;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ goto out;
+ mutex_lock(&cam->s_mutex);
+ if (cam->state != S_STREAMING)
+ goto out_unlock;
+
+ cafe_ctlr_stop_dma(cam);
+ ret = 0;
+
+ out_unlock:
+ mutex_unlock(&cam->s_mutex);
+ out:
+ return ret;
+}
+
+
+
+static int cafe_setup_siobuf(struct cafe_camera *cam, int index)
+{
+ struct cafe_sio_buffer *buf = cam->sb_bufs + index;
+
+ INIT_LIST_HEAD(&buf->list);
+ buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage);
+ buf->buffer = vmalloc_user(buf->v4lbuf.length);
+ if (buf->buffer == NULL)
+ return -ENOMEM;
+ buf->mapcount = 0;
+ buf->cam = cam;
+
+ buf->v4lbuf.index = index;
+ buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->v4lbuf.field = V4L2_FIELD_NONE;
+ buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
+ /*
+ * Offset: must be 32-bit even on a 64-bit system. video-buf
+ * just uses the length times the index, but the spec warns
+ * against doing just that - vma merging problems. So we
+ * leave a gap between each pair of buffers.
+ */
+ buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
+ return 0;
+}
+
+static int cafe_free_sio_buffers(struct cafe_camera *cam)
+{
+ int i;
+
+ /*
+ * If any buffers are mapped, we cannot free them at all.
+ */
+ for (i = 0; i < cam->n_sbufs; i++)
+ if (cam->sb_bufs[i].mapcount > 0)
+ return -EBUSY;
+ /*
+ * OK, let's do it.
+ */
+ for (i = 0; i < cam->n_sbufs; i++)
+ vfree(cam->sb_bufs[i].buffer);
+ cam->n_sbufs = 0;
+ kfree(cam->sb_bufs);
+ cam->sb_bufs = NULL;
+ INIT_LIST_HEAD(&cam->sb_avail);
+ INIT_LIST_HEAD(&cam->sb_full);
+ return 0;
+}
+
+
+
+static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret;
+
+ /*
+ * Make sure it's something we can do. User pointers could be
+ * implemented without great pain, but that's not been done yet.
+ */
+ if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ /*
+ * If they ask for zero buffers, they really want us to stop streaming
+ * (if it's happening) and free everything. Should we check owner?
+ */
+ mutex_lock(&cam->s_mutex);
+ if (req->count == 0) {
+ if (cam->state == S_STREAMING)
+ cafe_ctlr_stop_dma(cam);
+ ret = cafe_free_sio_buffers (cam);
+ goto out;
+ }
+ /*
+ * Device needs to be idle and working. We *could* try to do the
+ * right thing in S_SPECREAD by shutting things down, but it
+ * probably doesn't matter.
+ */
+ if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ cam->owner = filp;
+
+ if (req->count < min_buffers)
+ req->count = min_buffers;
+ else if (req->count > max_buffers)
+ req->count = max_buffers;
+ if (cam->n_sbufs > 0) {
+ ret = cafe_free_sio_buffers(cam);
+ if (ret)
+ goto out;
+ }
+
+ cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer),
+ GFP_KERNEL);
+ if (cam->sb_bufs == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) {
+ ret = cafe_setup_siobuf(cam, cam->n_sbufs);
+ if (ret)
+ break;
+ }
+
+ if (cam->n_sbufs == 0) /* no luck at all - ret already set */
+ kfree(cam->sb_bufs);
+ else
+ ret = 0;
+ req->count = cam->n_sbufs; /* In case of partial success */
+
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int cafe_vidioc_querybuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret = -EINVAL;
+
+ mutex_lock(&cam->s_mutex);
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ goto out;
+ if (buf->index < 0 || buf->index >= cam->n_sbufs)
+ goto out;
+ *buf = cam->sb_bufs[buf->index].v4lbuf;
+ ret = 0;
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int cafe_vidioc_qbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct cafe_camera *cam = filp->private_data;
+ struct cafe_sio_buffer *sbuf;
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ mutex_lock(&cam->s_mutex);
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ goto out;
+ if (buf->index < 0 || buf->index >= cam->n_sbufs)
+ goto out;
+ sbuf = cam->sb_bufs + buf->index;
+ if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
+ ret = 0; /* Already queued?? */
+ goto out;
+ }
+ if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) {
+ /* Spec doesn't say anything, seems appropriate tho */
+ ret = -EBUSY;
+ goto out;
+ }
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ list_add(&sbuf->list, &cam->sb_avail);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ ret = 0;
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int cafe_vidioc_dqbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct cafe_camera *cam = filp->private_data;
+ struct cafe_sio_buffer *sbuf;
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ mutex_lock(&cam->s_mutex);
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ goto out_unlock;
+ if (cam->state != S_STREAMING)
+ goto out_unlock;
+ if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) {
+ mutex_unlock(&cam->s_mutex);
+ if (wait_event_interruptible(cam->iowait,
+ !list_empty(&cam->sb_full))) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ mutex_lock(&cam->s_mutex);
+ }
+
+ if (cam->state != S_STREAMING)
+ ret = -EINTR;
+ else {
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /* Should probably recheck !list_empty() here */
+ sbuf = list_entry(cam->sb_full.next,
+ struct cafe_sio_buffer, list);
+ list_del_init(&sbuf->list);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
+ *buf = sbuf->v4lbuf;
+ ret = 0;
+ }
+
+ out_unlock:
+ mutex_unlock(&cam->s_mutex);
+ out:
+ return ret;
+}
+
+
+
+static void cafe_v4l_vm_open(struct vm_area_struct *vma)
+{
+ struct cafe_sio_buffer *sbuf = vma->vm_private_data;
+ /*
+ * Locking: done under mmap_sem, so we don't need to
+ * go back to the camera lock here.
+ */
+ sbuf->mapcount++;
+}
+
+
+static void cafe_v4l_vm_close(struct vm_area_struct *vma)
+{
+ struct cafe_sio_buffer *sbuf = vma->vm_private_data;
+
+ mutex_lock(&sbuf->cam->s_mutex);
+ sbuf->mapcount--;
+ /* Docs say we should stop I/O too... */
+ if (sbuf->mapcount == 0)
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+ mutex_unlock(&sbuf->cam->s_mutex);
+}
+
+static struct vm_operations_struct cafe_v4l_vm_ops = {
+ .open = cafe_v4l_vm_open,
+ .close = cafe_v4l_vm_close
+};
+
+
+static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct cafe_camera *cam = filp->private_data;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret = -EINVAL;
+ int i;
+ struct cafe_sio_buffer *sbuf = NULL;
+
+ if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+ /*
+ * Find the buffer they are looking for.
+ */
+ mutex_lock(&cam->s_mutex);
+ for (i = 0; i < cam->n_sbufs; i++)
+ if (cam->sb_bufs[i].v4lbuf.m.offset == offset) {
+ sbuf = cam->sb_bufs + i;
+ break;
+ }
+ if (sbuf == NULL)
+ goto out;
+
+ ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
+ if (ret)
+ goto out;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = sbuf;
+ vma->vm_ops = &cafe_v4l_vm_ops;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
+ cafe_v4l_vm_open(vma);
+ ret = 0;
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static int cafe_v4l_open(struct inode *inode, struct file *filp)
+{
+ struct cafe_camera *cam;
+
+ cam = cafe_find_dev(iminor(inode));
+ if (cam == NULL)
+ return -ENODEV;
+ filp->private_data = cam;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->users == 0) {
+ cafe_ctlr_power_up(cam);
+ __cafe_cam_reset(cam);
+ cafe_set_config_needed(cam, 1);
+ /* FIXME make sure this is complete */
+ }
+ (cam->users)++;
+ mutex_unlock(&cam->s_mutex);
+ return 0;
+}
+
+
+static int cafe_v4l_release(struct inode *inode, struct file *filp)
+{
+ struct cafe_camera *cam = filp->private_data;
+
+ mutex_lock(&cam->s_mutex);
+ (cam->users)--;
+ if (filp == cam->owner) {
+ cafe_ctlr_stop_dma(cam);
+ cafe_free_sio_buffers(cam);
+ cam->owner = NULL;
+ }
+ if (cam->users == 0) {
+ cafe_ctlr_power_down(cam);
+ if (! alloc_bufs_at_load)
+ cafe_free_dma_bufs(cam);
+ }
+ mutex_unlock(&cam->s_mutex);
+ return 0;
+}
+
+
+
+static unsigned int cafe_v4l_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct cafe_camera *cam = filp->private_data;
+
+ poll_wait(filp, &cam->iowait, pt);
+ if (cam->next_buf >= 0)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+
+
+static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int cafe_vidioc_s_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct cafe_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+
+
+static int cafe_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "cafe_ccic");
+ strcpy(cap->card, "cafe_ccic");
+ cap->version = CAFE_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+
+/*
+ * The default format we use until somebody says otherwise.
+ */
+static struct v4l2_pix_format cafe_def_pix_format = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .bytesperline = VGA_WIDTH*2,
+ .sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
+};
+
+static int cafe_vidioc_enum_fmt_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmt)
+{
+ struct cafe_camera *cam = priv;
+ int ret;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct cafe_camera *cam = priv;
+ int ret;
+
+ /*
+ * Can't do anything if the device is not idle
+ * Also can't if there are streaming buffers in place.
+ */
+ if (cam->state != S_IDLE || cam->n_sbufs > 0)
+ return -EBUSY;
+ /*
+ * See if the formatting works in principle.
+ */
+ ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt);
+ if (ret)
+ return ret;
+ /*
+ * Now we start to change things for real, so let's do it
+ * under lock.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->pix_format = fmt->fmt.pix;
+ /*
+ * Make sure we have appropriate DMA buffers.
+ */
+ ret = -ENOMEM;
+ if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
+ cafe_free_dma_bufs(cam);
+ if (cam->nbufs == 0) {
+ if (cafe_alloc_dma_bufs(cam, 0))
+ goto out;
+ }
+ /*
+ * It looks like this might work, so let's program the sensor.
+ */
+ ret = cafe_cam_configure(cam);
+ if (! ret)
+ ret = cafe_ctlr_configure(cam);
+ out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Return our stored notion of how the camera is/should be configured.
+ * The V4l2 spec wants us to be smarter, and actually get this from
+ * the camera (and not mess with it at open time). Someday.
+ */
+static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct cafe_camera *cam = priv;
+
+ f->fmt.pix = cam->pix_format;
+ return 0;
+}
+
+/*
+ * We only have one input - the sensor - so minimize the nonsense here.
+ */
+static int cafe_vidioc_enum_input(struct file *filp, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL; /* Not sure what should go here */
+ strcpy(input->name, "Camera");
+ return 0;
+}
+
+static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* from vivi.c */
+static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ return 0;
+}
+
+/*
+ * G/S_PARM. Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+
+static void cafe_v4l_dev_release(struct video_device *vd)
+{
+ struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev);
+
+ kfree(cam);
+}
+
+
+/*
+ * This template device holds all of those v4l2 methods; we
+ * clone it for specific real devices.
+ */
+
+static struct file_operations cafe_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = cafe_v4l_open,
+ .release = cafe_v4l_release,
+ .read = cafe_v4l_read,
+ .poll = cafe_v4l_poll,
+ .mmap = cafe_v4l_mmap,
+ .ioctl = video_ioctl2,
+ .llseek = no_llseek,
+};
+
+static struct video_device cafe_v4l_template = {
+ .name = "cafe",
+ .type = VFL_TYPE_GRABBER,
+ .type2 = VID_TYPE_CAPTURE,
+ .minor = -1, /* Get one dynamically */
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
+
+ .fops = &cafe_v4l_fops,
+ .release = cafe_v4l_dev_release,
+
+ .vidioc_querycap = cafe_vidioc_querycap,
+ .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap,
+ .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap,
+ .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap,
+ .vidioc_enum_input = cafe_vidioc_enum_input,
+ .vidioc_g_input = cafe_vidioc_g_input,
+ .vidioc_s_input = cafe_vidioc_s_input,
+ .vidioc_s_std = cafe_vidioc_s_std,
+ .vidioc_reqbufs = cafe_vidioc_reqbufs,
+ .vidioc_querybuf = cafe_vidioc_querybuf,
+ .vidioc_qbuf = cafe_vidioc_qbuf,
+ .vidioc_dqbuf = cafe_vidioc_dqbuf,
+ .vidioc_streamon = cafe_vidioc_streamon,
+ .vidioc_streamoff = cafe_vidioc_streamoff,
+ .vidioc_queryctrl = cafe_vidioc_queryctrl,
+ .vidioc_g_ctrl = cafe_vidioc_g_ctrl,
+ .vidioc_s_ctrl = cafe_vidioc_s_ctrl,
+ .vidioc_g_parm = cafe_vidioc_g_parm,
+ .vidioc_s_parm = cafe_vidioc_s_parm,
+};
+
+
+
+
+
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+
+
+
+static void cafe_frame_tasklet(unsigned long data)
+{
+ struct cafe_camera *cam = (struct cafe_camera *) data;
+ int i;
+ unsigned long flags;
+ struct cafe_sio_buffer *sbuf;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ for (i = 0; i < cam->nbufs; i++) {
+ int bufno = cam->next_buf;
+ if (bufno < 0) { /* "will never happen" */
+ cam_err(cam, "No valid bufs in tasklet!\n");
+ break;
+ }
+ if (++(cam->next_buf) >= cam->nbufs)
+ cam->next_buf = 0;
+ if (! test_bit(bufno, &cam->flags))
+ continue;
+ if (list_empty(&cam->sb_avail))
+ break; /* Leave it valid, hope for better later */
+ clear_bit(bufno, &cam->flags);
+ /*
+ * We could perhaps drop the spinlock during this
+ * big copy. Something to consider.
+ */
+ sbuf = list_entry(cam->sb_avail.next,
+ struct cafe_sio_buffer, list);
+ memcpy(sbuf->buffer, cam->dma_bufs[bufno],
+ cam->pix_format.sizeimage);
+ sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
+ sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+ list_move_tail(&sbuf->list, &cam->sb_full);
+ }
+ if (! list_empty(&cam->sb_full))
+ wake_up(&cam->iowait);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+
+static void cafe_frame_complete(struct cafe_camera *cam, int frame)
+{
+ /*
+ * Basic frame housekeeping.
+ */
+ if (test_bit(frame, &cam->flags) && printk_ratelimit())
+ cam_err(cam, "Frame overrun on %d, frames lost\n", frame);
+ set_bit(frame, &cam->flags);
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
+ if (cam->next_buf < 0)
+ cam->next_buf = frame;
+ cam->buf_seq[frame] = ++(cam->sequence);
+
+ switch (cam->state) {
+ /*
+ * If in single read mode, try going speculative.
+ */
+ case S_SINGLEREAD:
+ cam->state = S_SPECREAD;
+ cam->specframes = 0;
+ wake_up(&cam->iowait);
+ break;
+
+ /*
+ * If we are already doing speculative reads, and nobody is
+ * reading them, just stop.
+ */
+ case S_SPECREAD:
+ if (++(cam->specframes) >= cam->nbufs) {
+ cafe_ctlr_stop(cam);
+ cafe_ctlr_irq_disable(cam);
+ cam->state = S_IDLE;
+ }
+ wake_up(&cam->iowait);
+ break;
+ /*
+ * For the streaming case, we defer the real work to the
+ * camera tasklet.
+ *
+ * FIXME: if the application is not consuming the buffers,
+ * we should eventually put things on hold and restart in
+ * vidioc_dqbuf().
+ */
+ case S_STREAMING:
+ tasklet_schedule(&cam->s_tasklet);
+ break;
+
+ default:
+ cam_err(cam, "Frame interrupt in non-operational state\n");
+ break;
+ }
+}
+
+
+
+
+static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs)
+{
+ unsigned int frame;
+
+ cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
+ /*
+ * Handle any frame completions. There really should
+ * not be more than one of these, or we have fallen
+ * far behind.
+ */
+ for (frame = 0; frame < cam->nbufs; frame++)
+ if (irqs & (IRQ_EOF0 << frame))
+ cafe_frame_complete(cam, frame);
+ /*
+ * If a frame starts, note that we have DMA active. This
+ * code assumes that we won't get multiple frame interrupts
+ * at once; may want to rethink that.
+ */
+ if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2))
+ set_bit(CF_DMA_ACTIVE, &cam->flags);
+}
+
+
+
+static irqreturn_t cafe_irq(int irq, void *data)
+{
+ struct cafe_camera *cam = data;
+ unsigned int irqs;
+
+ spin_lock(&cam->dev_lock);
+ irqs = cafe_reg_read(cam, REG_IRQSTAT);
+ if ((irqs & ALLIRQS) == 0) {
+ spin_unlock(&cam->dev_lock);
+ return IRQ_NONE;
+ }
+ if (irqs & FRAMEIRQS)
+ cafe_frame_irq(cam, irqs);
+ if (irqs & TWSIIRQS) {
+ cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS);
+ wake_up(&cam->smbus_wait);
+ }
+ spin_unlock(&cam->dev_lock);
+ return IRQ_HANDLED;
+}
+
+
+/* -------------------------------------------------------------------------- */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * Debugfs stuff.
+ */
+
+static char cafe_debug_buf[1024];
+static struct dentry *cafe_dfs_root;
+
+static void cafe_dfs_setup(void)
+{
+ cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL);
+ if (IS_ERR(cafe_dfs_root)) {
+ cafe_dfs_root = NULL; /* Never mind */
+ printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n");
+ }
+}
+
+static void cafe_dfs_shutdown(void)
+{
+ if (cafe_dfs_root)
+ debugfs_remove(cafe_dfs_root);
+}
+
+static int cafe_dfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t cafe_dfs_read_regs(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cafe_camera *cam = file->private_data;
+ char *s = cafe_debug_buf;
+ int offset;
+
+ for (offset = 0; offset < 0x44; offset += 4)
+ s += sprintf(s, "%02x: %08x\n", offset,
+ cafe_reg_read(cam, offset));
+ for (offset = 0x88; offset <= 0x90; offset += 4)
+ s += sprintf(s, "%02x: %08x\n", offset,
+ cafe_reg_read(cam, offset));
+ for (offset = 0xb4; offset <= 0xbc; offset += 4)
+ s += sprintf(s, "%02x: %08x\n", offset,
+ cafe_reg_read(cam, offset));
+ for (offset = 0x3000; offset <= 0x300c; offset += 4)
+ s += sprintf(s, "%04x: %08x\n", offset,
+ cafe_reg_read(cam, offset));
+ return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+ s - cafe_debug_buf);
+}
+
+static struct file_operations cafe_dfs_reg_ops = {
+ .owner = THIS_MODULE,
+ .read = cafe_dfs_read_regs,
+ .open = cafe_dfs_open
+};
+
+static ssize_t cafe_dfs_read_cam(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cafe_camera *cam = file->private_data;
+ char *s = cafe_debug_buf;
+ int offset;
+
+ if (! cam->sensor)
+ return -EINVAL;
+ for (offset = 0x0; offset < 0x8a; offset++)
+ {
+ u8 v;
+
+ cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v);
+ s += sprintf(s, "%02x: %02x\n", offset, v);
+ }
+ return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+ s - cafe_debug_buf);
+}
+
+static struct file_operations cafe_dfs_cam_ops = {
+ .owner = THIS_MODULE,
+ .read = cafe_dfs_read_cam,
+ .open = cafe_dfs_open
+};
+
+
+
+static void cafe_dfs_cam_setup(struct cafe_camera *cam)
+{
+ char fname[40];
+
+ if (!cafe_dfs_root)
+ return;
+ sprintf(fname, "regs-%d", cam->v4ldev.minor);
+ cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+ cam, &cafe_dfs_reg_ops);
+ sprintf(fname, "cam-%d", cam->v4ldev.minor);
+ cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+ cam, &cafe_dfs_cam_ops);
+}
+
+
+static void cafe_dfs_cam_shutdown(struct cafe_camera *cam)
+{
+ if (! IS_ERR(cam->dfs_regs))
+ debugfs_remove(cam->dfs_regs);
+ if (! IS_ERR(cam->dfs_cam_regs))
+ debugfs_remove(cam->dfs_cam_regs);
+}
+
+#else
+
+#define cafe_dfs_setup()
+#define cafe_dfs_shutdown()
+#define cafe_dfs_cam_setup(cam)
+#define cafe_dfs_cam_shutdown(cam)
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+
+
+
+/* ------------------------------------------------------------------------*/
+/*
+ * PCI interface stuff.
+ */
+
+static int cafe_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ u16 classword;
+ struct cafe_camera *cam;
+ /*
+ * Make sure we have a camera here - we'll get calls for
+ * the other cafe devices as well.
+ */
+ pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword);
+ if (classword != PCI_CLASS_MULTIMEDIA_VIDEO)
+ return -ENODEV;
+ /*
+ * Start putting together one of our big camera structures.
+ */
+ ret = -ENOMEM;
+ cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+ if (cam == NULL)
+ goto out;
+ mutex_init(&cam->s_mutex);
+ mutex_lock(&cam->s_mutex);
+ spin_lock_init(&cam->dev_lock);
+ cam->state = S_NOTREADY;
+ cafe_set_config_needed(cam, 1);
+ init_waitqueue_head(&cam->smbus_wait);
+ init_waitqueue_head(&cam->iowait);
+ cam->pdev = pdev;
+ cam->pix_format = cafe_def_pix_format;
+ INIT_LIST_HEAD(&cam->dev_list);
+ INIT_LIST_HEAD(&cam->sb_avail);
+ INIT_LIST_HEAD(&cam->sb_full);
+ tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam);
+ /*
+ * Get set up on the PCI bus.
+ */
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out_free;
+ pci_set_master(pdev);
+
+ ret = -EIO;
+ cam->regs = pci_iomap(pdev, 0, 0);
+ if (! cam->regs) {
+ printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+ goto out_free;
+ }
+ ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+ if (ret)
+ goto out_iounmap;
+ cafe_ctlr_init(cam);
+ cafe_ctlr_power_up(cam);
+ /*
+ * Set up I2C/SMBUS communications
+ */
+ mutex_unlock(&cam->s_mutex); /* attach can deadlock */
+ ret = cafe_smbus_setup(cam);
+ if (ret)
+ goto out_freeirq;
+ /*
+ * Get the v4l2 setup done.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->v4ldev = cafe_v4l_template;
+ cam->v4ldev.debug = 0;
+// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+ ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto out_smbus;
+ /*
+ * If so requested, try to get our DMA buffers now.
+ */
+ if (alloc_bufs_at_load) {
+ if (cafe_alloc_dma_bufs(cam, 1))
+ cam_warn(cam, "Unable to alloc DMA buffers at load"
+ " will try again later.");
+ }
+
+ cafe_dfs_cam_setup(cam);
+ mutex_unlock(&cam->s_mutex);
+ cafe_add_dev(cam);
+ return 0;
+
+ out_smbus:
+ cafe_smbus_shutdown(cam);
+ out_freeirq:
+ cafe_ctlr_power_down(cam);
+ free_irq(pdev->irq, cam);
+ out_iounmap:
+ pci_iounmap(pdev, cam->regs);
+ out_free:
+ kfree(cam);
+ out:
+ return ret;
+}
+
+
+/*
+ * Shut down an initialized device
+ */
+static void cafe_shutdown(struct cafe_camera *cam)
+{
+/* FIXME: Make sure we take care of everything here */
+ cafe_dfs_cam_shutdown(cam);
+ if (cam->n_sbufs > 0)
+ /* What if they are still mapped? Shouldn't be, but... */
+ cafe_free_sio_buffers(cam);
+ cafe_remove_dev(cam);
+ cafe_ctlr_stop_dma(cam);
+ cafe_ctlr_power_down(cam);
+ cafe_smbus_shutdown(cam);
+ cafe_free_dma_bufs(cam);
+ free_irq(cam->pdev->irq, cam);
+ pci_iounmap(cam->pdev, cam->regs);
+ video_unregister_device(&cam->v4ldev);
+ /* kfree(cam); done in v4l_release () */
+}
+
+
+static void cafe_pci_remove(struct pci_dev *pdev)
+{
+ struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+
+ if (cam == NULL) {
+ cam_warn(cam, "pci_remove on unknown pdev %p\n", pdev);
+ return;
+ }
+ mutex_lock(&cam->s_mutex);
+ if (cam->users > 0)
+ cam_warn(cam, "Removing a device with users!\n");
+ cafe_shutdown(cam);
+/* No unlock - it no longer exists */
+}
+
+
+
+
+static struct pci_device_id cafe_ids[] = {
+ { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
+ { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
+ { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_ids);
+
+static struct pci_driver cafe_pci_driver = {
+ .name = "cafe1000-ccic",
+ .id_table = cafe_ids,
+ .probe = cafe_pci_probe,
+ .remove = cafe_pci_remove,
+};
+
+
+
+
+static int __init cafe_init(void)
+{
+ int ret;
+
+ printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+ CAFE_VERSION);
+ cafe_dfs_setup();
+ ret = pci_register_driver(&cafe_pci_driver);
+ if (ret) {
+ printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+ goto out;
+ }
+ request_module("ov7670"); /* FIXME want something more general */
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+
+static void __exit cafe_exit(void)
+{
+ pci_unregister_driver(&cafe_pci_driver);
+ cafe_dfs_shutdown();
+}
+
+module_init(cafe_init);
+module_exit(cafe_exit);
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 0f9d969..b2a66ba 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -53,6 +53,7 @@ config VIDEO_CX88_DVB
select DVB_OR51132 if !DVB_FE_CUSTOMISE
select DVB_CX22702 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 4673832..0cf0360 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -50,7 +50,6 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
#define dprintk(level,fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
-static LIST_HEAD(cx8802_devlist);
/* ------------------------------------------------------------------ */
@@ -882,7 +881,7 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file,
BLACKBIRD_MPEG_CAPTURE,
BLACKBIRD_RAW_BITS_NONE);
- cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
+ cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook);
blackbird_initialize_codec(dev);
cx88_set_scale(dev->core, dev->width, dev->height,
@@ -914,11 +913,15 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file,
}
default:
- return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
+ return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook);
}
return 0;
}
+int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg);
+unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
+
static unsigned int mpeg_translate_ioctl(unsigned int cmd)
{
return cmd;
@@ -927,33 +930,49 @@ static unsigned int mpeg_translate_ioctl(unsigned int cmd)
static int mpeg_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- cmd = mpeg_translate_ioctl( cmd );
- return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl);
+ cmd = cx88_ioctl_translator( cmd );
+ return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook);
}
static int mpeg_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
- struct cx8802_dev *h,*dev = NULL;
+ struct cx8802_dev *dev = NULL;
struct cx8802_fh *fh;
- struct list_head *list;
+ struct cx8802_driver *drv = NULL;
+ int err;
- list_for_each(list,&cx8802_devlist) {
- h = list_entry(list, struct cx8802_dev, devlist);
- if (h->mpeg_dev->minor == minor)
- dev = h;
- }
- if (NULL == dev)
+ dev = cx8802_get_device(inode);
+
+ dprintk( 1, "%s\n", __FUNCTION__);
+
+ if (dev == NULL)
return -ENODEV;
- if (blackbird_initialize_codec(dev) < 0)
+ /* Make sure we can acquire the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ if (drv) {
+ err = drv->request_acquire(drv);
+ if(err != 0) {
+ dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err);
+ return err;
+ }
+ }
+
+ if (blackbird_initialize_codec(dev) < 0) {
+ if (drv)
+ drv->request_release(drv);
return -EINVAL;
+ }
dprintk(1,"open minor=%d\n",minor);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ if (drv)
+ drv->request_release(drv);
return -ENOMEM;
+ }
file->private_data = fh;
fh->dev = dev;
@@ -974,6 +993,8 @@ static int mpeg_open(struct inode *inode, struct file *file)
static int mpeg_release(struct inode *inode, struct file *file)
{
struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = NULL;
+ struct cx8802_driver *drv = NULL;
/* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */
blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
@@ -992,6 +1013,16 @@ static int mpeg_release(struct inode *inode, struct file *file)
videobuf_mmap_free(&fh->mpegq);
file->private_data = NULL;
kfree(fh);
+
+ /* Make sure we release the hardware */
+ dev = cx8802_get_device(inode);
+ if (dev == NULL)
+ return -ENODEV;
+
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ if (drv)
+ drv->request_release(drv);
+
return 0;
}
@@ -1043,6 +1074,44 @@ static struct video_device cx8802_mpeg_template =
/* ------------------------------------------------------------------ */
+/* The CX8802 MPEG API will call this when we can use the hardware */
+static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->board) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* By default, core setup will leave the cx22702 out of reset, on the bus.
+ * We left the hardware on power up with the cx22702 active.
+ * We're being given access to re-arrange the GPIOs.
+ * Take the bus off the cx22702 and put the cx23416 on it.
+ */
+ cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */
+ cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* The CX8802 MPEG API will call this when we need to release the hardware */
+static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->board) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Exit leaving the cx23416 on the bus */
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
static void blackbird_unregister_video(struct cx8802_dev *dev)
{
if (dev->mpeg_dev) {
@@ -1073,28 +1142,23 @@ static int blackbird_register_video(struct cx8802_dev *dev)
/* ----------------------------------------------------------- */
-static int __devinit blackbird_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+static int cx8802_blackbird_probe(struct cx8802_driver *drv)
{
- struct cx8802_dev *dev;
- struct cx88_core *core;
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = core->dvbdev;
int err;
- /* general setup */
- core = cx88_core_get(pci_dev);
- if (NULL == core)
- return -EINVAL;
+ dprintk( 1, "%s\n", __FUNCTION__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->board,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
err = -ENODEV;
if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD))
goto fail_core;
- err = -ENOMEM;
- dev = kzalloc(sizeof(*dev),GFP_KERNEL);
- if (NULL == dev)
- goto fail_core;
- dev->pci = pci_dev;
- dev->core = core;
dev->width = 720;
dev->height = 576;
cx2341x_fill_defaults(&dev->params);
@@ -1106,64 +1170,36 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev,
dev->height = 576;
}
- err = cx8802_init_common(dev);
- if (0 != err)
- goto fail_free;
-
/* blackbird stuff */
printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
core->name);
host_setup(dev->core);
- list_add_tail(&dev->devlist,&cx8802_devlist);
blackbird_register_video(dev);
/* initial device configuration: needed ? */
return 0;
- fail_free:
- kfree(dev);
fail_core:
- cx88_core_put(core,pci_dev);
return err;
}
-static void __devexit blackbird_remove(struct pci_dev *pci_dev)
+static int cx8802_blackbird_remove(struct cx8802_driver *drv)
{
- struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
-
/* blackbird */
- blackbird_unregister_video(dev);
- list_del(&dev->devlist);
+ blackbird_unregister_video(drv->core->dvbdev);
- /* common */
- cx8802_fini_common(dev);
- cx88_core_put(dev->core,dev->pci);
- kfree(dev);
+ return 0;
}
-static struct pci_device_id cx8802_pci_tbl[] = {
- {
- .vendor = 0x14f1,
- .device = 0x8802,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },{
- /* --- end of list --- */
- }
-};
-MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
-
-static struct pci_driver blackbird_pci_driver = {
- .name = "cx88-blackbird",
- .id_table = cx8802_pci_tbl,
- .probe = blackbird_probe,
- .remove = __devexit_p(blackbird_remove),
-#ifdef CONFIG_PM
- .suspend = cx8802_suspend_common,
- .resume = cx8802_resume_common,
-#endif
+static struct cx8802_driver cx8802_blackbird_driver = {
+ .type_id = CX88_MPEG_BLACKBIRD,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_blackbird_probe,
+ .remove = cx8802_blackbird_remove,
+ .advise_acquire = cx8802_blackbird_advise_acquire,
+ .advise_release = cx8802_blackbird_advise_release,
};
static int blackbird_init(void)
@@ -1176,17 +1212,22 @@ static int blackbird_init(void)
printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
#endif
- return pci_register_driver(&blackbird_pci_driver);
+ cx88_ioctl_hook = mpeg_do_ioctl;
+ cx88_ioctl_translator = mpeg_translate_ioctl;
+ return cx8802_register_driver(&cx8802_blackbird_driver);
}
static void blackbird_fini(void)
{
- pci_unregister_driver(&blackbird_pci_driver);
+ cx8802_unregister_driver(&cx8802_blackbird_driver);
}
module_init(blackbird_init);
module_exit(blackbird_fini);
+EXPORT_SYMBOL(cx88_ioctl_hook);
+EXPORT_SYMBOL(cx88_ioctl_translator);
+
/* ----------------------------------------------------------- */
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index f764a57..c791708 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -281,18 +281,22 @@ struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x0000bde2,
+ .extadc = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x0000bde6,
+ .extadc = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000bde6,
+ .extadc = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x0000bd62,
+ .extadc = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -353,6 +357,7 @@ struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+ .extadc = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -523,6 +528,7 @@ struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
+ .extadc = 1,
}},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -646,18 +652,22 @@ struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00009d80,
+ .extadc = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00009d76,
+ .extadc = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00009d76,
+ .extadc = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x00009d00,
+ .extadc = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -786,25 +796,29 @@ struct cx88_board cx88_boards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
- .mpeg = CX88_MPEG_BLACKBIRD,
.input = {{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 0,
.gpio0 = 0x0000cd73,
+ .extadc = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 1,
.gpio0 = 0x0000cd73,
+ .extadc = 1,
},{
.type = CX88_VMUX_TELEVISION,
.vmux = 3,
.gpio0 = 0x0000cdb3,
+ .extadc = 1,
}},
.radio = {
.type = CX88_RADIO,
.vmux = 2,
.gpio0 = 0x0000cdf3,
+ .extadc = 1,
},
+ .mpeg = CX88_MPEG_BLACKBIRD,
},
[CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
/* Alexander Wold <awold@bigfoot.com> */
@@ -1050,7 +1064,6 @@ struct cx88_board cx88_boards[] = {
.mpeg = CX88_MPEG_DVB,
},
[CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
- /* FIXME: Audio not working for s-video / composite inputs. */
.name = "KWorld HardwareMpegTV XPert",
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -1065,10 +1078,12 @@ struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x3de6,
+ .extadc = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x3de6,
+ .extadc = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -1252,35 +1267,35 @@ struct cx88_board cx88_boards[] = {
.gpio0 = 0x070b,
}},
},
- [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
- .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- }},
- },
+ [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+ .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ }},
+ },
[CX88_BOARD_HAUPPAUGE_HVR1300] = {
.name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
.tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
@@ -1293,17 +1308,20 @@ struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xe780,
+ .extadc = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xe780,
+ .extadc = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xe780,
+ .extadc = 1,
}},
/* fixme: Add radio support */
- .mpeg = CX88_MPEG_DVB,
+ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
},
};
const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
@@ -1513,6 +1531,10 @@ struct cx88_subid cx88_subids[] = {
},{
.subvendor = 0x17de,
.subdevice = 0x0840,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0305,
.card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
},{
.subvendor = 0x18ac,
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 4b655f2..453af5e 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -1153,7 +1153,7 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
mutex_lock(&devlist);
cx88_ir_fini(core);
if (0 == core->i2c_rc)
- i2c_bit_del_bus(&core->i2c_adap);
+ i2c_del_adapter(&core->i2c_adap);
list_del(&core->devlist);
iounmap(core->lmmio);
cx88_devcount--;
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 0ef13e7..8b20335 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -42,7 +42,7 @@
#include "cx22702.h"
#include "or51132.h"
#include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
#include "nxt200x.h"
#include "cx24123.h"
#include "isl6421.h"
@@ -57,7 +57,7 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg)
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
/* ------------------------------------------------------------------ */
@@ -74,8 +74,8 @@ static int dvb_buf_setup(struct videobuf_queue *q,
return 0;
}
-static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int dvb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
{
struct cx8802_dev *dev = q->priv_data;
return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
@@ -87,7 +87,8 @@ static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
}
-static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void dvb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
{
cx88_free_buffer(q, (struct cx88_buffer*)vb);
}
@@ -100,6 +101,26 @@ static struct videobuf_queue_ops dvb_qops = {
};
/* ------------------------------------------------------------------ */
+
+static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx8802_driver *drv = NULL;
+ int ret = 0;
+
+ drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
+ if (drv) {
+ if (acquire)
+ ret = drv->request_acquire(drv);
+ else
+ ret = drv->request_release(drv);
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
@@ -268,35 +289,6 @@ static struct mt352_config dntv_live_dvbt_pro_config = {
};
#endif
-static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params)
-{
- u8 pllbuf[4];
- struct cx8802_dev *dev= fe->dvb->priv;
- struct i2c_msg msg =
- { .addr = dev->core->pll_addr, .flags = 0,
- .buf = pllbuf, .len = 4 };
- int err;
-
- dvb_pll_configure(dev->core->pll_desc, pllbuf,
- params->frequency,
- params->u.ofdm.bandwidth);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "cx88-dvb: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, pllbuf[0], pllbuf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
static struct zl10353_config dvico_fusionhdtv_hybrid = {
.demod_address = 0x0f,
.no_tuner = 1,
@@ -311,28 +303,12 @@ static struct cx22702_config connexant_refboard_config = {
.output_mode = CX22702_SERIAL_OUTPUT,
};
-static struct cx22702_config hauppauge_novat_config = {
- .demod_address = 0x43,
- .output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr1100_config = {
+static struct cx22702_config hauppauge_hvr_config = {
.demod_address = 0x63,
.output_mode = CX22702_SERIAL_OUTPUT,
};
-static struct cx22702_config hauppauge_hvr1300_config = {
- .demod_address = 0x63,
- .output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr3000_config = {
- .demod_address = 0x63,
- .output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static int or51132_set_ts_param(struct dvb_frontend* fe,
- int is_punctured)
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
{
struct cx8802_dev *dev= fe->dvb->priv;
dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -344,50 +320,6 @@ static struct or51132_config pchdtv_hd3000 = {
.set_ts_params = or51132_set_ts_param,
};
-static int lgdt3302_tuner_set_params(struct dvb_frontend* fe,
- struct dvb_frontend_parameters* params)
-{
- /* FIXME make this routine use the tuner-simple code.
- * It could probably be shared with a number of ATSC
- * frontends. Many share the same tuner with analog TV. */
-
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
- u8 buf[4];
- struct i2c_msg msg =
- { .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 };
- int err;
-
- dvb_pll_configure(core->pll_desc, buf, params->frequency, 0);
- dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n",
- __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "cx88-dvb: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int lgdt3303_tuner_set_params(struct dvb_frontend* fe,
- struct dvb_frontend_parameters* params)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- /* Put the analog decoder in standby to keep it quiet */
- cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
-
- return lg_h06xf_pll_set(fe, &core->i2c_adap, params);
-}
-
static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
{
struct cx8802_dev *dev= fe->dvb->priv;
@@ -432,8 +364,7 @@ static struct lgdt330x_config pchdtv_hd5500 = {
.set_ts_params = lgdt330x_set_ts_param,
};
-static int nxt200x_set_ts_param(struct dvb_frontend* fe,
- int is_punctured)
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
{
struct cx8802_dev *dev= fe->dvb->priv;
dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -469,11 +400,10 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
struct cx8802_dev *dev= fe->dvb->priv;
struct cx88_core *core = dev->core;
- if (voltage == SEC_VOLTAGE_OFF) {
+ if (voltage == SEC_VOLTAGE_OFF)
cx_write(MO_GP0_IO, 0x000006fb);
- } else {
+ else
cx_write(MO_GP0_IO, 0x000006f9);
- }
if (core->prev_set_voltage)
return core->prev_set_voltage(fe, voltage);
@@ -522,7 +452,7 @@ static int dvb_register(struct cx8802_dev *dev)
switch (dev->core->board) {
case CX88_BOARD_HAUPPAUGE_DVB_T1:
dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_novat_config,
+ &connexant_refboard_config,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -547,32 +477,11 @@ static int dvb_register(struct cx8802_dev *dev)
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr1100_config,
- &dev->core->i2c_adap);
- if (dev->dvb.frontend != NULL) {
- dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_fmd1216me);
- }
- break;
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr1300_config,
- &dev->core->i2c_adap);
- if (dev->dvb.frontend != NULL) {
- dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_fmd1216me);
- }
- break;
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr3000_config,
+ &hauppauge_hvr_config,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_fmd1216me);
+ &dev->core->i2c_adap, &dvb_pll_fmd1216me);
}
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
@@ -647,18 +556,17 @@ static int dvb_register(struct cx8802_dev *dev)
#endif
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
- dev->core->pll_addr = 0x61;
- dev->core->pll_desc = &dvb_pll_thomson_fe6600;
dev->dvb.frontend = dvb_attach(zl10353_attach,
&dvico_fusionhdtv_hybrid,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params;
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_thomson_fe6600);
}
break;
case CX88_BOARD_PCHDTV_HD3000:
- dev->dvb.frontend = dvb_attach(or51132_attach,
- &pchdtv_hd3000,
+ dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -679,13 +587,13 @@ static int dvb_register(struct cx8802_dev *dev)
/* Select RF connector callback */
fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
- dev->core->pll_addr = 0x61;
- dev->core->pll_desc = &dvb_pll_microtune_4042;
dev->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_3_gold,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_microtune_4042);
}
}
break;
@@ -699,13 +607,13 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(100);
cx_set(MO_GP0_IO, 9);
mdelay(200);
- dev->core->pll_addr = 0x61;
- dev->core->pll_desc = &dvb_pll_thomson_dtt761x;
dev->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_3_gold,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_thomson_dtt761x);
}
}
break;
@@ -723,7 +631,8 @@ static int dvb_register(struct cx8802_dev *dev)
&fusionhdtv_5_gold,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+ dvb_attach(lgh06xf_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap);
}
}
break;
@@ -741,7 +650,8 @@ static int dvb_register(struct cx8802_dev *dev)
&pchdtv_hd5500,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+ dvb_attach(lgh06xf_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap);
}
}
break;
@@ -782,6 +692,24 @@ static int dvb_register(struct cx8802_dev *dev)
dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
}
break;
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap, &dvb_pll_fmd1216me);
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap, &dvb_pll_fmd1216me);
+ }
+ break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->core->name);
@@ -796,6 +724,8 @@ static int dvb_register(struct cx8802_dev *dev)
dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min;
dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max;
}
+ /* Ensure all frontends negotiate bus access */
+ dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
/* Put the analog decoder in standby to keep it quiet */
cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
@@ -806,37 +736,67 @@ static int dvb_register(struct cx8802_dev *dev)
/* ----------------------------------------------------------- */
-static int __devinit dvb_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+/* CX8802 MPEG -> mini driver - We have been given the hardware */
+static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
{
- struct cx8802_dev *dev;
- struct cx88_core *core;
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __FUNCTION__);
+
+ switch (core->board) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* We arrive here with either the cx23416 or the cx22702
+ * on the bus. Take the bus from the cx23416 and enable the
+ * cx22702 demod
+ */
+ cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */
+ cx_clear(MO_GP0_IO, 0x00000004);
+ udelay(1000);
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* CX8802 MPEG -> mini driver - We no longer have the hardware */
+static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __FUNCTION__);
+
+ switch (core->board) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Do Nothing, leave the cx22702 on the bus. */
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+static int cx8802_dvb_probe(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = drv->core->dvbdev;
int err;
- /* general setup */
- core = cx88_core_get(pci_dev);
- if (NULL == core)
- return -EINVAL;
+ dprintk( 1, "%s\n", __FUNCTION__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->board,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
err = -ENODEV;
if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB))
goto fail_core;
- err = -ENOMEM;
- dev = kzalloc(sizeof(*dev),GFP_KERNEL);
- if (NULL == dev)
- goto fail_core;
- dev->pci = pci_dev;
- dev->core = core;
-
- err = cx8802_init_common(dev);
- if (0 != err)
- goto fail_free;
-
#ifdef HAVE_VP3054_I2C
err = vp3054_i2c_probe(dev);
if (0 != err)
- goto fail_free;
+ goto fail_core;
#endif
/* dvb stuff */
@@ -848,28 +808,16 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev,
sizeof(struct cx88_buffer),
dev);
err = dvb_register(dev);
- if (0 != err)
- goto fail_fini;
+ if (err != 0)
+ printk("%s dvb_register failed err = %d\n", __FUNCTION__, err);
- /* Maintain a reference to cx88-video can query the 8802 device. */
- core->dvbdev = dev;
- return 0;
-
- fail_fini:
- cx8802_fini_common(dev);
- fail_free:
- kfree(dev);
fail_core:
- cx88_core_put(core,pci_dev);
return err;
}
-static void __devexit dvb_remove(struct pci_dev *pci_dev)
+static int cx8802_dvb_remove(struct cx8802_driver *drv)
{
- struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
-
- /* Destroy any 8802 reference. */
- dev->core->dvbdev = NULL;
+ struct cx8802_dev *dev = drv->core->dvbdev;
/* dvb */
videobuf_dvb_unregister(&dev->dvb);
@@ -878,33 +826,16 @@ static void __devexit dvb_remove(struct pci_dev *pci_dev)
vp3054_i2c_remove(dev);
#endif
- /* common */
- cx8802_fini_common(dev);
- cx88_core_put(dev->core,dev->pci);
- kfree(dev);
+ return 0;
}
-static struct pci_device_id cx8802_pci_tbl[] = {
- {
- .vendor = 0x14f1,
- .device = 0x8802,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },{
- /* --- end of list --- */
- }
-};
-MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
-
-static struct pci_driver dvb_pci_driver = {
- .name = "cx88-dvb",
- .id_table = cx8802_pci_tbl,
- .probe = dvb_probe,
- .remove = __devexit_p(dvb_remove),
-#ifdef CONFIG_PM
- .suspend = cx8802_suspend_common,
- .resume = cx8802_resume_common,
-#endif
+static struct cx8802_driver cx8802_dvb_driver = {
+ .type_id = CX88_MPEG_DVB,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_dvb_probe,
+ .remove = cx8802_dvb_remove,
+ .advise_acquire = cx8802_dvb_advise_acquire,
+ .advise_release = cx8802_dvb_advise_release,
};
static int dvb_init(void)
@@ -917,12 +848,12 @@ static int dvb_init(void)
printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
#endif
- return pci_register_driver(&dvb_pci_driver);
+ return cx8802_register_driver(&cx8802_dvb_driver);
}
static void dvb_fini(void)
{
- pci_unregister_driver(&dvb_pci_driver);
+ cx8802_unregister_driver(&cx8802_dvb_driver);
}
module_init(dvb_init);
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index e60a0a5..8136673 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -155,6 +155,35 @@ static void cx88_ir_work(struct work_struct *work)
mod_timer(&ir->timer, timeout);
}
+static void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir)
+{
+ if (ir->polling) {
+ INIT_WORK(&ir->work, cx88_ir_work);
+ init_timer(&ir->timer);
+ ir->timer.function = ir_timer;
+ ir->timer.data = (unsigned long)ir;
+ schedule_work(&ir->work);
+ }
+ if (ir->sampling) {
+ core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */
+ cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */
+ cx_write(MO_DDSCFG_IO, 0x5); /* enable */
+ }
+}
+
+static void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir)
+{
+ if (ir->sampling) {
+ cx_write(MO_DDSCFG_IO, 0x0);
+ core->pci_irqmask &= ~(1 << 18);
+ }
+
+ if (ir->polling) {
+ del_timer_sync(&ir->timer);
+ flush_scheduled_work();
+ }
+}
+
/* ---------------------------------------------------------------------- */
int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
@@ -163,14 +192,12 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
struct input_dev *input_dev;
IR_KEYTAB_TYPE *ir_codes = NULL;
int ir_type = IR_TYPE_OTHER;
+ int err = -ENOMEM;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
input_dev = input_allocate_device();
- if (!ir || !input_dev) {
- kfree(ir);
- input_free_device(input_dev);
- return -ENOMEM;
- }
+ if (!ir || !input_dev)
+ goto err_out_free;
ir->input = input_dev;
@@ -280,9 +307,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
}
if (NULL == ir_codes) {
- kfree(ir);
- input_free_device(input_dev);
- return -ENODEV;
+ err = -ENODEV;
+ goto err_out_free;
}
/* init input device */
@@ -307,23 +333,22 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->core = core;
core->ir = ir;
- if (ir->polling) {
- INIT_WORK(&ir->work, cx88_ir_work);
- init_timer(&ir->timer);
- ir->timer.function = ir_timer;
- ir->timer.data = (unsigned long)ir;
- schedule_work(&ir->work);
- }
- if (ir->sampling) {
- core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */
- cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */
- cx_write(MO_DDSCFG_IO, 0x5); /* enable */
- }
+ cx88_ir_start(core, ir);
/* all done */
- input_register_device(ir->input);
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_stop;
return 0;
+
+ err_out_stop:
+ cx88_ir_stop(core, ir);
+ core->ir = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
}
int cx88_ir_fini(struct cx88_core *core)
@@ -334,15 +359,7 @@ int cx88_ir_fini(struct cx88_core *core)
if (NULL == ir)
return 0;
- if (ir->sampling) {
- cx_write(MO_DDSCFG_IO, 0x0);
- core->pci_irqmask &= ~(1 << 18);
- }
- if (ir->polling) {
- del_timer(&ir->timer);
- flush_scheduled_work();
- }
-
+ cx88_ir_stop(core, ir);
input_unregister_device(ir->input);
kfree(ir);
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 6b23a4e..1fe1a83 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -44,8 +44,12 @@ module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg)
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
+#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+
+static LIST_HEAD(cx8802_devlist);
/* ------------------------------------------------------------------ */
static int cx8802_start_dma(struct cx8802_dev *dev,
@@ -65,17 +69,13 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
/* FIXME: this needs a review.
* also: move to cx88-blackbird + cx88-dvb source files? */
- if (cx88_boards[core->board].mpeg == (CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) ) {
- /* Report a warning until the mini driver patch is applied,
- * else the following conditions will set the dma registers incorrectly.
- * This will be removed in the next major patch and changes to the conditions
- * will be made.
- */
- printk(KERN_INFO "%s() board->(CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) is invalid\n", __FUNCTION__);
- return -EINVAL;
- }
- if (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) {
+ dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
+
+ if ( (core->active_type_id == CX88_MPEG_DVB) &&
+ (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) ) {
+
+ dprintk( 1, "cx8802_start_dma doing .dvb\n");
/* negedge driven & software reset */
cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
udelay(100);
@@ -93,15 +93,17 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
udelay(100);
break;
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ break;
default:
cx_write(TS_SOP_STAT, 0x00);
break;
}
cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
udelay(100);
- }
-
- if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
+ } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
+ (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) ) {
+ dprintk( 1, "cx8802_start_dma doing .blackbird\n");
cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
@@ -112,6 +114,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
udelay(100);
+ } else {
+ printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__,
+ cx88_boards[core->board].mpeg );
+ return -EINVAL;
}
/* reset counter */
@@ -542,8 +548,315 @@ int cx8802_resume_common(struct pci_dev *pci_dev)
return 0;
}
+struct cx8802_dev * cx8802_get_device(struct inode *inode)
+{
+ int minor = iminor(inode);
+ struct cx8802_dev *h = NULL;
+ struct list_head *list;
+
+ list_for_each(list,&cx8802_devlist) {
+ h = list_entry(list, struct cx8802_dev, devlist);
+ if (h->mpeg_dev->minor == minor)
+ return h;
+ }
+
+ return NULL;
+}
+
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
+{
+ struct cx8802_dev *h = NULL;
+ struct cx8802_driver *d = NULL;
+ struct list_head *list;
+ struct list_head *list2;
+
+ list_for_each(list,&cx8802_devlist) {
+ h = list_entry(list, struct cx8802_dev, devlist);
+ if (h != dev)
+ continue;
+
+ list_for_each(list2, &h->drvlist.devlist) {
+ d = list_entry(list2, struct cx8802_driver, devlist);
+
+ /* only unregister the correct driver type */
+ if (d->type_id == btype) {
+ return d;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Driver asked for hardware access. */
+int cx8802_request_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+
+ /* Fail a request for hardware if the device is busy. */
+ if (core->active_type_id != CX88_BOARD_NONE)
+ return -EBUSY;
+
+ if (drv->advise_acquire)
+ {
+ core->active_type_id = drv->type_id;
+ drv->advise_acquire(drv);
+
+ mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
+ }
+
+ return 0;
+}
+
+/* Driver asked to release hardware. */
+int cx8802_request_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+
+ if (drv->advise_release)
+ {
+ drv->advise_release(drv);
+ core->active_type_id = CX88_BOARD_NONE;
+ mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
+ }
+
+ return 0;
+}
+
+static int cx8802_check_driver(struct cx8802_driver *drv)
+{
+ if (drv == NULL)
+ return -ENODEV;
+
+ if ((drv->type_id != CX88_MPEG_DVB) &&
+ (drv->type_id != CX88_MPEG_BLACKBIRD))
+ return -EINVAL;
+
+ if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
+ (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
+ return -EINVAL;
+
+ if ((drv->probe == NULL) ||
+ (drv->remove == NULL) ||
+ (drv->advise_acquire == NULL) ||
+ (drv->advise_release == NULL))
+ return -EINVAL;
+
+ return 0;
+}
+
+int cx8802_register_driver(struct cx8802_driver *drv)
+{
+ struct cx8802_dev *h;
+ struct cx8802_driver *driver;
+ struct list_head *list;
+ int err = 0, i = 0;
+
+ printk(KERN_INFO "%s() ->registering driver type=%s access=%s\n", __FUNCTION__ ,
+ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+ drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+ if ((err = cx8802_check_driver(drv)) != 0) {
+ printk(KERN_INFO "%s() cx8802_driver is invalid\n", __FUNCTION__ );
+ return err;
+ }
+
+ list_for_each(list,&cx8802_devlist) {
+ h = list_entry(list, struct cx8802_dev, devlist);
+
+ printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n",
+ h->core->name,h->pci->subsystem_vendor,
+ h->pci->subsystem_device,cx88_boards[h->core->board].name,
+ h->core->board);
+
+ /* Bring up a new struct for each driver instance */
+ driver = kzalloc(sizeof(*drv),GFP_KERNEL);
+ if (driver == NULL)
+ return -ENOMEM;
+
+ /* Snapshot of the driver registration data */
+ drv->core = h->core;
+ drv->suspend = cx8802_suspend_common;
+ drv->resume = cx8802_resume_common;
+ drv->request_acquire = cx8802_request_acquire;
+ drv->request_release = cx8802_request_release;
+ memcpy(driver, drv, sizeof(*driver));
+
+ err = drv->probe(driver);
+ if (err == 0) {
+ i++;
+ mutex_lock(&drv->core->lock);
+ list_add_tail(&driver->devlist,&h->drvlist.devlist);
+ mutex_unlock(&drv->core->lock);
+ } else {
+ printk(KERN_ERR "%s() ->probe failed err = %d\n", __FUNCTION__, err);
+ }
+
+ }
+ if (i == 0)
+ err = -ENODEV;
+ else
+ err = 0;
+
+ return err;
+}
+
+int cx8802_unregister_driver(struct cx8802_driver *drv)
+{
+ struct cx8802_dev *h;
+ struct cx8802_driver *d;
+ struct list_head *list;
+ struct list_head *list2, *q;
+ int err = 0, i = 0;
+
+ printk(KERN_INFO "%s() ->unregistering driver type=%s\n", __FUNCTION__ ,
+ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird");
+
+ list_for_each(list,&cx8802_devlist) {
+ i++;
+ h = list_entry(list, struct cx8802_dev, devlist);
+
+ printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n",
+ h->core->name,h->pci->subsystem_vendor,
+ h->pci->subsystem_device,cx88_boards[h->core->board].name,
+ h->core->board);
+
+ list_for_each_safe(list2, q, &h->drvlist.devlist) {
+ d = list_entry(list2, struct cx8802_driver, devlist);
+
+ /* only unregister the correct driver type */
+ if (d->type_id != drv->type_id)
+ continue;
+
+ err = d->remove(d);
+ if (err == 0) {
+ mutex_lock(&drv->core->lock);
+ list_del(list2);
+ mutex_unlock(&drv->core->lock);
+ } else
+ printk(KERN_ERR "%s() ->remove failed err = %d\n", __FUNCTION__, err);
+
+ }
+
+ }
+
+ return err;
+}
+
/* ----------------------------------------------------------- */
+static int __devinit cx8802_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8802_dev *dev;
+ struct cx88_core *core;
+ int err;
+
+ /* general setup */
+ core = cx88_core_get(pci_dev);
+ if (NULL == core)
+ return -EINVAL;
+ printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
+
+ err = -ENODEV;
+ if (!cx88_boards[core->board].mpeg)
+ goto fail_core;
+
+ err = -ENOMEM;
+ dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ goto fail_core;
+ dev->pci = pci_dev;
+ dev->core = core;
+
+ err = cx8802_init_common(dev);
+ if (err != 0)
+ goto fail_free;
+
+ INIT_LIST_HEAD(&dev->drvlist.devlist);
+ list_add_tail(&dev->devlist,&cx8802_devlist);
+
+ /* Maintain a reference so cx88-video can query the 8802 device. */
+ core->dvbdev = dev;
+ return 0;
+
+ fail_free:
+ kfree(dev);
+ fail_core:
+ cx88_core_put(core,pci_dev);
+ return err;
+}
+
+static void __devexit cx8802_remove(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev;
+ struct cx8802_driver *h;
+ struct list_head *list;
+
+ dev = pci_get_drvdata(pci_dev);
+
+ dprintk( 1, "%s\n", __FUNCTION__);
+
+ list_for_each(list,&dev->drvlist.devlist) {
+ h = list_entry(list, struct cx8802_driver, devlist);
+ dprintk( 1, " ->driver\n");
+ if (h->remove == NULL) {
+ printk(KERN_ERR "%s .. skipping driver, no probe function\n", __FUNCTION__);
+ continue;
+ }
+ printk(KERN_INFO "%s .. Removing driver type %d\n", __FUNCTION__, h->type_id);
+ cx8802_unregister_driver(h);
+ list_del(&dev->drvlist.devlist);
+ }
+
+ /* Destroy any 8802 reference. */
+ dev->core->dvbdev = NULL;
+
+ /* common */
+ cx8802_fini_common(dev);
+ cx88_core_put(dev->core,dev->pci);
+ kfree(dev);
+}
+
+static struct pci_device_id cx8802_pci_tbl[] = {
+ {
+ .vendor = 0x14f1,
+ .device = 0x8802,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver cx8802_pci_driver = {
+ .name = "cx88-mpeg driver manager",
+ .id_table = cx8802_pci_tbl,
+ .probe = cx8802_probe,
+ .remove = __devexit_p(cx8802_remove),
+};
+
+static int cx8802_init(void)
+{
+ printk(KERN_INFO "cx2388x cx88-mpeg Driver Manager version %d.%d.%d loaded\n",
+ (CX88_VERSION_CODE >> 16) & 0xff,
+ (CX88_VERSION_CODE >> 8) & 0xff,
+ CX88_VERSION_CODE & 0xff);
+#ifdef SNAPSHOT
+ printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
+ SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+ return pci_register_driver(&cx8802_pci_driver);
+}
+
+static void cx8802_fini(void)
+{
+ pci_unregister_driver(&cx8802_pci_driver);
+}
+
+module_init(cx8802_init);
+module_exit(cx8802_fini);
EXPORT_SYMBOL(cx8802_buf_prepare);
EXPORT_SYMBOL(cx8802_buf_queue);
EXPORT_SYMBOL(cx8802_cancel_buffers);
@@ -551,9 +864,10 @@ EXPORT_SYMBOL(cx8802_cancel_buffers);
EXPORT_SYMBOL(cx8802_init_common);
EXPORT_SYMBOL(cx8802_fini_common);
-EXPORT_SYMBOL(cx8802_suspend_common);
-EXPORT_SYMBOL(cx8802_resume_common);
-
+EXPORT_SYMBOL(cx8802_register_driver);
+EXPORT_SYMBOL(cx8802_unregister_driver);
+EXPORT_SYMBOL(cx8802_get_device);
+EXPORT_SYMBOL(cx8802_get_driver);
/* ----------------------------------------------------------- */
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 58ba9f7..3482e01 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -143,19 +143,6 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl)
cx88_start_audio_dma(core);
if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
- /* sets sound input from external adc */
- switch (core->board) {
- case CX88_BOARD_HAUPPAUGE_ROSLYN:
- case CX88_BOARD_KWORLD_MCE200_DELUXE:
- case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT:
- case CX88_BOARD_PIXELVIEW_PLAYTV_P7000:
- case CX88_BOARD_ASUS_PVR_416:
- cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
- break;
- default:
- cx_set(AUD_CTL, EN_I2SIN_ENABLE);
- }
-
cx_write(AUD_I2SINPUTCNTL, 4);
cx_write(AUD_BAUDRATE, 1);
/* 'pass-thru mode': this enables the i2s output to the mpeg encoder */
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 90e298d..8613378 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -454,6 +454,14 @@ static int video_mux(struct cx88_core *core, unsigned int input)
cx_clear(MO_FILTER_ODD, 0x00002020);
break;
}
+
+ if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
+ /* sets sound input from external adc */
+ if (INPUT(input)->extadc)
+ cx_set(AUD_CTL, EN_I2SIN_ENABLE);
+ else
+ cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+ }
return 0;
}
@@ -1490,6 +1498,30 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
mutex_unlock(&core->lock);
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /* ioctls to allow direct acces to the cx2388x registers */
+ case VIDIOC_INT_G_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != 0)
+ return -EINVAL;
+ /* cx2388x has a 24-bit register space */
+ reg->val = cx_read(reg->reg&0xffffff);
+ return 0;
+ }
+ case VIDIOC_INT_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx_write(reg->reg&0xffffff, reg->val);
+ return 0;
+ }
+#endif
default:
return v4l_compat_translate_ioctl(inode,file,cmd,arg,
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c
index 2b4f197..6068c9b 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.c
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c
@@ -168,7 +168,7 @@ void vp3054_i2c_remove(struct cx8802_dev *dev)
dev->core->board != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
return;
- i2c_bit_del_bus(&vp3054_i2c->adap);
+ i2c_del_adapter(&vp3054_i2c->adap);
kfree(vp3054_i2c);
}
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 3bc91aa..7054e94 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -74,6 +74,11 @@ enum cx88_board_type {
CX88_MPEG_BLACKBIRD
};
+enum cx8802_board_access {
+ CX8802_DRVCTL_SHARED = 1,
+ CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
/* ----------------------------------------------------------- */
/* tv norms */
@@ -220,6 +225,7 @@ struct cx88_input {
enum cx88_itype type;
unsigned int vmux;
u32 gpio0, gpio1, gpio2, gpio3;
+ unsigned int extadc:1;
};
struct cx88_board {
@@ -330,6 +336,7 @@ struct cx88_core {
/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
struct cx8802_dev *dvbdev;
+ enum cx88_board_type active_type_id;
};
struct cx8800_dev;
@@ -405,6 +412,31 @@ struct cx8802_suspend_state {
int disabled;
};
+struct cx8802_driver {
+ struct cx88_core *core;
+ struct list_head devlist;
+
+ /* Type of driver and access required */
+ enum cx88_board_type type_id;
+ enum cx8802_board_access hw_access;
+
+ /* MPEG 8802 internal only */
+ int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
+ int (*resume)(struct pci_dev *pci_dev);
+
+ /* MPEG 8802 -> mini driver - Driver probe and configuration */
+ int (*probe)(struct cx8802_driver *drv);
+ int (*remove)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 -> mini driver - Access for hardware control */
+ int (*advise_acquire)(struct cx8802_driver *drv);
+ int (*advise_release)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 <- mini driver - Access for hardware control */
+ int (*request_acquire)(struct cx8802_driver *drv);
+ int (*request_release)(struct cx8802_driver *drv);
+};
+
struct cx8802_dev {
struct cx88_core *core;
spinlock_t slock;
@@ -439,6 +471,9 @@ struct cx8802_dev {
/* mpeg params */
struct cx2341x_mpeg_params params;
+
+ /* List of attached drivers */
+ struct cx8802_driver drvlist;
};
/* ----------------------------------------------------------- */
@@ -571,6 +606,11 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
int cx88_audio_thread(void *data);
+int cx8802_register_driver(struct cx8802_driver *drv);
+int cx8802_unregister_driver(struct cx8802_driver *drv);
+struct cx8802_dev * cx8802_get_device(struct inode *inode);
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
+
/* ----------------------------------------------------------- */
/* cx88-input.c */
@@ -600,6 +640,13 @@ extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
extern const u32 cx88_user_ctrls[];
extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl);
+/* ----------------------------------------------------------- */
+/* cx88-blackbird.c */
+/* used by cx88-ivtv ioctl emulation layer */
+extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg);
+extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
+
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c
index b1012e9..917021f 100644
--- a/drivers/media/video/dabusb.c
+++ b/drivers/media/video/dabusb.c
@@ -218,7 +218,7 @@ static int dabusb_alloc_buffers (pdabusb_t s)
pipesize, packets, transfer_buffer_length);
while (buffers < (s->total_buffer_size << 10)) {
- b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL);
+ b = kzalloc(sizeof (buff_t), GFP_KERNEL);
if (!b) {
err("kzalloc(sizeof(buff_t))==NULL");
goto err;
@@ -659,7 +659,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm
switch (cmd) {
case IOCTL_DAB_BULK:
- pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+ pbulk = kmalloc(sizeof (bulk_transfer_t), GFP_KERNEL);
if (!pbulk) {
ret = -ENOMEM;
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index ab87e7b..59edf58 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -305,15 +305,14 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
int ir_type;
struct IR_i2c *ir;
struct input_dev *input_dev;
+ int err;
ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL);
input_dev = input_allocate_device();
if (!ir || !input_dev) {
- input_free_device(input_dev);
- kfree(ir);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_out_free;
}
- memset(ir,0,sizeof(*ir));
ir->c = client_template;
ir->input = input_dev;
@@ -355,32 +354,34 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
break;
case 0x7a:
case 0x47:
+ case 0x71:
/* Handled by saa7134-input */
name = "SAA713x remote";
ir_type = IR_TYPE_OTHER;
break;
default:
/* shouldn't happen */
- printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr);
- kfree(ir);
- return -1;
+ printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr);
+ err = -ENODEV;
+ goto err_out_free;
}
/* Sets name */
snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name);
- ir->ir_codes=ir_codes;
+ ir->ir_codes = ir_codes;
/* register i2c device
* At device register, IR codes may be changed to be
* board dependent.
*/
- i2c_attach_client(&ir->c);
+ err = i2c_attach_client(&ir->c);
+ if (err)
+ goto err_out_free;
/* If IR not supported or disabled, unregisters driver */
if (ir->get_key == NULL) {
- i2c_detach_client(&ir->c);
- kfree(ir);
- return -1;
+ err = -ENODEV;
+ goto err_out_detach;
}
/* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */
@@ -389,15 +390,17 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
ir->c.dev.bus_id);
/* init + register input device */
- ir_input_init(input_dev,&ir->ir,ir_type,ir->ir_codes);
+ ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
input_dev->id.bustype = BUS_I2C;
input_dev->name = ir->c.name;
input_dev->phys = ir->phys;
- /* register event device */
- input_register_device(ir->input);
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_detach;
+
printk(DEVNAME ": %s detected at %s [%s]\n",
- ir->input->name,ir->input->phys,adap->name);
+ ir->input->name, ir->input->phys, adap->name);
/* start polling via eventd */
INIT_WORK(&ir->work, ir_work);
@@ -407,6 +410,13 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
schedule_work(&ir->work);
return 0;
+
+ err_out_detach:
+ i2c_detach_client(&ir->c);
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
}
static int ir_detach(struct i2c_client *client)
@@ -414,7 +424,7 @@ static int ir_detach(struct i2c_client *client)
struct IR_i2c *ir = i2c_get_clientdata(client);
/* kill outstanding polls */
- del_timer(&ir->timer);
+ del_timer_sync(&ir->timer);
flush_scheduled_work();
/* unregister devices */
@@ -439,7 +449,7 @@ static int ir_probe(struct i2c_adapter *adap)
*/
static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
- static const int probe_saa7134[] = { 0x7a, 0x47, -1 };
+ static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 };
static const int probe_em28XX[] = { 0x30, 0x47, -1 };
const int *probe = NULL;
struct i2c_client c;
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index b0aea40..152cc6b 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -160,10 +160,6 @@ static int mxb_probe(struct saa7146_dev* dev)
printk("mxb: saa7111 i2c module not available.\n");
return -ENODEV;
}
- if ((result = request_module("tuner")) < 0) {
- printk("mxb: tuner i2c module not available.\n");
- return -ENODEV;
- }
if ((result = request_module("tea6420")) < 0) {
printk("mxb: tea6420 i2c module not available.\n");
return -ENODEV;
@@ -176,6 +172,10 @@ static int mxb_probe(struct saa7146_dev* dev)
printk("mxb: tda9840 i2c module not available.\n");
return -ENODEV;
}
+ if ((result = request_module("tuner")) < 0) {
+ printk("mxb: tuner i2c module not available.\n");
+ return -ENODEV;
+ }
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if( NULL == mxb ) {
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
new file mode 100644
index 0000000..5ed0adc
--- /dev/null
+++ b/drivers/media/video/ov7670.c
@@ -0,0 +1,1333 @@
+/*
+ * A V4L2 driver for OmniVision OV7670 cameras.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc. Written
+ * by Jonathan Corbet with substantial inspiration from Mark
+ * McClelland's ovcamchip code.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+MODULE_LICENSE("GPL");
+
+/*
+ * Basic window sizes. These probably belong somewhere more globally
+ * useful.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define CIF_WIDTH 352
+#define CIF_HEIGHT 288
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+
+/*
+ * Our nominal (default) frame rate.
+ */
+#define OV7670_FRAME_RATE 30
+
+/*
+ * The 7670 sits on i2c with ID 0x42
+ */
+#define OV7670_I2C_ADDR 0x42
+
+/* Registers */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE 0x01 /* blue gain */
+#define REG_RED 0x02 /* red gain */
+#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1 0x04 /* Control 1 */
+#define COM1_CCIR656 0x40 /* CCIR656 enable */
+#define REG_BAVE 0x05 /* U/B Average level */
+#define REG_GbAVE 0x06 /* Y/Gb Average level */
+#define REG_AECHH 0x07 /* AEC MS 5 bits */
+#define REG_RAVE 0x08 /* V/R Average level */
+#define REG_COM2 0x09 /* Control 2 */
+#define COM2_SSLEEP 0x10 /* Soft sleep mode */
+#define REG_PID 0x0a /* Product ID MSB */
+#define REG_VER 0x0b /* Product ID LSB */
+#define REG_COM3 0x0c /* Control 3 */
+#define COM3_SWAP 0x40 /* Byte swap */
+#define COM3_SCALEEN 0x08 /* Enable scaling */
+#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */
+#define REG_COM4 0x0d /* Control 4 */
+#define REG_COM5 0x0e /* All "reserved" */
+#define REG_COM6 0x0f /* Control 6 */
+#define REG_AECH 0x10 /* More bits of AEC value */
+#define REG_CLKRC 0x11 /* Clocl control */
+#define CLK_EXT 0x40 /* Use external clock directly */
+#define CLK_SCALE 0x3f /* Mask for internal clock scale */
+#define REG_COM7 0x12 /* Control 7 */
+#define COM7_RESET 0x80 /* Register reset */
+#define COM7_FMT_MASK 0x38
+#define COM7_FMT_VGA 0x00
+#define COM7_FMT_CIF 0x20 /* CIF format */
+#define COM7_FMT_QVGA 0x10 /* QVGA format */
+#define COM7_FMT_QCIF 0x08 /* QCIF format */
+#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */
+#define COM7_YUV 0x00 /* YUV */
+#define COM7_BAYER 0x01 /* Bayer format */
+#define COM7_PBAYER 0x05 /* "Processed bayer" */
+#define REG_COM8 0x13 /* Control 8 */
+#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define COM8_BFILT 0x20 /* Band filter enable */
+#define COM8_AGC 0x04 /* Auto gain enable */
+#define COM8_AWB 0x02 /* White balance enable */
+#define COM8_AEC 0x01 /* Auto exposure enable */
+#define REG_COM9 0x14 /* Control 9 - gain ceiling */
+#define REG_COM10 0x15 /* Control 10 */
+#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */
+#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */
+#define COM10_HREF_REV 0x08 /* Reverse HREF */
+#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */
+#define COM10_VS_NEG 0x02 /* VSYNC negative */
+#define COM10_HS_NEG 0x01 /* HSYNC negative */
+#define REG_HSTART 0x17 /* Horiz start high bits */
+#define REG_HSTOP 0x18 /* Horiz stop high bits */
+#define REG_VSTART 0x19 /* Vert start high bits */
+#define REG_VSTOP 0x1a /* Vert stop high bits */
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+#define REG_MIDH 0x1c /* Manuf. ID high */
+#define REG_MIDL 0x1d /* Manuf. ID low */
+#define REG_MVFP 0x1e /* Mirror / vflip */
+#define MVFP_MIRROR 0x20 /* Mirror image */
+#define MVFP_FLIP 0x10 /* Vertical flip */
+
+#define REG_AEW 0x24 /* AGC upper limit */
+#define REG_AEB 0x25 /* AGC lower limit */
+#define REG_VPT 0x26 /* AGC/AEC fast mode op region */
+#define REG_HSYST 0x30 /* HSYNC rising edge delay */
+#define REG_HSYEN 0x31 /* HSYNC falling edge delay */
+#define REG_HREF 0x32 /* HREF pieces */
+#define REG_TSLB 0x3a /* lots of stuff */
+#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */
+#define REG_COM11 0x3b /* Control 11 */
+#define COM11_NIGHT 0x80 /* NIght mode enable */
+#define COM11_NMFR 0x60 /* Two bit NM frame rate */
+#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define COM11_50HZ 0x08 /* Manual 50Hz select */
+#define COM11_EXP 0x02
+#define REG_COM12 0x3c /* Control 12 */
+#define COM12_HREF 0x80 /* HREF always */
+#define REG_COM13 0x3d /* Control 13 */
+#define COM13_GAMMA 0x80 /* Gamma enable */
+#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */
+#define REG_COM14 0x3e /* Control 14 */
+#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */
+#define REG_EDGE 0x3f /* Edge enhancement factor */
+#define REG_COM15 0x40 /* Control 15 */
+#define COM15_R10F0 0x00 /* Data range 10 to F0 */
+#define COM15_R01FE 0x80 /* 01 to FE */
+#define COM15_R00FF 0xc0 /* 00 to FF */
+#define COM15_RGB565 0x10 /* RGB565 output */
+#define COM15_RGB555 0x30 /* RGB555 output */
+#define REG_COM16 0x41 /* Control 16 */
+#define COM16_AWBGAIN 0x08 /* AWB gain enable */
+#define REG_COM17 0x42 /* Control 17 */
+#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */
+#define COM17_CBAR 0x08 /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58. Sign for v-red is bit 0, and up from there.
+ */
+#define REG_CMATRIX_BASE 0x4f
+#define CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT 0x55 /* Brightness */
+#define REG_CONTRAS 0x56 /* Contrast control */
+
+#define REG_GFIX 0x69 /* Fix gain control */
+
+#define REG_RGB444 0x8c /* RGB 444 control */
+#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
+#define R444_RGBX 0x01 /* Empty nibble at end */
+
+#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX 0xa5 /* 50hz banding step limit */
+#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define REG_BD60MAX 0xab /* 60hz banding step limit */
+
+
+/*
+ * Information we maintain about a known sensor.
+ */
+struct ov7670_format_struct; /* coming later */
+struct ov7670_info {
+ struct ov7670_format_struct *fmt; /* Current format */
+ unsigned char sat; /* Saturation value */
+ int hue; /* Hue value */
+};
+
+
+
+
+/*
+ * The default register settings, as obtained from OmniVision. There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+ { REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ * 2 = 20fps
+ * 1 = 30fps
+ */
+ { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */
+ { REG_TSLB, 0x04 }, /* OV */
+ { REG_COM7, 0 }, /* VGA */
+ /*
+ * Set the hardware window. These values from OV don't entirely
+ * make sense - hstop is less than hstart. But they work...
+ */
+ { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 },
+ { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 },
+ { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a },
+
+ { REG_COM3, 0 }, { REG_COM14, 0 },
+ /* Mystery scaling numbers */
+ { 0x70, 0x3a }, { 0x71, 0x35 },
+ { 0x72, 0x11 }, { 0x73, 0xf0 },
+ { 0xa2, 0x02 }, { REG_COM10, 0x0 },
+
+ /* Gamma curve values */
+ { 0x7a, 0x20 }, { 0x7b, 0x10 },
+ { 0x7c, 0x1e }, { 0x7d, 0x35 },
+ { 0x7e, 0x5a }, { 0x7f, 0x69 },
+ { 0x80, 0x76 }, { 0x81, 0x80 },
+ { 0x82, 0x88 }, { 0x83, 0x8f },
+ { 0x84, 0x96 }, { 0x85, 0xa3 },
+ { 0x86, 0xaf }, { 0x87, 0xc4 },
+ { 0x88, 0xd7 }, { 0x89, 0xe8 },
+
+ /* AGC and AEC parameters. Note we start by disabling those features,
+ then turn them only after tweaking the values. */
+ { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+ { REG_GAIN, 0 }, { REG_AECH, 0 },
+ { REG_COM4, 0x40 }, /* magic reserved bit */
+ { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+ { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 },
+ { REG_AEW, 0x95 }, { REG_AEB, 0x33 },
+ { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 },
+ { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */
+ { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 },
+ { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 },
+ { REG_HAECC7, 0x94 },
+ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+
+ /* Almost all of these are magic "reserved" values. */
+ { REG_COM5, 0x61 }, { REG_COM6, 0x4b },
+ { 0x16, 0x02 }, { REG_MVFP, 0x07|MVFP_MIRROR },
+ { 0x21, 0x02 }, { 0x22, 0x91 },
+ { 0x29, 0x07 }, { 0x33, 0x0b },
+ { 0x35, 0x0b }, { 0x37, 0x1d },
+ { 0x38, 0x71 }, { 0x39, 0x2a },
+ { REG_COM12, 0x78 }, { 0x4d, 0x40 },
+ { 0x4e, 0x20 }, { REG_GFIX, 0 },
+ { 0x6b, 0x4a }, { 0x74, 0x10 },
+ { 0x8d, 0x4f }, { 0x8e, 0 },
+ { 0x8f, 0 }, { 0x90, 0 },
+ { 0x91, 0 }, { 0x96, 0 },
+ { 0x9a, 0 }, { 0xb0, 0x84 },
+ { 0xb1, 0x0c }, { 0xb2, 0x0e },
+ { 0xb3, 0x82 }, { 0xb8, 0x0a },
+
+ /* More reserved magic, some of which tweaks white balance */
+ { 0x43, 0x0a }, { 0x44, 0xf0 },
+ { 0x45, 0x34 }, { 0x46, 0x58 },
+ { 0x47, 0x28 }, { 0x48, 0x3a },
+ { 0x59, 0x88 }, { 0x5a, 0x88 },
+ { 0x5b, 0x44 }, { 0x5c, 0x67 },
+ { 0x5d, 0x49 }, { 0x5e, 0x0e },
+ { 0x6c, 0x0a }, { 0x6d, 0x55 },
+ { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */
+ { 0x6a, 0x40 }, { REG_BLUE, 0x40 },
+ { REG_RED, 0x60 },
+ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+ /* Matrix coefficients */
+ { 0x4f, 0x80 }, { 0x50, 0x80 },
+ { 0x51, 0 }, { 0x52, 0x22 },
+ { 0x53, 0x5e }, { 0x54, 0x80 },
+ { 0x58, 0x9e },
+
+ { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 },
+ { 0x75, 0x05 }, { 0x76, 0xe1 },
+ { 0x4c, 0 }, { 0x77, 0x01 },
+ { REG_COM13, 0xc3 }, { 0x4b, 0x09 },
+ { 0xc9, 0x60 }, { REG_COM16, 0x38 },
+ { 0x56, 0x40 },
+
+ { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO },
+ { 0xa4, 0x88 }, { 0x96, 0 },
+ { 0x97, 0x30 }, { 0x98, 0x20 },
+ { 0x99, 0x30 }, { 0x9a, 0x84 },
+ { 0x9b, 0x29 }, { 0x9c, 0x03 },
+ { 0x9d, 0x4c }, { 0x9e, 0x3f },
+ { 0x78, 0x04 },
+
+ /* Extra-weird stuff. Some sort of multiplexor register */
+ { 0x79, 0x01 }, { 0xc8, 0xf0 },
+ { 0x79, 0x0f }, { 0xc8, 0x00 },
+ { 0x79, 0x10 }, { 0xc8, 0x7e },
+ { 0x79, 0x0a }, { 0xc8, 0x80 },
+ { 0x79, 0x0b }, { 0xc8, 0x01 },
+ { 0x79, 0x0c }, { 0xc8, 0x0f },
+ { 0x79, 0x0d }, { 0xc8, 0x20 },
+ { 0x79, 0x09 }, { 0xc8, 0x80 },
+ { 0x79, 0x02 }, { 0xc8, 0xc0 },
+ { 0x79, 0x03 }, { 0xc8, 0x40 },
+ { 0x79, 0x05 }, { 0xc8, 0x30 },
+ { 0x79, 0x26 },
+
+ { 0xff, 0xff }, /* END MARKER */
+};
+
+
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ *
+ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
+ */
+
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+ { REG_COM7, 0x0 }, /* Selects YUV mode */
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ { REG_COM1, 0 },
+ { REG_COM15, COM15_R00FF },
+ { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0x80 }, /* "matrix coefficient 1" */
+ { 0x50, 0x80 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x22 }, /* "matrix coefficient 4" */
+ { 0x53, 0x5e }, /* "matrix coefficient 5" */
+ { 0x54, 0x80 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT },
+ { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+ { REG_COM7, COM7_RGB }, /* Selects RGB mode */
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ { REG_COM1, 0x0 },
+ { REG_COM15, COM15_RGB565 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT },
+ { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb444[] = {
+ { REG_COM7, COM7_RGB }, /* Selects RGB mode */
+ { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */
+ { REG_COM1, 0x40 }, /* Magic reserved bit */
+ { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */
+ { 0xff, 0xff },
+};
+
+
+
+
+/*
+ * Low-level register I/O.
+ */
+
+static int ov7670_read(struct i2c_client *c, unsigned char reg,
+ unsigned char *value)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(c, reg);
+ if (ret >= 0)
+ *value = (unsigned char) ret;
+ return ret;
+}
+
+
+static int ov7670_write(struct i2c_client *c, unsigned char reg,
+ unsigned char value)
+{
+ return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+
+/*
+ * Write a list of register settings; ff/ff stops the process.
+ */
+static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff || vals->value != 0xff) {
+ int ret = ov7670_write(c, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+
+/*
+ * Stuff that knows about the sensor.
+ */
+static void ov7670_reset(struct i2c_client *client)
+{
+ ov7670_write(client, REG_COM7, COM7_RESET);
+ msleep(1);
+}
+
+
+static int ov7670_init(struct i2c_client *client)
+{
+ return ov7670_write_array(client, ov7670_default_regs);
+}
+
+
+
+static int ov7670_detect(struct i2c_client *client)
+{
+ unsigned char v;
+ int ret;
+
+ ret = ov7670_init(client);
+ if (ret < 0)
+ return ret;
+ ret = ov7670_read(client, REG_MIDH, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x7f) /* OV manuf. id. */
+ return -ENODEV;
+ ret = ov7670_read(client, REG_MIDL, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0xa2)
+ return -ENODEV;
+ /*
+ * OK, we know we have an OmniVision chip...but which one?
+ */
+ ret = ov7670_read(client, REG_PID, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x76) /* PID + VER = 0x76 / 0x73 */
+ return -ENODEV;
+ ret = ov7670_read(client, REG_VER, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x73) /* PID + VER = 0x76 / 0x73 */
+ return -ENODEV;
+ return 0;
+}
+
+
+/*
+ * Store information about the video data format. The color matrix
+ * is deeply tied into the format, so keep the relevant values here.
+ * The magic matrix nubmers come from OmniVision.
+ */
+static struct ov7670_format_struct {
+ __u8 *desc;
+ __u32 pixelformat;
+ struct regval_list *regs;
+ int cmatrix[CMATRIX_LEN];
+} ov7670_formats[] = {
+ {
+ .desc = "YUYV 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .regs = ov7670_fmt_yuv422,
+ .cmatrix = { 128, -128, 0, -34, -94, 128 },
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .regs = ov7670_fmt_rgb444,
+ .cmatrix = { 179, -179, 0, -61, -176, 228 },
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .regs = ov7670_fmt_rgb565,
+ .cmatrix = { 179, -179, 0, -61, -176, 228 },
+ },
+};
+#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+
+/*
+ * All formats we support are 2 bytes/pixel.
+ */
+#define BYTES_PER_PIXEL 2
+
+/*
+ * Then there is the issue of window sizes. Try to capture the info here.
+ */
+
+/*
+ * QCIF mode is done (by OV) in a very strange way - it actually looks like
+ * VGA with weird scaling options - they do *not* use the canned QCIF mode
+ * which is allegedly provided by the sensor. So here's the weird register
+ * settings.
+ */
+static struct regval_list ov7670_qcif_regs[] = {
+ { REG_COM3, COM3_SCALEEN|COM3_DCWEN },
+ { REG_COM3, COM3_DCWEN },
+ { REG_COM14, COM14_DCWEN | 0x01},
+ { 0x73, 0xf1 },
+ { 0xa2, 0x52 },
+ { 0x7b, 0x1c },
+ { 0x7c, 0x28 },
+ { 0x7d, 0x3c },
+ { 0x7f, 0x69 },
+ { REG_COM9, 0x38 },
+ { 0xa1, 0x0b },
+ { 0x74, 0x19 },
+ { 0x9a, 0x80 },
+ { 0x43, 0x14 },
+ { REG_COM13, 0xc0 },
+ { 0xff, 0xff },
+};
+
+static struct ov7670_win_size {
+ int width;
+ int height;
+ unsigned char com7_bit;
+ int hstart; /* Start/stop values for the camera. Note */
+ int hstop; /* that they do not always make complete */
+ int vstart; /* sense to humans, but evidently the sensor */
+ int vstop; /* will do the right thing... */
+ struct regval_list *regs; /* Regs to tweak */
+/* h/vref stuff */
+} ov7670_win_sizes[] = {
+ /* VGA */
+ {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .com7_bit = COM7_FMT_VGA,
+ .hstart = 158, /* These values from */
+ .hstop = 14, /* Omnivision */
+ .vstart = 10,
+ .vstop = 490,
+ .regs = NULL,
+ },
+ /* CIF */
+ {
+ .width = CIF_WIDTH,
+ .height = CIF_HEIGHT,
+ .com7_bit = COM7_FMT_CIF,
+ .hstart = 170, /* Empirically determined */
+ .hstop = 90,
+ .vstart = 14,
+ .vstop = 494,
+ .regs = NULL,
+ },
+ /* QVGA */
+ {
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ .com7_bit = COM7_FMT_QVGA,
+ .hstart = 164, /* Empirically determined */
+ .hstop = 20,
+ .vstart = 14,
+ .vstop = 494,
+ .regs = NULL,
+ },
+ /* QCIF */
+ {
+ .width = QCIF_WIDTH,
+ .height = QCIF_HEIGHT,
+ .com7_bit = COM7_FMT_VGA, /* see comment above */
+ .hstart = 456, /* Empirically determined */
+ .hstop = 24,
+ .vstart = 14,
+ .vstop = 494,
+ .regs = ov7670_qcif_regs,
+ },
+};
+
+#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0]))
+
+
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
+ int vstart, int vstop)
+{
+ int ret;
+ unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+ ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff);
+ ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff);
+ ret += ov7670_read(client, REG_HREF, &v);
+ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+ msleep(10);
+ ret += ov7670_write(client, REG_HREF, v);
+/*
+ * Vertical: similar arrangement, but only 10 bits.
+ */
+ ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff);
+ ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff);
+ ret += ov7670_read(client, REG_VREF, &v);
+ v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
+ msleep(10);
+ ret += ov7670_write(client, REG_VREF, v);
+ return ret;
+}
+
+
+static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+{
+ struct ov7670_format_struct *ofmt;
+
+ if (fmt->index >= N_OV7670_FMTS)
+ return -EINVAL;
+
+ ofmt = ov7670_formats + fmt->index;
+ fmt->flags = 0;
+ strcpy(fmt->description, ofmt->desc);
+ fmt->pixelformat = ofmt->pixelformat;
+ return 0;
+}
+
+
+static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
+ struct ov7670_format_struct **ret_fmt,
+ struct ov7670_win_size **ret_wsize)
+{
+ int index;
+ struct ov7670_win_size *wsize;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ for (index = 0; index < N_OV7670_FMTS; index++)
+ if (ov7670_formats[index].pixelformat == pix->pixelformat)
+ break;
+ if (index >= N_OV7670_FMTS)
+ return -EINVAL;
+ if (ret_fmt != NULL)
+ *ret_fmt = ov7670_formats + index;
+ /*
+ * Fields: the OV devices claim to be progressive.
+ */
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ /*
+ * Round requested image size down to the nearest
+ * we support, but not below the smallest.
+ */
+ for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
+ wsize++)
+ if (pix->width >= wsize->width && pix->height >= wsize->height)
+ break;
+ if (wsize >= ov7670_win_sizes + N_WIN_SIZES)
+ wsize--; /* Take the smallest one */
+ if (ret_wsize != NULL)
+ *ret_wsize = wsize;
+ /*
+ * Note the size we'll actually handle.
+ */
+ pix->width = wsize->width;
+ pix->height = wsize->height;
+ pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+ pix->sizeimage = pix->height*pix->bytesperline;
+ return 0;
+}
+
+/*
+ * Set a format.
+ */
+static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+{
+ int ret;
+ struct ov7670_format_struct *ovfmt;
+ struct ov7670_win_size *wsize;
+ struct ov7670_info *info = i2c_get_clientdata(c);
+ unsigned char com7;
+
+ ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize);
+ if (ret)
+ return ret;
+ /*
+ * COM7 is a pain in the ass, it doesn't like to be read then
+ * quickly written afterward. But we have everything we need
+ * to set it absolutely here, as long as the format-specific
+ * register sets list it first.
+ */
+ com7 = ovfmt->regs[0].value;
+ com7 |= wsize->com7_bit;
+ ov7670_write(c, REG_COM7, com7);
+ /*
+ * Now write the rest of the array. Also store start/stops
+ */
+ ov7670_write_array(c, ovfmt->regs + 1);
+ ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart,
+ wsize->vstop);
+ ret = 0;
+ if (wsize->regs)
+ ret = ov7670_write_array(c, wsize->regs);
+ info->fmt = ovfmt;
+ return 0;
+}
+
+/*
+ * Implement G/S_PARM. There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ unsigned char clkrc;
+ int ret;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = ov7670_read(c, REG_CLKRC, &clkrc);
+ if (ret < 0)
+ return ret;
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = OV7670_FRAME_RATE;
+ if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1)
+ cp->timeperframe.denominator /= (clkrc & CLK_SCALE);
+ return 0;
+}
+
+static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ unsigned char clkrc;
+ int ret, div;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+ /*
+ * CLKRC has a reserved bit, so let's preserve it.
+ */
+ ret = ov7670_read(c, REG_CLKRC, &clkrc);
+ if (ret < 0)
+ return ret;
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ div = 1; /* Reset to full rate */
+ else
+ div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator;
+ if (div == 0)
+ div = 1;
+ else if (div > CLK_SCALE)
+ div = CLK_SCALE;
+ clkrc = (clkrc & 0x80) | div;
+ tpf->numerator = 1;
+ tpf->denominator = OV7670_FRAME_RATE/div;
+ return ov7670_write(c, REG_CLKRC, clkrc);
+}
+
+
+
+/*
+ * Code for dealing with controls.
+ */
+
+
+
+
+
+static int ov7670_store_cmatrix(struct i2c_client *client,
+ int matrix[CMATRIX_LEN])
+{
+ int i, ret;
+ unsigned char signbits;
+
+ /*
+ * Weird crap seems to exist in the upper part of
+ * the sign bits register, so let's preserve it.
+ */
+ ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits);
+ signbits &= 0xc0;
+
+ for (i = 0; i < CMATRIX_LEN; i++) {
+ unsigned char raw;
+
+ if (matrix[i] < 0) {
+ signbits |= (1 << i);
+ if (matrix[i] < -255)
+ raw = 0xff;
+ else
+ raw = (-1 * matrix[i]) & 0xff;
+ }
+ else {
+ if (matrix[i] > 255)
+ raw = 0xff;
+ else
+ raw = matrix[i] & 0xff;
+ }
+ ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw);
+ }
+ ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits);
+ return ret;
+}
+
+
+/*
+ * Hue also requires messing with the color matrix. It also requires
+ * trig functions, which tend not to be well supported in the kernel.
+ * So here is a simple table of sine values, 0-90 degrees, in steps
+ * of five degrees. Values are multiplied by 1000.
+ *
+ * The following naive approximate trig functions require an argument
+ * carefully limited to -180 <= theta <= 180.
+ */
+#define SIN_STEP 5
+static const int ov7670_sin_table[] = {
+ 0, 87, 173, 258, 342, 422,
+ 499, 573, 642, 707, 766, 819,
+ 866, 906, 939, 965, 984, 996,
+ 1000
+};
+
+static int ov7670_sine(int theta)
+{
+ int chs = 1;
+ int sine;
+
+ if (theta < 0) {
+ theta = -theta;
+ chs = -1;
+ }
+ if (theta <= 90)
+ sine = ov7670_sin_table[theta/SIN_STEP];
+ else {
+ theta -= 90;
+ sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
+ }
+ return sine*chs;
+}
+
+static int ov7670_cosine(int theta)
+{
+ theta = 90 - theta;
+ if (theta > 180)
+ theta -= 360;
+ else if (theta < -180)
+ theta += 360;
+ return ov7670_sine(theta);
+}
+
+
+
+
+static void ov7670_calc_cmatrix(struct ov7670_info *info,
+ int matrix[CMATRIX_LEN])
+{
+ int i;
+ /*
+ * Apply the current saturation setting first.
+ */
+ for (i = 0; i < CMATRIX_LEN; i++)
+ matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7;
+ /*
+ * Then, if need be, rotate the hue value.
+ */
+ if (info->hue != 0) {
+ int sinth, costh, tmpmatrix[CMATRIX_LEN];
+
+ memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
+ sinth = ov7670_sine(info->hue);
+ costh = ov7670_cosine(info->hue);
+
+ matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
+ matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
+ matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
+ matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
+ matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
+ matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
+ }
+}
+
+
+
+static int ov7670_t_sat(struct i2c_client *client, int value)
+{
+ struct ov7670_info *info = i2c_get_clientdata(client);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ info->sat = value;
+ ov7670_calc_cmatrix(info, matrix);
+ ret = ov7670_store_cmatrix(client, matrix);
+ return ret;
+}
+
+static int ov7670_q_sat(struct i2c_client *client, __s32 *value)
+{
+ struct ov7670_info *info = i2c_get_clientdata(client);
+
+ *value = info->sat;
+ return 0;
+}
+
+static int ov7670_t_hue(struct i2c_client *client, int value)
+{
+ struct ov7670_info *info = i2c_get_clientdata(client);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ if (value < -180 || value > 180)
+ return -EINVAL;
+ info->hue = value;
+ ov7670_calc_cmatrix(info, matrix);
+ ret = ov7670_store_cmatrix(client, matrix);
+ return ret;
+}
+
+
+static int ov7670_q_hue(struct i2c_client *client, __s32 *value)
+{
+ struct ov7670_info *info = i2c_get_clientdata(client);
+
+ *value = info->hue;
+ return 0;
+}
+
+
+/*
+ * Some weird registers seem to store values in a sign/magnitude format!
+ */
+static unsigned char ov7670_sm_to_abs(unsigned char v)
+{
+ if ((v & 0x80) == 0)
+ return v + 128;
+ else
+ return 128 - (v & 0x7f);
+}
+
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+ if (v > 127)
+ return v & 0x7f;
+ else
+ return (128 - v) | 0x80;
+}
+
+static int ov7670_t_brightness(struct i2c_client *client, int value)
+{
+ unsigned char com8, v;
+ int ret;
+
+ ov7670_read(client, REG_COM8, &com8);
+ com8 &= ~COM8_AEC;
+ ov7670_write(client, REG_COM8, com8);
+ v = ov7670_abs_to_sm(value);
+ ret = ov7670_write(client, REG_BRIGHT, v);
+ return ret;
+}
+
+static int ov7670_q_brightness(struct i2c_client *client, __s32 *value)
+{
+ unsigned char v;
+ int ret = ov7670_read(client, REG_BRIGHT, &v);
+
+ *value = ov7670_sm_to_abs(v);
+ return ret;
+}
+
+static int ov7670_t_contrast(struct i2c_client *client, int value)
+{
+ return ov7670_write(client, REG_CONTRAS, (unsigned char) value);
+}
+
+static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
+{
+ unsigned char v;
+ int ret = ov7670_read(client, REG_CONTRAS, &v);
+
+ *value = v;
+ return ret;
+}
+
+static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
+{
+ int ret;
+ unsigned char v;
+
+ ret = ov7670_read(client, REG_MVFP, &v);
+ *value = (v & MVFP_MIRROR) == MVFP_MIRROR;
+ return ret;
+}
+
+
+static int ov7670_t_hflip(struct i2c_client *client, int value)
+{
+ unsigned char v;
+ int ret;
+
+ ret = ov7670_read(client, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_MIRROR;
+ else
+ v &= ~MVFP_MIRROR;
+ msleep(10); /* FIXME */
+ ret += ov7670_write(client, REG_MVFP, v);
+ return ret;
+}
+
+
+
+static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
+{
+ int ret;
+ unsigned char v;
+
+ ret = ov7670_read(client, REG_MVFP, &v);
+ *value = (v & MVFP_FLIP) == MVFP_FLIP;
+ return ret;
+}
+
+
+static int ov7670_t_vflip(struct i2c_client *client, int value)
+{
+ unsigned char v;
+ int ret;
+
+ ret = ov7670_read(client, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_FLIP;
+ else
+ v &= ~MVFP_FLIP;
+ msleep(10); /* FIXME */
+ ret += ov7670_write(client, REG_MVFP, v);
+ return ret;
+}
+
+
+static struct ov7670_control {
+ struct v4l2_queryctrl qc;
+ int (*query)(struct i2c_client *c, __s32 *value);
+ int (*tweak)(struct i2c_client *c, int value);
+} ov7670_controls[] =
+{
+ {
+ .qc = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0x80,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .tweak = ov7670_t_brightness,
+ .query = ov7670_q_brightness,
+ },
+ {
+ .qc = {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0x40, /* XXX ov7670 spec */
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .tweak = ov7670_t_contrast,
+ .query = ov7670_q_contrast,
+ },
+ {
+ .qc = {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 256,
+ .step = 1,
+ .default_value = 0x80,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .tweak = ov7670_t_sat,
+ .query = ov7670_q_sat,
+ },
+ {
+ .qc = {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HUE",
+ .minimum = -180,
+ .maximum = 180,
+ .step = 5,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .tweak = ov7670_t_hue,
+ .query = ov7670_q_hue,
+ },
+ {
+ .qc = {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .tweak = ov7670_t_vflip,
+ .query = ov7670_q_vflip,
+ },
+ {
+ .qc = {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .tweak = ov7670_t_hflip,
+ .query = ov7670_q_hflip,
+ },
+};
+#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0]))
+
+static struct ov7670_control *ov7670_find_control(__u32 id)
+{
+ int i;
+
+ for (i = 0; i < N_CONTROLS; i++)
+ if (ov7670_controls[i].qc.id == id)
+ return ov7670_controls + i;
+ return NULL;
+}
+
+
+static int ov7670_queryctrl(struct i2c_client *client,
+ struct v4l2_queryctrl *qc)
+{
+ struct ov7670_control *ctrl = ov7670_find_control(qc->id);
+
+ if (ctrl == NULL)
+ return -EINVAL;
+ *qc = ctrl->qc;
+ return 0;
+}
+
+static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+ int ret;
+
+ if (octrl == NULL)
+ return -EINVAL;
+ ret = octrl->query(client, &ctrl->value);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+ int ret;
+
+ if (octrl == NULL)
+ return -EINVAL;
+ ret = octrl->tweak(client, ctrl->value);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+
+
+
+
+
+/*
+ * Basic i2c stuff.
+ */
+static struct i2c_driver ov7670_driver;
+
+static int ov7670_attach(struct i2c_adapter *adapter)
+{
+ int ret;
+ struct i2c_client *client;
+ struct ov7670_info *info;
+
+ /*
+ * For now: only deal with adapters we recognize.
+ */
+ if (adapter->id != I2C_HW_SMBUS_CAFE)
+ return -ENODEV;
+
+ client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL);
+ if (! client)
+ return -ENOMEM;
+ client->adapter = adapter;
+ client->addr = OV7670_I2C_ADDR;
+ client->driver = &ov7670_driver,
+ strcpy(client->name, "OV7670");
+ /*
+ * Set up our info structure.
+ */
+ info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL);
+ if (! info) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ info->fmt = &ov7670_formats[0];
+ info->sat = 128; /* Review this */
+ i2c_set_clientdata(client, info);
+
+ /*
+ * Make sure it's an ov7670
+ */
+ ret = ov7670_detect(client);
+ if (ret)
+ goto out_free_info;
+ i2c_attach_client(client);
+ return 0;
+
+ out_free_info:
+ kfree(info);
+ out_free:
+ kfree(client);
+ return ret;
+}
+
+
+static int ov7670_detach(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(i2c_get_clientdata(client));
+ kfree(client);
+ return 0;
+}
+
+
+static int ov7670_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_INT_G_CHIP_IDENT:
+ * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670;
+ return 0;
+
+ case VIDIOC_INT_RESET:
+ ov7670_reset(client);
+ return 0;
+
+ case VIDIOC_INT_INIT:
+ return ov7670_init(client);
+
+ case VIDIOC_ENUM_FMT:
+ return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg);
+ case VIDIOC_TRY_FMT:
+ return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL);
+ case VIDIOC_S_FMT:
+ return ov7670_s_fmt(client, (struct v4l2_format *) arg);
+ case VIDIOC_QUERYCTRL:
+ return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg);
+ case VIDIOC_S_CTRL:
+ return ov7670_s_ctrl(client, (struct v4l2_control *) arg);
+ case VIDIOC_G_CTRL:
+ return ov7670_g_ctrl(client, (struct v4l2_control *) arg);
+ case VIDIOC_S_PARM:
+ return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
+ case VIDIOC_G_PARM:
+ return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
+ }
+ return -EINVAL;
+}
+
+
+
+static struct i2c_driver ov7670_driver = {
+ .driver = {
+ .name = "ov7670",
+ },
+ .id = I2C_DRIVERID_OV7670,
+ .class = I2C_CLASS_CAM_DIGITAL,
+ .attach_adapter = ov7670_attach,
+ .detach_client = ov7670_detach,
+ .command = ov7670_command,
+};
+
+
+/*
+ * Module initialization
+ */
+static int __init ov7670_mod_init(void)
+{
+ printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n");
+ return i2c_add_driver(&ov7670_driver);
+}
+
+static void __exit ov7670_mod_exit(void)
+{
+ i2c_del_driver(&ov7670_driver);
+}
+
+module_init(ov7670_mod_init);
+module_exit(ov7670_mod_exit);
diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c
index 368d6e2..86d2884 100644
--- a/drivers/media/video/planb.c
+++ b/drivers/media/video/planb.c
@@ -138,7 +138,7 @@ static int grabbuf_alloc(struct planb *pb)
+ MAX_LNUM
#endif /* PLANB_GSCANLINE */
);
- if ((pb->rawbuf = (unsigned char**) kmalloc (npage
+ if ((pb->rawbuf = kmalloc(npage
* sizeof(unsigned long), GFP_KERNEL)) == 0)
return -ENOMEM;
for (i = 0; i < npage; i++) {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index c80c26b..848fb233 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -260,6 +260,22 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
sizeof(decoder_ops[0]))) - 1;
hdw->decoder_ctrl = &ctxt->ctrl;
cp->handler = &ctxt->handler;
+ {
+ /*
+ Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit
+ of nuttiness for cx25840 causes that module to
+ correctly set up its video scaling. This is really
+ a problem in the cx25840 module itself, but we work
+ around it here. The problem has not been seen in
+ ivtv because there VBI is supported and set up. We
+ don't do VBI here (at least not yet) and thus we
+ never attempted to even set it up.
+ */
+ struct v4l2_format fmt;
+ memset(&fmt,0,sizeof(fmt));
+ fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt);
+ }
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
cp->client->addr);
return !0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 1f78733..d200496 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -356,28 +356,6 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
return 0;
}
-static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp)
-{
- /* If we're dealing with a 24xxx device, force the horizontal
- maximum to be 720 no matter what, since we can't get the device
- to work properly with any other value. Otherwise just return
- the normal value. */
- *vp = cptr->info->def.type_int.max_value;
- if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
- return 0;
-}
-
-static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp)
-{
- /* If we're dealing with a 24xxx device, force the horizontal
- minimum to be 720 no matter what, since we can't get the device
- to work properly with any other value. Otherwise just return
- the normal value. */
- *vp = cptr->info->def.type_int.min_value;
- if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
- return 0;
-}
-
static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
{
/* Actual maximum depends on the video standard in effect. */
@@ -758,10 +736,6 @@ static const struct pvr2_ctl_info control_defs[] = {
.default_value = 720,
DEFREF(res_hor),
DEFINT(19,720),
- /* Hook in check for clamp on horizontal resolution in
- order to avoid unsolved problem involving cx25840. */
- .get_max_value = ctrl_hres_max_get,
- .get_min_value = ctrl_hres_min_get,
},{
.desc = "Vertical capture resolution",
.name = "resolution_ver",
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 3b9012f8e3..f9bb41d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -185,6 +185,79 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
}
}
+
+/* This is a special entry point for cases of I2C transaction attempts to
+ the IR receiver. The implementation here simulates the IR receiver by
+ issuing a command to the FX2 firmware and using that response to return
+ what the real I2C receiver would have returned. We use this for 24xxx
+ devices, where the IR receiver chip has been removed and replaced with
+ FX2 related logic. */
+static int i2c_24xxx_ir(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ u8 dat[4];
+ unsigned int stat;
+
+ if (!(rlen || wlen)) {
+ /* This is a probe attempt. Just let it succeed. */
+ return 0;
+ }
+
+ /* We don't understand this kind of transaction */
+ if ((wlen != 0) || (rlen == 0)) return -EIO;
+
+ if (rlen < 3) {
+ /* Mike Isely <isely@pobox.com> Appears to be a probe
+ attempt from lirc. Just fill in zeroes and return. If
+ we try instead to do the full transaction here, then bad
+ things seem to happen within the lirc driver module
+ (version 0.8.0-7 sources from Debian, when run under
+ vanilla 2.6.17.6 kernel) - and I don't have the patience
+ to chase it down. */
+ if (rlen > 0) rdata[0] = 0;
+ if (rlen > 1) rdata[1] = 0;
+ return 0;
+ }
+
+ /* Issue a command to the FX2 to read the IR receiver. */
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = 0xec;
+ stat = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,4);
+ dat[0] = hdw->cmd_buffer[0];
+ dat[1] = hdw->cmd_buffer[1];
+ dat[2] = hdw->cmd_buffer[2];
+ dat[3] = hdw->cmd_buffer[3];
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+
+ /* Give up if that operation failed. */
+ if (stat != 0) return stat;
+
+ /* Mangle the results into something that looks like the real IR
+ receiver. */
+ rdata[2] = 0xc1;
+ if (dat[0] != 1) {
+ /* No code received. */
+ rdata[0] = 0;
+ rdata[1] = 0;
+ } else {
+ u16 val;
+ /* Mash the FX2 firmware-provided IR code into something
+ that the normal i2c chip-level driver expects. */
+ val = dat[1];
+ val <<= 8;
+ val |= dat[2];
+ val >>= 1;
+ val &= ~0x0003;
+ val |= 0x8000;
+ rdata[0] = (val >> 8) & 0xffu;
+ rdata[1] = val & 0xffu;
+ }
+
+ return 0;
+}
+
/* This is a special entry point that is entered if an I2C operation is
attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this
part doesn't work, but we know it is really there. So let's look for
@@ -887,17 +960,17 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
{
unsigned int idx;
- // The default action for all possible I2C addresses is just to do
- // the transfer normally.
+ /* The default action for all possible I2C addresses is just to do
+ the transfer normally. */
for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
hdw->i2c_func[idx] = pvr2_i2c_basic_op;
}
- // If however we're dealing with new hardware, insert some hacks in
- // the I2C transfer stack to let things work better.
+ /* However, deal with various special cases for 24xxx hardware. */
if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
hdw->i2c_func[0x1b] = i2c_hack_wm8775;
hdw->i2c_func[0x44] = i2c_hack_cx25840;
+ hdw->i2c_func[0x18] = i2c_24xxx_ir;
}
// Configure the adapter and set up everything else related to it.
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index f28398d..c2374ed 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -851,7 +851,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
/* On 60Hz, it is using a higher Vertical Output Size */
if (!is_50hz)
- res+=(VRES_60HZ-480)>>1;
+ res += (VRES_60HZ - 480) >> 1;
/* height */
saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
@@ -907,7 +907,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
/* Activates task "B" */
saa711x_write(client, R_80_GLOBAL_CNTL_1,
- saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20);
+ saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
return 0;
}
@@ -932,11 +932,11 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
if (std & V4L2_STD_525_60) {
v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
saa711x_writeregs(client, saa7115_cfg_60hz_video);
- saa711x_set_size(client,720,480);
+ saa711x_set_size(client, 720, 480);
} else {
v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
saa711x_writeregs(client, saa7115_cfg_50hz_video);
- saa711x_set_size(client,720,576);
+ saa711x_set_size(client, 720, 576);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -1464,13 +1464,13 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
client->driver = &i2c_driver_saa711x;
snprintf(client->name, sizeof(client->name) - 1, "saa7115");
- for (i=0;i<0x0f;i++) {
+ for (i = 0; i < 0x0f; i++) {
saa711x_write(client, 0, i);
- name[i] = (saa711x_read(client, 0) &0x0f) +'0';
- if (name[i]>'9')
- name[i]+='a'-'9'-1;
+ name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+ if (name[i] > '9')
+ name[i] += 'a' - '9' - 1;
}
- name[i]='\0';
+ name[i] = '\0';
saa711x_write(client, 0, 5);
chip_id = saa711x_read(client, 0) & 0x0f;
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 4abf5c0..ffb0f64 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -1,10 +1,6 @@
/*
* SAA713x ALSA support for V4L
*
- *
- * Caveats:
- * - Volume doesn't work (it's always at max)
- *
* 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
@@ -614,13 +610,18 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream)
snd_card_saa7134_pcm_t *pcm;
snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
struct saa7134_dev *dev = saa7134->dev;
- int err;
+ int amux, err;
mutex_lock(&dev->dmasound.lock);
dev->dmasound.read_count = 0;
dev->dmasound.read_offset = 0;
+ amux = dev->input->amux;
+ if ((amux < 1) || (amux > 3))
+ amux = 1;
+ dev->dmasound.input = amux - 1;
+
mutex_unlock(&dev->dmasound.lock);
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
@@ -713,6 +714,8 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
struct snd_ctl_elem_value * ucontrol)
{
snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+ struct saa7134_dev *dev = chip->dev;
+
int change, addr = kcontrol->private_value;
int left, right;
@@ -727,10 +730,52 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
if (right > 20)
right = 20;
spin_lock_irq(&chip->mixer_lock);
- change = chip->mixer_volume[addr][0] != left ||
- chip->mixer_volume[addr][1] != right;
- chip->mixer_volume[addr][0] = left;
- chip->mixer_volume[addr][1] = right;
+ change = 0;
+ if (chip->mixer_volume[addr][0] != left) {
+ change = 1;
+ right = left;
+ }
+ if (chip->mixer_volume[addr][1] != right) {
+ change = 1;
+ left = right;
+ }
+ if (change) {
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ left = 20;
+ break;
+ case MIXER_ADDR_LINE1:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10,
+ (left > 10) ? 0x00 : 0x10);
+ break;
+ case MIXER_ADDR_LINE2:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20,
+ (left > 10) ? 0x00 : 0x20);
+ break;
+ }
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ left = 20;
+ break;
+ case MIXER_ADDR_LINE1:
+ saa_andorb(0x0594, 0x10,
+ (left > 10) ? 0x00 : 0x10);
+ break;
+ case MIXER_ADDR_LINE2:
+ saa_andorb(0x0594, 0x20,
+ (left > 10) ? 0x00 : 0x20);
+ break;
+ }
+ break;
+ }
+ chip->mixer_volume[addr][0] = left;
+ chip->mixer_volume[addr][1] = right;
+ }
spin_unlock_irq(&chip->mixer_lock);
return change;
}
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 51f0cfd..4dead84 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -2462,14 +2462,17 @@ struct saa7134_board saa7134_boards[] = {
.vmux = 1,
.amux = TV,
.tv = 1,
+ .gpio = 0x0000000,
},{
.name = name_comp1,
.vmux = 3,
.amux = LINE2,
+ .gpio = 0x0200000,
},{
.name = name_svideo,
.vmux = 8,
.amux = LINE2,
+ .gpio = 0x0200000,
}},
.radio = {
.name = name_radio,
@@ -3022,6 +3025,158 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE1,
},
},
+ [SAA7134_BOARD_PINNACLE_PCTV_310i] = {
+ .name = "Pinnacle PCTV 310i",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x000200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = {
+ /* Mikhail Fedotov <mo_fedotov@mail.ru> */
+ .name = "Avermedia AVerTV Studio 507",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x00,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x01,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x00,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = {
+ /* Francis Barber <fedora@barber-family.id.au> */
+ .name = "Compro Videomate DVB-T200A",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
+ /* Thomas Genty <tomlohave@gmail.com> */
+ .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
+ .name = "Terratec Cinergy HT PCMCIA",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ }},
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3631,6 +3786,36 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x4860,
.driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID,
},{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002f,
+ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x9715,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4876,
+ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6701,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1172,
+ .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA,
+ },{
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -3717,6 +3902,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_305:
case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
case SAA7134_BOARD_AVERMEDIA_777:
/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */
@@ -3725,6 +3911,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
case SAA7134_BOARD_MANLI_MTV001:
case SAA7134_BOARD_MANLI_MTV002:
case SAA7134_BOARD_BEHOLD_409FM:
@@ -3793,7 +3980,9 @@ int saa7134_board_init1(struct saa7134_dev *dev)
break;
/* i2c remotes */
case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_UPMOST_PURPLE_TV:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
dev->has_remote = SAA7134_REMOTE_I2C;
break;
case SAA7134_BOARD_AVERMEDIA_A169_B:
@@ -3924,9 +4113,11 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_PHILIPS_TIGER:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_TEVION_DVBT_220RF:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
/* this is a hybrid board, initialize to analog mode
* and configure firmware eeprom address
*/
@@ -3952,6 +4143,14 @@ int saa7134_board_init2(struct saa7134_dev *dev)
i2c_transfer(&dev->i2c_adap, &msg, 1);
}
break;
+ case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+ /* make the tda10046 find its eeprom */
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ }
+ break;
case SAA7134_BOARD_KWORLD_ATSC110:
{
/* enable tuner */
@@ -3964,6 +4163,29 @@ int saa7134_board_init2(struct saa7134_dev *dev)
dev->name, i);
}
break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ /* The T200 and the T200A share the same pci id. Consequently,
+ * we are going to query eeprom to try to find out which one we
+ * are actually looking at. */
+
+ /* Don't do this if the board was specifically selected with an
+ * insmod option or if we have the default configuration T200*/
+ if(!dev->autodetected || (dev->eedata[0x41] == 0xd0))
+ break;
+ if(dev->eedata[0x41] == 0x02) {
+ /* Reconfigure board as T200A */
+ dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+ printk(KERN_INFO "%s: Reconfigured board as %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+ } else {
+ printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n",
+ dev->name, dev->eedata[0x41]);
+ break;
+ }
+ break;
}
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 5c9e63d..ed038ff 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -889,15 +889,16 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
must_configure_manually();
dev->board = SAA7134_BOARD_UNKNOWN;
}
+ dev->autodetected = card[dev->nr] != dev->board;
dev->tuner_type = saa7134_boards[dev->board].tuner_type;
dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
if (UNSET != tuner[dev->nr])
dev->tuner_type = tuner[dev->nr];
- printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
- dev->name,pci_dev->subsystem_vendor,
- pci_dev->subsystem_device,saa7134_boards[dev->board].name,
- dev->board, card[dev->nr] == dev->board ?
- "insmod option" : "autodetected");
+ printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name,pci_dev->subsystem_vendor,
+ pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+ dev->board, dev->autodetected ?
+ "autodetected" : "insmod option");
/* get mmio */
if (!request_mem_region(pci_resource_start(pci_dev,0),
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 6b61d9b..fa83398 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -50,6 +50,10 @@ static unsigned int antenna_pwr = 0;
module_param(antenna_pwr, int, 0444);
MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
+static int use_frontent = 0;
+module_param(use_frontent, int, 0644);
+MODULE_PARM_DESC(use_frontent,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
+
/* ------------------------------------------------------------------ */
static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
{
@@ -293,7 +297,7 @@ static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dv
return philips_tda6651_pll_set(0x60, fe, params);
}
-static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
const struct firmware **fw, char *name)
{
struct saa7134_dev *dev = fe->dvb->priv;
@@ -308,7 +312,7 @@ static struct tda1004x_config philips_tu1216_60_config = {
.xtal_freq = TDA10046_XTAL_4M,
.agc_config = TDA10046_AGC_DEFAULT,
.if_freq = TDA10046_FREQ_3617,
- .request_firmware = philips_tu1216_request_firmware,
+ .request_firmware = philips_tda1004x_request_firmware,
};
/* ------------------------------------------------------------------ */
@@ -331,12 +335,12 @@ static struct tda1004x_config philips_tu1216_61_config = {
.xtal_freq = TDA10046_XTAL_4M,
.agc_config = TDA10046_AGC_DEFAULT,
.if_freq = TDA10046_FREQ_3617,
- .request_firmware = philips_tu1216_request_firmware,
+ .request_firmware = philips_tda1004x_request_firmware,
};
/* ------------------------------------------------------------------ */
-static int philips_europa_tuner_init(struct dvb_frontend *fe)
+static int philips_td1316_tuner_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
@@ -347,18 +351,8 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
return -EIO;
- msleep(1);
-
- /* switch the board to dvb mode */
- init_msg.addr = 0x43;
- init_msg.len = 0x02;
- msg[0] = 0x00;
- msg[1] = 0x40;
if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
- return -EIO;
-
+ fe->ops.i2c_gate_ctrl(fe, 0);
return 0;
}
@@ -367,6 +361,22 @@ static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_f
return philips_tda6651_pll_set(0x61, fe, params);
}
+static int philips_europa_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 msg[] = { 0x00, 0x40};
+ struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+
+ if (philips_td1316_tuner_init(fe))
+ return -EIO;
+ msleep(1);
+ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
@@ -671,7 +681,7 @@ static struct tda1004x_config tda827x_lifeview_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
@@ -812,32 +822,40 @@ static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
return 0;
}
/* ------------------------------------------------------------------ */
-static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
{
- int ret;
struct saa7134_dev *dev = fe->dvb->priv;
static u8 tda8290_close[] = { 0x21, 0xc0};
static u8 tda8290_open[] = { 0x21, 0x80};
struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
-
- /* close tda8290 i2c bridge */
- tda8290_msg.buf = tda8290_close;
- ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
- if (ret != 1)
+ if (enable) {
+ tda8290_msg.buf = tda8290_close;
+ } else {
+ tda8290_msg.buf = tda8290_open;
+ }
+ if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1)
return -EIO;
msleep(20);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ int ret;
+
ret = philips_tda827xa_pll_set(0x61, fe, params);
if (ret != 0)
return ret;
- /* open tda8290 i2c bridge */
- tda8290_msg.buf = tda8290_open;
- i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
- return ret;
+ return 0;
}
static int philips_tiger_tuner_init(struct dvb_frontend *fe)
@@ -867,13 +885,80 @@ static struct tda1004x_config philips_tiger_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .if_freq = TDA10046_FREQ_045,
+ .request_firmware = NULL,
+};
+/* ------------------------------------------------------------------ */
+
+static int cinergy_ht_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 data[] = { 0x3c, 0x33, 0x62};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ philips_tda827xa_tuner_sleep( 0x61, fe);
+ return 0;
+}
+
+static struct tda1004x_config cinergy_ht_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X_GP01,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
/* ------------------------------------------------------------------ */
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .if_freq = TDA10046_FREQ_045,
+ .request_firmware = philips_tda1004x_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config hauppauge_hvr_1110_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .if_freq = TDA10046_FREQ_045,
+ .request_firmware = philips_tda1004x_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config asus_p7131_dual_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .if_freq = TDA10046_FREQ_045,
+ .request_firmware = philips_tda1004x_request_firmware,
+};
+
static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
@@ -921,7 +1006,7 @@ static struct tda1004x_config lifeview_trio_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GPL,
+ .agc_config = TDA10046_AGC_TDA827X_GP00,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
@@ -958,7 +1043,7 @@ static struct tda1004x_config ads_tech_duo_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GPL,
+ .agc_config = TDA10046_AGC_TDA827X_GP00,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
@@ -983,7 +1068,7 @@ static struct tda1004x_config tevion_dvbt220rf_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
@@ -1028,7 +1113,7 @@ static struct tda1004x_config md8800_dvbt_config = {
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
+ .agc_config = TDA10046_AGC_TDA827X_GP11,
.if_freq = TDA10046_FREQ_045,
.request_firmware = NULL,
};
@@ -1168,6 +1253,29 @@ static int dvb_init(struct saa7134_dev *dev)
&philips_tiger_config,
&dev->i2c_adap);
if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
+ dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &pinnacle_pctv_310i_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
+ dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &hauppauge_hvr_1110_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1175,9 +1283,10 @@ static int dvb_init(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
dev->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_tiger_config,
+ &asus_p7131_dual_config,
&dev->i2c_adap);
if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep;
dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1194,12 +1303,27 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_FLYDVB_TRIO:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &lifeview_trio_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params;
+ if(! use_frontent) { //terrestrial
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &lifeview_trio_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
+ dev->dvb.frontend->ops.tuner_ops.set_params =
+ lifeview_trio_tuner_set_params;
+ }
+ } else { //satellite
+ dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63,
+ &dev->i2c_adap, 0) == NULL) {
+ printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
+ }
+ if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap,
+ 0x08, 0, 0) == NULL) {
+ printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
+ }
+ }
}
break;
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
@@ -1281,7 +1405,27 @@ static int dvb_init(struct saa7134_dev *dev)
dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params;
}
break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &cinergy_ht_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
+ dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
+ }
+ break;
default:
printk("%s: Huh? unknown DVB card?\n",dev->name);
break;
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 6162550..6f9fe86 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -341,6 +341,7 @@ static int attach_inform(struct i2c_client *client)
switch (client->addr) {
case 0x7a:
case 0x47:
+ case 0x71:
{
struct IR_i2c *ir = i2c_get_clientdata(client);
d1printk("%s i2c IR detected (%s).\n",
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index dee8355..60b38de 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -112,6 +112,27 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char buf[5], cod4, code3, code4;
+
+ /* poll IR chip */
+ if (5 != i2c_master_recv(&ir->c,buf,5))
+ return -EIO;
+
+ cod4 = buf[4];
+ code4 = (cod4 >> 2);
+ code3 = buf[3];
+ if (code3 == 0)
+ /* no key pressed */
+ return 0;
+
+ /* return key */
+ *ir_key = code4;
+ *ir_raw = code4;
+ return 1;
+}
+
void saa7134_input_irq(struct saa7134_dev *dev)
{
struct saa7134_ir *ir = dev->remote;
@@ -131,6 +152,23 @@ static void saa7134_input_timer(unsigned long data)
mod_timer(&ir->timer, timeout);
}
+static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir)
+{
+ if (ir->polling) {
+ init_timer(&ir->timer);
+ ir->timer.function = saa7134_input_timer;
+ ir->timer.data = (unsigned long)dev;
+ ir->timer.expires = jiffies + HZ;
+ add_timer(&ir->timer);
+ }
+}
+
+static void saa7134_ir_stop(struct saa7134_dev *dev)
+{
+ if (dev->remote->polling)
+ del_timer_sync(&dev->remote->timer);
+}
+
int saa7134_input_init1(struct saa7134_dev *dev)
{
struct saa7134_ir *ir;
@@ -141,6 +179,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
u32 mask_keyup = 0;
int polling = 0;
int ir_type = IR_TYPE_OTHER;
+ int err;
if (dev->has_remote != SAA7134_REMOTE_GPIO)
return -ENODEV;
@@ -184,6 +223,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_307:
case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
ir_codes = ir_codes_avermedia;
mask_keycode = 0x0007C8;
@@ -266,9 +306,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ir || !input_dev) {
- kfree(ir);
- input_free_device(input_dev);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_out_free;
}
ir->dev = input_dev;
@@ -299,18 +338,22 @@ int saa7134_input_init1(struct saa7134_dev *dev)
}
input_dev->cdev.dev = &dev->pci->dev;
- /* all done */
dev->remote = ir;
- if (ir->polling) {
- init_timer(&ir->timer);
- ir->timer.function = saa7134_input_timer;
- ir->timer.data = (unsigned long)dev;
- ir->timer.expires = jiffies + HZ;
- add_timer(&ir->timer);
- }
+ saa7134_ir_start(dev, ir);
+
+ err = input_register_device(ir->dev);
+ if (err)
+ goto err_out_stop;
- input_register_device(ir->dev);
return 0;
+
+ err_out_stop:
+ saa7134_ir_stop(dev);
+ dev->remote = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
}
void saa7134_input_fini(struct saa7134_dev *dev)
@@ -318,8 +361,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
if (NULL == dev->remote)
return;
- if (dev->remote->polling)
- del_timer_sync(&dev->remote->timer);
+ saa7134_ir_stop(dev);
input_unregister_device(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
@@ -335,6 +377,7 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
switch (dev->board) {
case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV");
if (pinnacle_remote == 0) {
ir->get_key = get_key_pinnacle_color;
@@ -349,6 +392,11 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
ir->get_key = get_key_purpletv;
ir->ir_codes = ir_codes_purpletv;
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110");
+ ir->get_key = get_key_hvr1110;
+ ir->ir_codes = ir_codes_hauppauge_new;
+ break;
default:
dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board);
break;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 7cf96b4..e88ad7b 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -227,6 +227,11 @@ struct saa7134_format {
#define SAA7134_BOARD_PROTEUS_2309 98
#define SAA7134_BOARD_AVERMEDIA_A16AR 99
#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
+#define SAA7134_BOARD_PINNACLE_PCTV_310i 101
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103
+#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104
+#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -446,6 +451,9 @@ struct saa7134_dev {
struct v4l2_prio_state prio;
#endif
+ /* insmod option/autodetected */
+ int autodetected;
+
/* various device info */
unsigned int resources;
struct video_device *video_dev;
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index 6d1ef1e..a1ec3ac 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -687,7 +687,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
if (stv680->sbuf[i].data == NULL) {
PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
- return -1;
+ goto nomem_err;
}
}
@@ -698,7 +698,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
if (stv680->scratch[i].data == NULL) {
PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
- return -1;
+ goto nomem_err;
}
stv680->scratch[i].state = BUFFER_UNUSED;
}
@@ -706,7 +706,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
for (i = 0; i < STV680_NUMSBUF; i++) {
urb = usb_alloc_urb (0, GFP_KERNEL);
if (!urb)
- return -ENOMEM;
+ goto nomem_err;
/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
usb_fill_bulk_urb (urb, stv680->udev,
@@ -721,6 +721,21 @@ static int stv680_start_stream (struct usb_stv *stv680)
stv680->framecount = 0;
return 0;
+
+ nomem_err:
+ for (i = 0; i < STV680_NUMSCRATCH; i++) {
+ kfree(stv680->scratch[i].data);
+ stv680->scratch[i].data = NULL;
+ }
+ for (i = 0; i < STV680_NUMSBUF; i++) {
+ usb_kill_urb(stv680->urb[i]);
+ usb_free_urb(stv680->urb[i]);
+ stv680->urb[i] = NULL;
+ kfree(stv680->sbuf[i].data);
+ stv680->sbuf[i].data = NULL;
+ }
+ return -ENOMEM;
+
}
static int stv680_stop_stream (struct usb_stv *stv680)
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index 87ffb0e..fde576f 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -482,6 +482,12 @@ static int tda9887_set_config(struct tuner *t, char *buf)
buf[1] &= ~cQSS;
if (t->tda9887_config & TDA9887_GATING_18)
buf[3] &= ~cGating_36;
+
+ if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
+ radio_stereo.e &= ~cTunerGainLow;
+ radio_mono.e &= ~cTunerGainLow;
+ }
+
return 0;
}
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 40590ba..705daaa 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -443,6 +443,10 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
printk("%02x ",buffer[i]);
printk("\n");
}
+ /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
+ if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
+ return -ENODEV;
+
/* autodetection code based on the i2c addr */
if (!no_autodetect) {
switch (addr) {
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
index 63db4e9..1b9b074 100644
--- a/drivers/media/video/tuner-simple.c
+++ b/drivers/media/video/tuner-simple.c
@@ -108,6 +108,7 @@ static int tuner_stereo(struct i2c_client *c)
case TUNER_PHILIPS_FM1216ME_MK3:
case TUNER_PHILIPS_FM1236_MK3:
case TUNER_PHILIPS_FM1256_IH3:
+ case TUNER_LG_NTSC_TAPE:
stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
break;
default:
@@ -421,6 +422,7 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
case TUNER_PHILIPS_FM1216ME_MK3:
case TUNER_PHILIPS_FM1236_MK3:
case TUNER_PHILIPS_FMD1216ME_MK3:
+ case TUNER_LG_NTSC_TAPE:
buffer[3] = 0x19;
break;
case TUNER_TNF_5335MF:
@@ -465,6 +467,8 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
config |= TDA9887_INTERCARRIER;
/* if (params->port1_set_for_fm_mono)
config &= ~TDA9887_PORT1_ACTIVE;*/
+ if (params->fm_gain_normal)
+ config |= TDA9887_GAIN_NORMAL;
i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config);
}
if (4 != (rc = i2c_master_send(c,buffer,4)))
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c
index 7816823..74c3e6f 100644
--- a/drivers/media/video/tuner-types.c
+++ b/drivers/media/video/tuner-types.c
@@ -651,6 +651,7 @@ static struct tuner_params tuner_microtune_4049_fm5_params[] = {
.has_tda9887 = 1,
.port1_invert_for_secam_lc = 1,
.default_pll_gating_18 = 1,
+ .fm_gain_normal=1,
},
};
@@ -672,16 +673,6 @@ static struct tuner_params tuner_panasonic_vp27_params[] = {
},
};
-/* ------------ TUNER_LG_NTSC_TAPE - LGINNOTEK NTSC ------------ */
-
-static struct tuner_params tuner_lg_ntsc_tape_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_fm1236_mk3_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
- },
-};
-
/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */
static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = {
@@ -1331,8 +1322,8 @@ struct tunertype tuners[] = {
},
[TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */
.name = "LG NTSC (TAPE series)",
- .params = tuner_lg_ntsc_tape_params,
- .count = ARRAY_SIZE(tuner_lg_ntsc_tape_params),
+ .params = tuner_fm1236_mk3_params,
+ .count = ARRAY_SIZE(tuner_fm1236_mk3_params),
},
[TUNER_TNF_8831BGFF] = { /* Philips PAL */
.name = "Tenna TNF 8831 BGFF)",
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 6b9ef73..2624e3f 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -430,7 +430,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tvee->has_radio = eeprom_data[i+len-1];
/* old style tag, don't know how to detect
IR presence, mark as unknown. */
- tvee->has_ir = 2;
+ tvee->has_ir = -1;
tvee->model =
eeprom_data[i+8] +
(eeprom_data[i+9] << 8);
@@ -653,13 +653,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
STRM(decoderIC, tvee->decoder_processor),
tvee->decoder_processor);
}
- if (tvee->has_ir == 2)
+ if (tvee->has_ir == -1)
tveeprom_info("has %sradio\n",
tvee->has_radio ? "" : "no ");
else
- tveeprom_info("has %sradio, has %sIR remote\n",
+ tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n",
tvee->has_radio ? "" : "no ",
- tvee->has_ir ? "" : "no ");
+ (tvee->has_ir & 1) ? "" : "no ",
+ (tvee->has_ir & 2) ? "" : "no ");
}
EXPORT_SYMBOL(tveeprom_hauppauge_analog);
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index bbf2bee..ec0ff22 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -86,6 +86,7 @@ MODULE_DEVICE_TABLE(usb, qcm_table);
static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
{
struct input_dev *input_dev;
+ int error;
usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
@@ -106,7 +107,13 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
input_dev->private = cam;
- input_register_device(cam->input);
+ error = input_register_device(cam->input);
+ if (error) {
+ warn("Failed to register camera's input device, err: %d\n",
+ error);
+ input_free_device(cam->input);
+ cam->input = NULL;
+ }
}
static void qcm_unregister_input(struct qcm *cam)
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index d8b8802..b560c9d 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -690,7 +690,7 @@ int usbvideo_register(
}
base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo);
- cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL);
+ cams = kzalloc(base_size, GFP_KERNEL);
if (cams == NULL) {
err("Failed to allocate %d. bytes for usbvideo struct", base_size);
return -ENOMEM;
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
new file mode 100644
index 0000000..fc24ef0
--- /dev/null
+++ b/drivers/media/video/usbvision/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_USBVISION
+ tristate "USB video devices based on Nogatech NT1003/1004/1005"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ There are more than 50 different USB video devices based on
+ NT1003/1004/1005 USB Bridges. This driver enables using those
+ devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbvision.
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
new file mode 100644
index 0000000..9ac92a8
--- /dev/null
+++ b/drivers/media/video/usbvision/Makefile
@@ -0,0 +1,5 @@
+usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o
+
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
new file mode 100644
index 0000000..134eb98
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -0,0 +1,157 @@
+/*
+ * USBVISION.H
+ * usbvision header file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/list.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <media/tuner.h>
+#include "usbvision.h"
+
+/* Supported Devices: A table for usbvision.c*/
+struct usbvision_device_data_st usbvision_device_data[] = {
+ {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"},
+ {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"},
+ {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"},
+ {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"},
+ {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"},
+ {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"},
+ {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"},
+ {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"},
+ {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"},
+ {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"},
+ {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"},
+ {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"},
+ {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"},
+ {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"},
+ {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"},
+ {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
+ {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"},
+ {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "},
+ {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"},
+ {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
+ {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
+ {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"},
+ {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"},
+ {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"},
+ {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"},
+ {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"},
+ {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"},
+ {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"},
+ {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"},
+ {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"},
+ {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"},
+ {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"},
+ {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"},
+ {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"},
+ {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"},
+ {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"},
+ {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"},
+ {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"},
+ {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"},
+ {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"},
+ {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"},
+ {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"},
+ {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"},
+ {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"},
+ {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"},
+ {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"},
+ {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"},
+ {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"},
+ {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"},
+ {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"},
+ {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"},
+ {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"},
+ {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"},
+ {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"},
+ {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"},
+ {0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
+ {} /* Terminating entry */
+};
+
+/* Supported Devices */
+
+struct usb_device_id usbvision_table [] = {
+ { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */
+ { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */
+ { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */
+ { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */
+ { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */
+ { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */
+ { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */
+ { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */
+ { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */
+ { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */
+ { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */
+ { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */
+ { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */
+ { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */
+ { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */
+ { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */
+ { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */
+ { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */
+ { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */
+ { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */
+ { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */
+ { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */
+ { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */
+ { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/
+ { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */
+ { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/
+ { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */
+ { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/
+ { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */
+ { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */
+ { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */
+ { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */
+ { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */
+ { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */
+ { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */
+ { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */
+ { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */
+ { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */
+ { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */
+ { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */
+ { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */
+ { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */
+ { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */
+ { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */
+ { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */
+ { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */
+ { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */
+ { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */
+ { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
+ { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */
+ { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
+ { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */
+ { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */
+ { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */
+ { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */
+
+ { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usbvision_table);
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
new file mode 100644
index 0000000..797b97b
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -0,0 +1,2554 @@
+/*
+ * usbvision-core.c - driver for NT100x USB video capture devices
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/sched.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/videodev2.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/audiochip.h>
+
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "usbvision.h"
+
+static unsigned int core_debug = 0;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int force_testpattern = 0;
+module_param(force_testpattern,int,0644);
+MODULE_PARM_DESC(force_testpattern,"enable test pattern display [core]");
+
+static int adjustCompression = 1; // Set the compression to be adaptive
+module_param(adjustCompression, int, 0444);
+MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device. Default: 1 (On)");
+
+static int SwitchSVideoInput = 0; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently.
+module_param(SwitchSVideoInput, int, 0444);
+MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)");
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+ #define PDEBUG(level, fmt, args...) \
+ if (core_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+ #define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+#define DBG_HEADER 1<<0
+#define DBG_IRQ 1<<1
+#define DBG_ISOC 1<<2
+#define DBG_PARSE 1<<3
+#define DBG_SCRATCH 1<<4
+#define DBG_FUNC 1<<5
+
+static const int max_imgwidth = MAX_FRAME_WIDTH;
+static const int max_imgheight = MAX_FRAME_HEIGHT;
+static const int min_imgwidth = MIN_FRAME_WIDTH;
+static const int min_imgheight = MIN_FRAME_HEIGHT;
+
+/* The value of 'scratch_buf_size' affects quality of the picture
+ * in many ways. Shorter buffers may cause loss of data when client
+ * is too slow. Larger buffers are memory-consuming and take longer
+ * to work with. This setting can be adjusted, but the default value
+ * should be OK for most desktop users.
+ */
+#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) // 128kB memory scratch buffer
+static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE;
+
+// Function prototypes
+static int usbvision_request_intra (struct usb_usbvision *usbvision);
+static int usbvision_unrequest_intra (struct usb_usbvision *usbvision);
+static int usbvision_adjust_compression (struct usb_usbvision *usbvision);
+static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/*
+ * Here we want the physical address of the memory.
+ * This is used when initializing the contents of the area.
+ */
+
+void *usbvision_rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+void usbvision_rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ size = PAGE_ALIGN(size);
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vfree(mem);
+}
+
+
+
+#if ENABLE_HEXDUMP
+static void usbvision_hexdump(const unsigned char *data, int len)
+{
+ char tmp[80];
+ int i, k;
+
+ for (i = k = 0; len > 0; i++, len--) {
+ if (i > 0 && (i % 16 == 0)) {
+ printk("%s\n", tmp);
+ k = 0;
+ }
+ k += sprintf(&tmp[k], "%02x ", data[i]);
+ }
+ if (k > 0)
+ printk("%s\n", tmp);
+}
+#endif
+
+/********************************
+ * scratch ring buffer handling
+ ********************************/
+int scratch_len(struct usb_usbvision *usbvision) /*This returns the amount of data actually in the buffer */
+{
+ int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr;
+ if (len < 0) {
+ len += scratch_buf_size;
+ }
+ PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len);
+
+ return len;
+}
+
+
+/* This returns the free space left in the buffer */
+int scratch_free(struct usb_usbvision *usbvision)
+{
+ int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr;
+ if (free <= 0) {
+ free += scratch_buf_size;
+ }
+ if (free) {
+ free -= 1; /* at least one byte in the buffer must */
+ /* left blank, otherwise there is no chance to differ between full and empty */
+ }
+ PDEBUG(DBG_SCRATCH, "return %d\n", free);
+
+ return free;
+}
+
+
+/* This puts data into the buffer */
+int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, int len)
+{
+ int len_part;
+
+ if (usbvision->scratch_write_ptr + len < scratch_buf_size) {
+ memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len);
+ usbvision->scratch_write_ptr += len;
+ }
+ else {
+ len_part = scratch_buf_size - usbvision->scratch_write_ptr;
+ memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part);
+ if (len == len_part) {
+ usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */
+ }
+ else {
+ memcpy(usbvision->scratch, data + len_part, len - len_part);
+ usbvision->scratch_write_ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr);
+
+ return len;
+}
+
+/* This marks the write_ptr as position of new frame header */
+void scratch_mark_header(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr);
+
+ usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] =
+ usbvision->scratch_write_ptr;
+ usbvision->scratch_headermarker_write_ptr += 1;
+ usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER;
+}
+
+/* This gets data from the buffer at the given "ptr" position */
+int scratch_get_extra(struct usb_usbvision *usbvision, unsigned char *data, int *ptr, int len)
+{
+ int len_part;
+ if (*ptr + len < scratch_buf_size) {
+ memcpy(data, usbvision->scratch + *ptr, len);
+ *ptr += len;
+ }
+ else {
+ len_part = scratch_buf_size - *ptr;
+ memcpy(data, usbvision->scratch + *ptr, len_part);
+ if (len == len_part) {
+ *ptr = 0; /* just set the y_ptr to zero */
+ }
+ else {
+ memcpy(data + len_part, usbvision->scratch, len - len_part);
+ *ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr);
+
+ return len;
+}
+
+
+/* This sets the scratch extra read pointer */
+void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, int len)
+{
+ *ptr = (usbvision->scratch_read_ptr + len)%scratch_buf_size;
+
+ PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/*This increments the scratch extra read pointer */
+void scratch_inc_extra_ptr(int *ptr, int len)
+{
+ *ptr = (*ptr + len) % scratch_buf_size;
+
+ PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/* This gets data from the buffer */
+int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, int len)
+{
+ int len_part;
+ if (usbvision->scratch_read_ptr + len < scratch_buf_size) {
+ memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len);
+ usbvision->scratch_read_ptr += len;
+ }
+ else {
+ len_part = scratch_buf_size - usbvision->scratch_read_ptr;
+ memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part);
+ if (len == len_part) {
+ usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */
+ }
+ else {
+ memcpy(data + len_part, usbvision->scratch, len - len_part);
+ usbvision->scratch_read_ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr);
+
+ return len;
+}
+
+
+/* This sets read pointer to next header and returns it */
+int scratch_get_header(struct usb_usbvision *usbvision,struct usbvision_frame_header *header)
+{
+ int errCode = 0;
+
+ PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr);
+
+ while (usbvision->scratch_headermarker_write_ptr -
+ usbvision->scratch_headermarker_read_ptr != 0) {
+ usbvision->scratch_read_ptr =
+ usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr];
+ usbvision->scratch_headermarker_read_ptr += 1;
+ usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER;
+ scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH);
+ if ((header->magic_1 == USBVISION_MAGIC_1)
+ && (header->magic_2 == USBVISION_MAGIC_2)
+ && (header->headerLength == USBVISION_HEADER_LENGTH)) {
+ errCode = USBVISION_HEADER_LENGTH;
+ header->frameWidth = header->frameWidthLo + (header->frameWidthHi << 8);
+ header->frameHeight = header->frameHeightLo + (header->frameHeightHi << 8);
+ break;
+ }
+ }
+
+ return errCode;
+}
+
+
+/*This removes len bytes of old data from the buffer */
+void scratch_rm_old(struct usb_usbvision *usbvision, int len)
+{
+
+ usbvision->scratch_read_ptr += len;
+ usbvision->scratch_read_ptr %= scratch_buf_size;
+ PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr);
+}
+
+
+/*This resets the buffer - kills all data in it too */
+void scratch_reset(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_SCRATCH, "\n");
+
+ usbvision->scratch_read_ptr = 0;
+ usbvision->scratch_write_ptr = 0;
+ usbvision->scratch_headermarker_read_ptr = 0;
+ usbvision->scratch_headermarker_write_ptr = 0;
+ usbvision->isocstate = IsocState_NoFrame;
+}
+
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision)
+{
+ usbvision->scratch = vmalloc(scratch_buf_size);
+ scratch_reset(usbvision);
+ if(usbvision->scratch == NULL) {
+ err("%s: unable to allocate %d bytes for scratch",
+ __FUNCTION__, scratch_buf_size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void usbvision_scratch_free(struct usb_usbvision *usbvision)
+{
+ if (usbvision->scratch != NULL) {
+ vfree(usbvision->scratch);
+ usbvision->scratch = NULL;
+ }
+}
+
+/*
+ * usbvision_testpattern()
+ *
+ * Procedure forms a test pattern (yellow grid on blue background).
+ *
+ * Parameters:
+ * fullframe: if TRUE then entire frame is filled, otherwise the procedure
+ * continues from the current scanline.
+ * pmode 0: fill the frame with solid blue color (like on VCR or TV)
+ * 1: Draw a colored grid
+ *
+ */
+void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe,
+ int pmode)
+{
+ static const char proc[] = "usbvision_testpattern";
+ struct usbvision_frame *frame;
+ unsigned char *f;
+ int num_cell = 0;
+ int scan_length = 0;
+ static int num_pass = 0;
+
+ if (usbvision == NULL) {
+ printk(KERN_ERR "%s: usbvision == NULL\n", proc);
+ return;
+ }
+ if (usbvision->curFrame == NULL) {
+ printk(KERN_ERR "%s: usbvision->curFrame is NULL.\n", proc);
+ return;
+ }
+
+ /* Grab the current frame */
+ frame = usbvision->curFrame;
+
+ /* Optionally start at the beginning */
+ if (fullframe) {
+ frame->curline = 0;
+ frame->scanlength = 0;
+ }
+
+ /* Form every scan line */
+ for (; frame->curline < frame->frmheight; frame->curline++) {
+ int i;
+
+ f = frame->data + (usbvision->curwidth * 3 * frame->curline);
+ for (i = 0; i < usbvision->curwidth; i++) {
+ unsigned char cb = 0x80;
+ unsigned char cg = 0;
+ unsigned char cr = 0;
+
+ if (pmode == 1) {
+ if (frame->curline % 32 == 0)
+ cb = 0, cg = cr = 0xFF;
+ else if (i % 32 == 0) {
+ if (frame->curline % 32 == 1)
+ num_cell++;
+ cb = 0, cg = cr = 0xFF;
+ } else {
+ cb =
+ ((num_cell * 7) +
+ num_pass) & 0xFF;
+ cg =
+ ((num_cell * 5) +
+ num_pass * 2) & 0xFF;
+ cr =
+ ((num_cell * 3) +
+ num_pass * 3) & 0xFF;
+ }
+ } else {
+ /* Just the blue screen */
+ }
+
+ *f++ = cb;
+ *f++ = cg;
+ *f++ = cr;
+ scan_length += 3;
+ }
+ }
+
+ frame->grabstate = FrameState_Done;
+ frame->scanlength += scan_length;
+ ++num_pass;
+
+}
+
+/*
+ * usbvision_decompress_alloc()
+ *
+ * allocates intermediate buffer for decompression
+ */
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision)
+{
+ int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
+ usbvision->IntraFrameBuffer = vmalloc(IFB_size);
+ if (usbvision->IntraFrameBuffer == NULL) {
+ err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * usbvision_decompress_free()
+ *
+ * frees intermediate buffer for decompression
+ */
+void usbvision_decompress_free(struct usb_usbvision *usbvision)
+{
+ if (usbvision->IntraFrameBuffer != NULL) {
+ vfree(usbvision->IntraFrameBuffer);
+ usbvision->IntraFrameBuffer = NULL;
+ }
+}
+
+/************************************************************
+ * Here comes the data parsing stuff that is run as interrupt
+ ************************************************************/
+/*
+ * usbvision_find_header()
+ *
+ * Locate one of supported header markers in the scratch buffer.
+ */
+static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision)
+{
+ struct usbvision_frame *frame;
+ int foundHeader = 0;
+
+ frame = usbvision->curFrame;
+
+ while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) {
+ // found header in scratch
+ PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u",
+ frame->isocHeader.magic_2,
+ frame->isocHeader.magic_1,
+ frame->isocHeader.headerLength,
+ frame->isocHeader.frameNum,
+ frame->isocHeader.framePhase,
+ frame->isocHeader.frameLatency,
+ frame->isocHeader.dataFormat,
+ frame->isocHeader.formatParam,
+ frame->isocHeader.frameWidth,
+ frame->isocHeader.frameHeight);
+
+ if (usbvision->requestIntra) {
+ if (frame->isocHeader.formatParam & 0x80) {
+ foundHeader = 1;
+ usbvision->lastIsocFrameNum = -1; // do not check for lost frames this time
+ usbvision_unrequest_intra(usbvision);
+ break;
+ }
+ }
+ else {
+ foundHeader = 1;
+ break;
+ }
+ }
+
+ if (foundHeader) {
+ frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width;
+ frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height;
+ frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3;
+ }
+ else { // no header found
+ PDEBUG(DBG_HEADER, "skipping scratch data, no header");
+ scratch_reset(usbvision);
+ return ParseState_EndParse;
+ }
+
+ // found header
+ if (frame->isocHeader.dataFormat==ISOC_MODE_COMPRESS) {
+ //check isocHeader.frameNum for lost frames
+ if (usbvision->lastIsocFrameNum >= 0) {
+ if (((usbvision->lastIsocFrameNum + 1) % 32) != frame->isocHeader.frameNum) {
+ // unexpected frame drop: need to request new intra frame
+ PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isocHeader.frameNum);
+ usbvision_request_intra(usbvision);
+ return ParseState_NextFrame;
+ }
+ }
+ usbvision->lastIsocFrameNum = frame->isocHeader.frameNum;
+ }
+ usbvision->header_count++;
+ frame->scanstate = ScanState_Lines;
+ frame->curline = 0;
+
+ if (force_testpattern) {
+ usbvision_testpattern(usbvision, 1, 1);
+ return ParseState_NextFrame;
+ }
+ return ParseState_Continue;
+}
+
+static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+ volatile struct usbvision_frame *frame;
+ unsigned char *f;
+ int len;
+ int i;
+ unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components
+ unsigned char rv, gv, bv; // RGB components
+ int clipmask_index, bytes_per_pixel;
+ int stretch_bytes, clipmask_add;
+
+ frame = usbvision->curFrame;
+ f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+ /* Make sure there's enough data for the entire line */
+ len = (frame->isocHeader.frameWidth * 2)+5;
+ if (scratch_len(usbvision) < len) {
+ PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len);
+ return ParseState_Out;
+ }
+
+ if ((frame->curline + 1) >= frame->frmheight) {
+ return ParseState_NextFrame;
+ }
+
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+ clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+ clipmask_add = usbvision->stretch_width;
+
+ for (i = 0; i < frame->frmwidth; i+=(2 * usbvision->stretch_width)) {
+
+ scratch_get(usbvision, &yuyv[0], 4);
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = yuyv[0]; // Y
+ *f++ = yuyv[3]; // U
+ }
+ else {
+
+ YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+ *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+ *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+ break;
+ }
+ }
+ clipmask_index += clipmask_add;
+ f += stretch_bytes;
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = yuyv[2]; // Y
+ *f++ = yuyv[1]; // V
+ }
+ else {
+
+ YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+ *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+ *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+ break;
+ }
+ }
+ clipmask_index += clipmask_add;
+ f += stretch_bytes;
+ }
+
+ frame->curline += usbvision->stretch_height;
+ *pcopylen += frame->v4l2_linesize * usbvision->stretch_height;
+
+ if (frame->curline >= frame->frmheight) {
+ return ParseState_NextFrame;
+ }
+ else {
+ return ParseState_Continue;
+ }
+}
+
+/* The decompression routine */
+static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed,
+ unsigned char *Decompressed, int *StartPos,
+ int *BlockTypeStartPos, int Len)
+{
+ int RestPixel, Idx, MaxPos, Pos, ExtraPos, BlockLen, BlockTypePos, BlockTypeLen;
+ unsigned char BlockByte, BlockCode, BlockType, BlockTypeByte, Integrator;
+
+ Integrator = 0;
+ Pos = *StartPos;
+ BlockTypePos = *BlockTypeStartPos;
+ MaxPos = 396; //Pos + Len;
+ ExtraPos = Pos;
+ BlockLen = 0;
+ BlockByte = 0;
+ BlockCode = 0;
+ BlockType = 0;
+ BlockTypeByte = 0;
+ BlockTypeLen = 0;
+ RestPixel = Len;
+
+ for (Idx = 0; Idx < Len; Idx++) {
+
+ if (BlockLen == 0) {
+ if (BlockTypeLen==0) {
+ BlockTypeByte = Compressed[BlockTypePos];
+ BlockTypePos++;
+ BlockTypeLen = 4;
+ }
+ BlockType = (BlockTypeByte & 0xC0) >> 6;
+
+ //statistic:
+ usbvision->ComprBlockTypes[BlockType]++;
+
+ Pos = ExtraPos;
+ if (BlockType == 0) {
+ if(RestPixel >= 24) {
+ Idx += 23;
+ RestPixel -= 24;
+ Integrator = Decompressed[Idx];
+ } else {
+ Idx += RestPixel - 1;
+ RestPixel = 0;
+ }
+ } else {
+ BlockCode = Compressed[Pos];
+ Pos++;
+ if (RestPixel >= 24) {
+ BlockLen = 24;
+ } else {
+ BlockLen = RestPixel;
+ }
+ RestPixel -= BlockLen;
+ ExtraPos = Pos + (BlockLen / 4);
+ }
+ BlockTypeByte <<= 2;
+ BlockTypeLen -= 1;
+ }
+ if (BlockLen > 0) {
+ if ((BlockLen%4) == 0) {
+ BlockByte = Compressed[Pos];
+ Pos++;
+ }
+ if (BlockType == 1) { //inter Block
+ Integrator = Decompressed[Idx];
+ }
+ switch (BlockByte & 0xC0) {
+ case 0x03<<6:
+ Integrator += Compressed[ExtraPos];
+ ExtraPos++;
+ break;
+ case 0x02<<6:
+ Integrator += BlockCode;
+ break;
+ case 0x00:
+ Integrator -= BlockCode;
+ break;
+ }
+ Decompressed[Idx] = Integrator;
+ BlockByte <<= 2;
+ BlockLen -= 1;
+ }
+ }
+ *StartPos = ExtraPos;
+ *BlockTypeStartPos = BlockTypePos;
+ return Idx;
+}
+
+
+/*
+ * usbvision_parse_compress()
+ *
+ * Parse compressed frame from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+#define USBVISION_STRIP_MAGIC 0x5A
+#define USBVISION_STRIP_LEN_MAX 400
+#define USBVISION_STRIP_HEADER_LEN 3
+
+ struct usbvision_frame *frame;
+ unsigned char *f,*u = NULL ,*v = NULL;
+ unsigned char StripData[USBVISION_STRIP_LEN_MAX];
+ unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN];
+ int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos;
+ int clipmask_index, bytes_per_pixel, rc;
+ int imageSize;
+ unsigned char rv, gv, bv;
+ static unsigned char *Y, *U, *V;
+
+ frame = usbvision->curFrame;
+ imageSize = frame->frmwidth * frame->frmheight;
+ if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) ||
+ (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) { // this is a planar format
+ //... v4l2_linesize not used here.
+ f = frame->data + (frame->width * frame->curline);
+ } else
+ f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers
+ // get base of u and b planes add halfoffset
+
+ u = frame->data
+ + imageSize
+ + (frame->frmwidth >>1) * frame->curline ;
+ v = u + (imageSize >>1 );
+
+ } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){
+
+ v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ;
+ u = v + (imageSize >>2) ;
+ }
+
+ if (frame->curline == 0) {
+ usbvision_adjust_compression(usbvision);
+ }
+
+ if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) {
+ return ParseState_Out;
+ }
+
+ //get strip header without changing the scratch_read_ptr
+ scratch_set_extra_ptr(usbvision, &StripPtr, 0);
+ scratch_get_extra(usbvision, &StripHeader[0], &StripPtr,
+ USBVISION_STRIP_HEADER_LEN);
+
+ if (StripHeader[0] != USBVISION_STRIP_MAGIC) {
+ // wrong strip magic
+ usbvision->stripMagicErrors++;
+ return ParseState_NextFrame;
+ }
+
+ if (frame->curline != (int)StripHeader[2]) {
+ //line number missmatch error
+ usbvision->stripLineNumberErrors++;
+ }
+
+ StripLen = 2 * (unsigned int)StripHeader[1];
+ if (StripLen > USBVISION_STRIP_LEN_MAX) {
+ // strip overrun
+ // I think this never happens
+ usbvision_request_intra(usbvision);
+ }
+
+ if (scratch_len(usbvision) < StripLen) {
+ //there is not enough data for the strip
+ return ParseState_Out;
+ }
+
+ if (usbvision->IntraFrameBuffer) {
+ Y = usbvision->IntraFrameBuffer + frame->frmwidth * frame->curline;
+ U = usbvision->IntraFrameBuffer + imageSize + (frame->frmwidth / 2) * (frame->curline / 2);
+ V = usbvision->IntraFrameBuffer + imageSize / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2);
+ }
+ else {
+ return ParseState_NextFrame;
+ }
+
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+
+ scratch_get(usbvision, StripData, StripLen);
+
+ IdxEnd = frame->frmwidth;
+ BlockTypePos = USBVISION_STRIP_HEADER_LEN;
+ StartBlockPos = BlockTypePos + (IdxEnd - 1) / 96 + (IdxEnd / 2 - 1) / 96 + 2;
+ BlockPos = StartBlockPos;
+
+ usbvision->BlockPos = BlockPos;
+
+ if ((rc = usbvision_decompress(usbvision, StripData, Y, &BlockPos, &BlockTypePos, IdxEnd)) != IdxEnd) {
+ //return ParseState_Continue;
+ }
+ if (StripLen > usbvision->maxStripLen) {
+ usbvision->maxStripLen = StripLen;
+ }
+
+ if (frame->curline%2) {
+ if ((rc = usbvision_decompress(usbvision, StripData, V, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
+ //return ParseState_Continue;
+ }
+ }
+ else {
+ if ((rc = usbvision_decompress(usbvision, StripData, U, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
+ //return ParseState_Continue;
+ }
+ }
+
+ if (BlockPos > usbvision->comprBlockPos) {
+ usbvision->comprBlockPos = BlockPos;
+ }
+ if (BlockPos > StripLen) {
+ usbvision->stripLenErrors++;
+ }
+
+ for (Idx = 0; Idx < IdxEnd; Idx++) {
+ if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = Y[Idx];
+ *f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2];
+ }
+ else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) {
+ *f++ = Y[Idx];
+ if ( Idx & 0x01)
+ *u++ = U[Idx>>1] ;
+ else
+ *v++ = V[Idx>>1];
+ }
+ else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
+ *f++ = Y [Idx];
+ if ( !(( Idx & 0x01 ) | ( frame->curline & 0x01 )) ){
+
+/* only need do this for 1 in 4 pixels */
+/* intraframe buffer is YUV420 format */
+
+ *u++ = U[Idx >>1];
+ *v++ = V[Idx >>1];
+ }
+
+ }
+ else {
+ YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_GREY:
+ *f++ = Y[Idx];
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+ *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+ *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+ f++;
+ break;
+ }
+ }
+ clipmask_index++;
+ }
+ /* Deal with non-integer no. of bytes for YUV420P */
+ if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 )
+ *pcopylen += frame->v4l2_linesize;
+ else
+ *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1;
+
+ frame->curline += 1;
+
+ if (frame->curline >= frame->frmheight) {
+ return ParseState_NextFrame;
+ }
+ else {
+ return ParseState_Continue;
+ }
+
+}
+
+
+/*
+ * usbvision_parse_lines_420()
+ *
+ * Parse two lines from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+ struct usbvision_frame *frame;
+ unsigned char *f_even = NULL, *f_odd = NULL;
+ unsigned int pixel_per_line, block;
+ int pixel, block_split;
+ int y_ptr, u_ptr, v_ptr, y_odd_offset;
+ const int y_block_size = 128;
+ const int uv_block_size = 64;
+ const int sub_block_size = 32;
+ const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4;
+ const int uv_step[]= { 0, 0, 0, 4 }, uv_step_size = 4;
+ unsigned char y[2], u, v; /* YUV components */
+ int y_, u_, v_, vb, uvg, ur;
+ int r_, g_, b_; /* RGB components */
+ unsigned char g;
+ int clipmask_even_index, clipmask_odd_index, bytes_per_pixel;
+ int clipmask_add, stretch_bytes;
+
+ frame = usbvision->curFrame;
+ f_even = frame->data + (frame->v4l2_linesize * frame->curline);
+ f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height;
+
+ /* Make sure there's enough data for the entire line */
+ /* In this mode usbvision transfer 3 bytes for every 2 pixels */
+ /* I need two lines to decode the color */
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+ clipmask_even_index = frame->curline * MAX_FRAME_WIDTH;
+ clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH;
+ clipmask_add = usbvision->stretch_width;
+ pixel_per_line = frame->isocHeader.frameWidth;
+
+ if (scratch_len(usbvision) < (int)pixel_per_line * 3) {
+ //printk(KERN_DEBUG "out of data, need %d\n", len);
+ return ParseState_Out;
+ }
+
+ if ((frame->curline + 1) >= frame->frmheight) {
+ return ParseState_NextFrame;
+ }
+
+ block_split = (pixel_per_line%y_block_size) ? 1 : 0; //are some blocks splitted into different lines?
+
+ y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
+ + block_split * uv_block_size;
+
+ scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset);
+ scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size);
+ scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset
+ + (4 - block_split) * sub_block_size);
+
+ for (block = 0; block < (pixel_per_line / sub_block_size);
+ block++) {
+
+
+ for (pixel = 0; pixel < sub_block_size; pixel +=2) {
+ scratch_get(usbvision, &y[0], 2);
+ scratch_get_extra(usbvision, &u, &u_ptr, 1);
+ scratch_get_extra(usbvision, &v, &v_ptr, 1);
+
+ //I don't use the YUV_TO_RGB macro for better performance
+ v_ = v - 128;
+ u_ = u - 128;
+ vb = 132252 * v_;
+ uvg= -53281 * u_ - 25625 * v_;
+ ur = 104595 * u_;
+
+ if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_even++ = y[0];
+ *f_even++ = v;
+ }
+ else {
+ y_ = 76284 * (y[0] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg)>> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+ *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(b_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(r_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(b_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(r_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+ *f_even++ = (0x03 & ( g >> 6)) |
+ (0x7C & (LIMIT_RGB(r_) >> 1));
+ break;
+ }
+ }
+ clipmask_even_index += clipmask_add;
+ f_even += stretch_bytes;
+
+ if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_even++ = y[1];
+ *f_even++ = u;
+ }
+ else {
+ y_ = 76284 * (y[1] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg)>> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+ *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(b_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(r_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(b_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(r_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+ *f_even++ = (0x03 & ( g >> 6)) |
+ (0x7C & (LIMIT_RGB(r_) >> 1));
+ break;
+ }
+ }
+ clipmask_even_index += clipmask_add;
+ f_even += stretch_bytes;
+
+ scratch_get_extra(usbvision, &y[0], &y_ptr, 2);
+
+ if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_odd++ = y[0];
+ *f_odd++ = v;
+ }
+ else {
+ y_ = 76284 * (y[0] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg)>> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+ *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(b_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(r_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(b_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(r_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+ *f_odd++ = (0x03 & ( g >> 6)) |
+ (0x7C & (LIMIT_RGB(r_) >> 1));
+ break;
+ }
+ }
+ clipmask_odd_index += clipmask_add;
+ f_odd += stretch_bytes;
+
+ if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_odd++ = y[1];
+ *f_odd++ = u;
+ }
+ else {
+ y_ = 76284 * (y[1] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg)>> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+ *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(b_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(r_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(b_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(r_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+ *f_odd++ = (0x03 & ( g >> 6)) |
+ (0x7C & (LIMIT_RGB(r_) >> 1));
+ break;
+ }
+ }
+ clipmask_odd_index += clipmask_add;
+ f_odd += stretch_bytes;
+ }
+
+ scratch_rm_old(usbvision,y_step[block % y_step_size] * sub_block_size);
+ scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size]
+ * sub_block_size);
+ scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size]
+ * sub_block_size);
+ scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size]
+ * sub_block_size);
+ }
+
+ scratch_rm_old(usbvision, pixel_per_line * 3 / 2
+ + block_split * sub_block_size);
+
+ frame->curline += 2 * usbvision->stretch_height;
+ *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height;
+
+ if (frame->curline >= frame->frmheight)
+ return ParseState_NextFrame;
+ else
+ return ParseState_Continue;
+}
+
+/*
+ * usbvision_parse_data()
+ *
+ * Generic routine to parse the scratch buffer. It employs either
+ * usbvision_find_header() or usbvision_parse_lines() to do most
+ * of work.
+ *
+ */
+static void usbvision_parse_data(struct usb_usbvision *usbvision)
+{
+ struct usbvision_frame *frame;
+ enum ParseState newstate;
+ long copylen = 0;
+ unsigned long lock_flags;
+
+ frame = usbvision->curFrame;
+
+ PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision));
+
+ while (1) {
+
+ newstate = ParseState_Out;
+ if (scratch_len(usbvision)) {
+ if (frame->scanstate == ScanState_Scanning) {
+ newstate = usbvision_find_header(usbvision);
+ }
+ else if (frame->scanstate == ScanState_Lines) {
+ if (usbvision->isocMode == ISOC_MODE_YUV420) {
+ newstate = usbvision_parse_lines_420(usbvision, &copylen);
+ }
+ else if (usbvision->isocMode == ISOC_MODE_YUV422) {
+ newstate = usbvision_parse_lines_422(usbvision, &copylen);
+ }
+ else if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+ newstate = usbvision_parse_compress(usbvision, &copylen);
+ }
+
+ }
+ }
+ if (newstate == ParseState_Continue) {
+ continue;
+ }
+ else if ((newstate == ParseState_NextFrame) || (newstate == ParseState_Out)) {
+ break;
+ }
+ else {
+ return; /* ParseState_EndParse */
+ }
+ }
+
+ if (newstate == ParseState_NextFrame) {
+ frame->grabstate = FrameState_Done;
+ do_gettimeofday(&(frame->timestamp));
+ frame->sequence = usbvision->frame_num;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_move_tail(&(frame->frame), &usbvision->outqueue);
+ usbvision->curFrame = NULL;
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ usbvision->frame_num++;
+
+ /* This will cause the process to request another frame. */
+ if (waitqueue_active(&usbvision->wait_frame)) {
+ PDEBUG(DBG_PARSE, "Wake up !");
+ wake_up_interruptible(&usbvision->wait_frame);
+ }
+ }
+ else
+ frame->grabstate = FrameState_Grabbing;
+
+
+ /* Update the frame's uncompressed length. */
+ frame->scanlength += copylen;
+}
+
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int usbvision_compress_isochronous(struct usb_usbvision *usbvision,
+ struct urb *urb)
+{
+ unsigned char *packet_data;
+ int i, totlen = 0;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int packet_len = urb->iso_frame_desc[i].actual_length;
+ int packet_stat = urb->iso_frame_desc[i].status;
+
+ packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (packet_stat) { // packet_stat != 0 ?????????????
+ PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat);
+ usbvision->isocErrCount++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (packet_len < 0) {
+ PDEBUG(DBG_ISOC, "error packet [%d]", i);
+ usbvision->isocSkipCount++;
+ continue;
+ }
+ else if (packet_len == 0) { /* Frame end ????? */
+ PDEBUG(DBG_ISOC, "null packet [%d]", i);
+ usbvision->isocstate=IsocState_NoFrame;
+ usbvision->isocSkipCount++;
+ continue;
+ }
+ else if (packet_len > usbvision->isocPacketSize) {
+ PDEBUG(DBG_ISOC, "packet[%d] > isocPacketSize", i);
+ usbvision->isocSkipCount++;
+ continue;
+ }
+
+ PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len);
+
+ if (usbvision->isocstate==IsocState_NoFrame) { //new frame begins
+ usbvision->isocstate=IsocState_InFrame;
+ scratch_mark_header(usbvision);
+ usbvision_measure_bandwidth(usbvision);
+ PDEBUG(DBG_ISOC, "packet with header");
+ }
+
+ /*
+ * If usbvision continues to feed us with data but there is no
+ * consumption (if, for example, V4L client fell asleep) we
+ * may overflow the buffer. We have to move old data over to
+ * free room for new data. This is bad for old data. If we
+ * just drop new data then it's bad for new data... choose
+ * your favorite evil here.
+ */
+ if (scratch_free(usbvision) < packet_len) {
+
+ usbvision->scratch_ovf_count++;
+ PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d",
+ scratch_len(usbvision), packet_len);
+ scratch_rm_old(usbvision, packet_len - scratch_free(usbvision));
+ }
+
+ /* Now we know that there is enough room in scratch buffer */
+ scratch_put(usbvision, packet_data, packet_len);
+ totlen += packet_len;
+ usbvision->isocDataCount += packet_len;
+ usbvision->isocPacketCount++;
+ }
+#if ENABLE_HEXDUMP
+ if (totlen > 0) {
+ static int foo = 0;
+ if (foo < 1) {
+ printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen);
+ usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen);
+ ++foo;
+ }
+ }
+#endif
+ return totlen;
+}
+
+static void usbvision_isocIrq(struct urb *urb)
+{
+ int errCode = 0;
+ int len;
+ struct usb_usbvision *usbvision = urb->context;
+ int i;
+ unsigned long startTime = jiffies;
+ struct usbvision_frame **f;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return;
+
+ f = &usbvision->curFrame;
+
+ /* Manage streaming interruption */
+ if (usbvision->streaming == Stream_Interrupt) {
+ usbvision->streaming = Stream_Idle;
+ if ((*f)) {
+ (*f)->grabstate = FrameState_Ready;
+ (*f)->scanstate = ScanState_Scanning;
+ }
+ PDEBUG(DBG_IRQ, "stream interrupted");
+ wake_up_interruptible(&usbvision->wait_stream);
+ }
+
+ /* Copy the data received into our scratch buffer */
+ len = usbvision_compress_isochronous(usbvision, urb);
+
+ usbvision->isocUrbCount++;
+ usbvision->urb_length = len;
+
+ if (usbvision->streaming == Stream_On) {
+
+ /* If we collected enough data let's parse! */
+ if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) { /* 12 == header_length */
+ /*If we don't have a frame we're current working on, complain */
+ if(!list_empty(&(usbvision->inqueue))) {
+ if (!(*f)) {
+ (*f) = list_entry(usbvision->inqueue.next,struct usbvision_frame, frame);
+ }
+ usbvision_parse_data(usbvision);
+ }
+ else {
+ PDEBUG(DBG_IRQ, "received data, but no one needs it");
+ scratch_reset(usbvision);
+ }
+ }
+ }
+ else {
+ PDEBUG(DBG_IRQ, "received data, but no one needs it");
+ scratch_reset(usbvision);
+ }
+
+ usbvision->timeInIrq += jiffies - startTime;
+
+ for (i = 0; i < USBVISION_URB_FRAMES; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ urb->status = 0;
+ urb->dev = usbvision->dev;
+ errCode = usb_submit_urb (urb, GFP_ATOMIC);
+
+ /* Disable this warning. By design of the driver. */
+ // if(errCode) {
+ // err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode);
+ // }
+
+ return;
+}
+
+/*************************************/
+/* Low level usbvision access functions */
+/*************************************/
+
+/*
+ * usbvision_read_reg()
+ *
+ * return < 0 -> Error
+ * >= 0 -> Data
+ */
+
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
+{
+ int errCode = 0;
+ unsigned char buffer[1];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -1;
+
+ errCode = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0, (__u16) reg, buffer, 1, HZ);
+
+ if (errCode < 0) {
+ err("%s: failed: error %d", __FUNCTION__, errCode);
+ return errCode;
+ }
+ return buffer[0];
+}
+
+/*
+ * usbvision_write_reg()
+ *
+ * return 1 -> Reg written
+ * 0 -> usbvision is not yet ready
+ * -1 -> Something went wrong
+ */
+
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+ unsigned char value)
+{
+ int errCode = 0;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);
+
+ if (errCode < 0) {
+ err("%s: failed: error %d", __FUNCTION__, errCode);
+ }
+ return errCode;
+}
+
+
+static void usbvision_ctrlUrb_complete(struct urb *urb)
+{
+ struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context;
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->ctrlUrbBusy = 0;
+ if (waitqueue_active(&usbvision->ctrlUrb_wq)) {
+ wake_up_interruptible(&usbvision->ctrlUrb_wq);
+ }
+}
+
+
+static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address,
+ unsigned char *data, int len)
+{
+ int errCode = 0;
+
+ PDEBUG(DBG_IRQ, "");
+ if (len > 8) {
+ return -EFAULT;
+ }
+// down(&usbvision->ctrlUrbLock);
+ if (usbvision->ctrlUrbBusy) {
+// up(&usbvision->ctrlUrbLock);
+ return -EBUSY;
+ }
+ usbvision->ctrlUrbBusy = 1;
+// up(&usbvision->ctrlUrbLock);
+
+ usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ usbvision->ctrlUrbSetup.bRequest = USBVISION_OP_CODE;
+ usbvision->ctrlUrbSetup.wValue = 0;
+ usbvision->ctrlUrbSetup.wIndex = cpu_to_le16(address);
+ usbvision->ctrlUrbSetup.wLength = cpu_to_le16(len);
+ usb_fill_control_urb (usbvision->ctrlUrb, usbvision->dev,
+ usb_sndctrlpipe(usbvision->dev, 1),
+ (unsigned char *)&usbvision->ctrlUrbSetup,
+ (void *)usbvision->ctrlUrbBuffer, len,
+ usbvision_ctrlUrb_complete,
+ (void *)usbvision);
+
+ memcpy(usbvision->ctrlUrbBuffer, data, len);
+
+ errCode = usb_submit_urb(usbvision->ctrlUrb, GFP_ATOMIC);
+ if (errCode < 0) {
+ // error in usb_submit_urb()
+ usbvision->ctrlUrbBusy = 0;
+ }
+ PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, errCode);
+ return errCode;
+}
+
+
+static int usbvision_init_compression(struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+
+ usbvision->lastIsocFrameNum = -1;
+ usbvision->isocDataCount = 0;
+ usbvision->isocPacketCount = 0;
+ usbvision->isocSkipCount = 0;
+ usbvision->comprLevel = 50;
+ usbvision->lastComprLevel = -1;
+ usbvision->isocUrbCount = 0;
+ usbvision->requestIntra = 1;
+ usbvision->isocMeasureBandwidthCount = 0;
+
+ return errCode;
+}
+
+/* this function measures the used bandwidth since last call
+ * return: 0 : no error
+ * sets usedBandwidth to 1-100 : 1-100% of full bandwidth resp. to isocPacketSize
+ */
+static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+
+ if (usbvision->isocMeasureBandwidthCount < 2) { // this gives an average bandwidth of 3 frames
+ usbvision->isocMeasureBandwidthCount++;
+ return errCode;
+ }
+ if ((usbvision->isocPacketSize > 0) && (usbvision->isocPacketCount > 0)) {
+ usbvision->usedBandwidth = usbvision->isocDataCount /
+ (usbvision->isocPacketCount + usbvision->isocSkipCount) *
+ 100 / usbvision->isocPacketSize;
+ }
+ usbvision->isocMeasureBandwidthCount = 0;
+ usbvision->isocDataCount = 0;
+ usbvision->isocPacketCount = 0;
+ usbvision->isocSkipCount = 0;
+ return errCode;
+}
+
+static int usbvision_adjust_compression (struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+ unsigned char buffer[6];
+
+ PDEBUG(DBG_IRQ, "");
+ if ((adjustCompression) && (usbvision->usedBandwidth > 0)) {
+ usbvision->comprLevel += (usbvision->usedBandwidth - 90) / 2;
+ RESTRICT_TO_RANGE(usbvision->comprLevel, 0, 100);
+ if (usbvision->comprLevel != usbvision->lastComprLevel) {
+ int distorsion;
+ if (usbvision->bridgeType == BRIDGE_NT1004 || usbvision->bridgeType == BRIDGE_NT1005) {
+ buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM Threshold 1
+ buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM Threshold 2
+ distorsion = 7 + 248 * usbvision->comprLevel / 100;
+ buffer[2] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (inter)
+ buffer[3] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (intra)
+ distorsion = 1 + 42 * usbvision->comprLevel / 100;
+ buffer[4] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (inter)
+ buffer[5] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (intra)
+ }
+ else { //BRIDGE_NT1003
+ buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM threshold 1
+ buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM threshold 2
+ distorsion = 2 + 253 * usbvision->comprLevel / 100;
+ buffer[2] = (unsigned char)(distorsion & 0xFF); // distorsion threshold bit0-7
+ buffer[3] = 0; //(unsigned char)((distorsion >> 8) & 0x0F); // distorsion threshold bit 8-11
+ distorsion = 0 + 43 * usbvision->comprLevel / 100;
+ buffer[4] = (unsigned char)(distorsion & 0xFF); // maximum distorsion bit0-7
+ buffer[5] = 0; //(unsigned char)((distorsion >> 8) & 0x01); // maximum distorsion bit 8
+ }
+ errCode = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6);
+ if (errCode == 0){
+ PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0],
+ buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
+ usbvision->lastComprLevel = usbvision->comprLevel;
+ }
+ }
+ }
+ return errCode;
+}
+
+static int usbvision_request_intra (struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+ unsigned char buffer[1];
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->requestIntra = 1;
+ buffer[0] = 1;
+ usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+ return errCode;
+}
+
+static int usbvision_unrequest_intra (struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+ unsigned char buffer[1];
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->requestIntra = 0;
+ buffer[0] = 0;
+ usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+ return errCode;
+}
+
+/*******************************
+ * usbvision utility functions
+ *******************************/
+
+int usbvision_power_off(struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+
+ PDEBUG(DBG_FUNC, "");
+
+ errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+ if (errCode == 1) {
+ usbvision->power = 0;
+ }
+ PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode!=1)?"ERROR":"power is off", errCode);
+ return errCode;
+}
+
+/*
+ * usbvision_set_video_format()
+ *
+ */
+static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format)
+{
+ static const char proc[] = "usbvision_set_video_format";
+ int rc;
+ unsigned char value[2];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ PDEBUG(DBG_FUNC, "isocMode %#02x", format);
+
+ if ((format != ISOC_MODE_YUV422)
+ && (format != ISOC_MODE_YUV420)
+ && (format != ISOC_MODE_COMPRESS)) {
+ printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420",
+ format);
+ format = ISOC_MODE_YUV420;
+ }
+ value[0] = 0x0A; //TODO: See the effect of the filter
+ value[1] = format;
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_FILT_CONT, value, 2, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ }
+ usbvision->isocMode = format;
+ return rc;
+}
+
+/*
+ * usbvision_set_output()
+ *
+ */
+
+int usbvision_set_output(struct usb_usbvision *usbvision, int width,
+ int height)
+{
+ int errCode = 0;
+ int UsbWidth, UsbHeight;
+ unsigned int frameRate=0, frameDrop=0;
+ unsigned char value[4];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision)) {
+ return 0;
+ }
+
+ if (width > MAX_USB_WIDTH) {
+ UsbWidth = width / 2;
+ usbvision->stretch_width = 2;
+ }
+ else {
+ UsbWidth = width;
+ usbvision->stretch_width = 1;
+ }
+
+ if (height > MAX_USB_HEIGHT) {
+ UsbHeight = height / 2;
+ usbvision->stretch_height = 2;
+ }
+ else {
+ UsbHeight = height;
+ usbvision->stretch_height = 1;
+ }
+
+ RESTRICT_TO_RANGE(UsbWidth, MIN_FRAME_WIDTH, MAX_USB_WIDTH);
+ UsbWidth &= ~(MIN_FRAME_WIDTH-1);
+ RESTRICT_TO_RANGE(UsbHeight, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT);
+ UsbHeight &= ~(1);
+
+ PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d",
+ UsbWidth, UsbHeight, width, height,
+ usbvision->stretch_width, usbvision->stretch_height);
+
+ /* I'll not rewrite the same values */
+ if ((UsbWidth != usbvision->curwidth) || (UsbHeight != usbvision->curheight)) {
+ value[0] = UsbWidth & 0xff; //LSB
+ value[1] = (UsbWidth >> 8) & 0x03; //MSB
+ value[2] = UsbHeight & 0xff; //LSB
+ value[3] = (UsbHeight >> 8) & 0x03; //MSB
+
+ errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);
+
+ if (errCode < 0) {
+ err("%s failed: error %d", __FUNCTION__, errCode);
+ return errCode;
+ }
+ usbvision->curwidth = usbvision->stretch_width * UsbWidth;
+ usbvision->curheight = usbvision->stretch_height * UsbHeight;
+ }
+
+ if (usbvision->isocMode == ISOC_MODE_YUV422) {
+ frameRate = (usbvision->isocPacketSize * 1000) / (UsbWidth * UsbHeight * 2);
+ }
+ else if (usbvision->isocMode == ISOC_MODE_YUV420) {
+ frameRate = (usbvision->isocPacketSize * 1000) / ((UsbWidth * UsbHeight * 12) / 8);
+ }
+ else {
+ frameRate = FRAMERATE_MAX;
+ }
+
+ if (usbvision->tvnorm->id & V4L2_STD_625_50) {
+ frameDrop = frameRate * 32 / 25 - 1;
+ }
+ else if (usbvision->tvnorm->id & V4L2_STD_525_60) {
+ frameDrop = frameRate * 32 / 30 - 1;
+ }
+
+ RESTRICT_TO_RANGE(frameDrop, FRAMERATE_MIN, FRAMERATE_MAX);
+
+ PDEBUG(DBG_FUNC, "frameRate %d fps, frameDrop %d", frameRate, frameDrop);
+
+ frameDrop = FRAMERATE_MAX; // We can allow the maximum here, because dropping is controlled
+
+ /* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
+ => frameSkip = 4;
+ => frameRate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25;
+
+ frameDrop = 9; => framePhase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ...
+ => frameSkip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ...
+ => frameRate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125;
+ */
+ errCode = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frameDrop);
+ return errCode;
+}
+
+
+/*
+ * usbvision_frames_alloc
+ * allocate the maximum frames this driver can manage
+ */
+int usbvision_frames_alloc(struct usb_usbvision *usbvision)
+{
+ int i;
+
+ /* Allocate memory for the frame buffers */
+ usbvision->max_frame_size = MAX_FRAME_SIZE;
+ usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size;
+ usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size);
+
+ if(usbvision->fbuf == NULL) {
+ err("%s: unable to allocate %d bytes for fbuf ",
+ __FUNCTION__, usbvision->fbuf_size);
+ return -ENOMEM;
+ }
+ spin_lock_init(&usbvision->queue_lock);
+ init_waitqueue_head(&usbvision->wait_frame);
+ init_waitqueue_head(&usbvision->wait_stream);
+
+ /* Allocate all buffers */
+ for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+ usbvision->frame[i].index = i;
+ usbvision->frame[i].grabstate = FrameState_Unused;
+ usbvision->frame[i].data = usbvision->fbuf +
+ i * usbvision->max_frame_size;
+ /*
+ * Set default sizes for read operation.
+ */
+ usbvision->stretch_width = 1;
+ usbvision->stretch_height = 1;
+ usbvision->frame[i].width = usbvision->curwidth;
+ usbvision->frame[i].height = usbvision->curheight;
+ usbvision->frame[i].bytes_read = 0;
+ }
+ return 0;
+}
+
+/*
+ * usbvision_frames_free
+ * frees memory allocated for the frames
+ */
+void usbvision_frames_free(struct usb_usbvision *usbvision)
+{
+ /* Have to free all that memory */
+ if (usbvision->fbuf != NULL) {
+ usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
+ usbvision->fbuf = NULL;
+ }
+}
+/*
+ * usbvision_empty_framequeues()
+ * prepare queues for incoming and outgoing frames
+ */
+void usbvision_empty_framequeues(struct usb_usbvision *usbvision)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&(usbvision->inqueue));
+ INIT_LIST_HEAD(&(usbvision->outqueue));
+
+ for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+ usbvision->frame[i].grabstate = FrameState_Unused;
+ usbvision->frame[i].bytes_read = 0;
+ }
+}
+
+/*
+ * usbvision_stream_interrupt()
+ * stops streaming
+ */
+int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
+{
+ int ret = 0;
+
+ /* stop reading from the device */
+
+ usbvision->streaming = Stream_Interrupt;
+ ret = wait_event_timeout(usbvision->wait_stream,
+ (usbvision->streaming == Stream_Idle),
+ msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
+ return ret;
+}
+
+/*
+ * usbvision_set_compress_params()
+ *
+ */
+
+static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
+{
+ static const char proc[] = "usbvision_set_compresion_params: ";
+ int rc;
+ unsigned char value[6];
+
+ value[0] = 0x0F; // Intra-Compression cycle
+ value[1] = 0x01; // Reg.45 one line per strip
+ value[2] = 0x00; // Reg.46 Force intra mode on all new frames
+ value[3] = 0x00; // Reg.47 FORCE_UP <- 0 normal operation (not force)
+ value[4] = 0xA2; // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode.
+ value[5] = 0x00; // Reg.49 DVI_YUV This has nothing to do with compression
+
+ //catched values for NT1004
+ // value[0] = 0xFF; // Never apply intra mode automatically
+ // value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip
+ // value[2] = 0x01; // Force intra mode on all new frames
+ // value[3] = 0x00; // Strip size 400 Bytes; do not force up
+ // value[4] = 0xA2; //
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_INTRA_CYC, value, 5, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+ if (usbvision->bridgeType == BRIDGE_NT1004) {
+ value[0] = 20; // PCM Threshold 1
+ value[1] = 12; // PCM Threshold 2
+ value[2] = 255; // Distorsion Threshold inter
+ value[3] = 255; // Distorsion Threshold intra
+ value[4] = 43; // Max Distorsion inter
+ value[5] = 43; // Max Distorsion intra
+ }
+ else {
+ value[0] = 20; // PCM Threshold 1
+ value[1] = 12; // PCM Threshold 2
+ value[2] = 255; // Distorsion Threshold d7-d0
+ value[3] = 0; // Distorsion Threshold d11-d8
+ value[4] = 43; // Max Distorsion d7-d0
+ value[5] = 0; // Max Distorsion d8
+ }
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_PCM_THR1, value, 6, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+
+ return rc;
+}
+
+
+/*
+ * usbvision_set_input()
+ *
+ * Set the input (saa711x, ...) size x y and other misc input params
+ * I've no idea if this parameters are right
+ *
+ */
+int usbvision_set_input(struct usb_usbvision *usbvision)
+{
+ static const char proc[] = "usbvision_set_input: ";
+ int rc;
+ unsigned char value[8];
+ unsigned char dvi_yuv_value;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ /* Set input format expected from decoder*/
+ if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) {
+ value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff;
+ } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
+ /* SAA7113 uses 8 bit output */
+ value[0] = USBVISION_8_422_SYNC;
+ } else {
+ /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
+ * as that is how saa7111 is configured */
+ value[0] = USBVISION_16_422_SYNC;
+ /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
+ }
+
+ rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]);
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+
+ if (usbvision->tvnorm->id & V4L2_STD_PAL) {
+ value[0] = 0xC0;
+ value[1] = 0x02; //0x02C0 -> 704 Input video line length
+ value[2] = 0x20;
+ value[3] = 0x01; //0x0120 -> 288 Input video n. of lines
+ value[4] = 0x60;
+ value[5] = 0x00; //0x0060 -> 96 Input video h offset
+ value[6] = 0x16;
+ value[7] = 0x00; //0x0016 -> 22 Input video v offset
+ } else if (usbvision->tvnorm->id & V4L2_STD_SECAM) {
+ value[0] = 0xC0;
+ value[1] = 0x02; //0x02C0 -> 704 Input video line length
+ value[2] = 0x20;
+ value[3] = 0x01; //0x0120 -> 288 Input video n. of lines
+ value[4] = 0x01;
+ value[5] = 0x00; //0x0001 -> 01 Input video h offset
+ value[6] = 0x01;
+ value[7] = 0x00; //0x0001 -> 01 Input video v offset
+ } else { /* V4L2_STD_NTSC */
+ value[0] = 0xD0;
+ value[1] = 0x02; //0x02D0 -> 720 Input video line length
+ value[2] = 0xF0;
+ value[3] = 0x00; //0x00F0 -> 240 Input video number of lines
+ value[4] = 0x50;
+ value[5] = 0x00; //0x0050 -> 80 Input video h offset
+ value[6] = 0x10;
+ value[7] = 0x00; //0x0010 -> 16 Input video v offset
+ }
+
+ if (usbvision_device_data[usbvision->DevModel].X_Offset >= 0) {
+ value[4]=usbvision_device_data[usbvision->DevModel].X_Offset & 0xff;
+ value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8;
+ }
+
+ if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) {
+ value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff;
+ value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8;
+ }
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE, /* USBVISION specific code */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_LXSIZE_I, value, 8, HZ);
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+
+ dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */
+
+ if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){
+ dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff;
+ }
+ else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
+ /* This changes as the fine sync control changes. Further investigation necessary */
+ dvi_yuv_value = 0x06;
+ }
+
+ return (usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value));
+}
+
+
+/*
+ * usbvision_set_dram_settings()
+ *
+ * Set the buffer address needed by the usbvision dram to operate
+ * This values has been taken with usbsnoop.
+ *
+ */
+
+static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
+{
+ int rc;
+ unsigned char value[8];
+
+ if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+ value[0] = 0x42;
+ value[1] = 0x71;
+ value[2] = 0xff;
+ value[3] = 0x00;
+ value[4] = 0x98;
+ value[5] = 0xe0;
+ value[6] = 0x71;
+ value[7] = 0xff;
+ // UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte)
+ // FDL: 0x00000-0x0E099 = 57498 Words
+ // VDW: 0x0E3FF-0x3FFFF
+ }
+ else {
+ value[0] = 0x42;
+ value[1] = 0x00;
+ value[2] = 0xff;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
+ value[7] = 0xff;
+ }
+ /* These are the values of the address of the video buffer,
+ * they have to be loaded into the USBVISION_DRM_PRM1-8
+ *
+ * Start address of video output buffer for read: drm_prm1-2 -> 0x00000
+ * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff
+ * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000
+ * Only used in compressed mode
+ * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff
+ * Only used in compressed mode
+ * Start address of video output buffer for write: drm_prm1-7 -> 0x00000
+ * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff
+ */
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE, /* USBVISION specific code */
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_DRM_PRM1, value, 8, HZ);
+
+ if (rc < 0) {
+ err("%sERROR=%d", __FUNCTION__, rc);
+ return rc;
+ }
+
+ /* Restart the video buffer logic */
+ if ((rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR |
+ USBVISION_RES_FDL | USBVISION_RES_VDW)) < 0)
+ return rc;
+ rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00);
+
+ return rc;
+}
+
+/*
+ * ()
+ *
+ * Power on the device, enables suspend-resume logic
+ * & reset the isoc End-Point
+ *
+ */
+
+int usbvision_power_on(struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+
+ PDEBUG(DBG_FUNC, "");
+
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_RES2);
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID);
+ errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2);
+ if (errCode == 1) {
+ usbvision->power = 1;
+ }
+ PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode<0)?"ERROR":"power is on", errCode);
+ return errCode;
+}
+
+
+/*
+ * usbvision timer stuff
+ */
+
+// to call usbvision_power_off from task queue
+static void call_usbvision_power_off(struct work_struct *work)
+{
+ struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, powerOffWork);
+
+ PDEBUG(DBG_FUNC, "");
+ down_interruptible(&usbvision->lock);
+ if(usbvision->user == 0) {
+ usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ up(&usbvision->lock);
+}
+
+static void usbvision_powerOffTimer(unsigned long data)
+{
+ struct usb_usbvision *usbvision = (void *) data;
+
+ PDEBUG(DBG_FUNC, "");
+ del_timer(&usbvision->powerOffTimer);
+ INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off);
+ (void) schedule_work(&usbvision->powerOffWork);
+
+}
+
+void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision)
+{
+ init_timer(&usbvision->powerOffTimer);
+ usbvision->powerOffTimer.data = (long) usbvision;
+ usbvision->powerOffTimer.function = usbvision_powerOffTimer;
+}
+
+void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision)
+{
+ mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
+}
+
+void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision)
+{
+ if (timer_pending(&usbvision->powerOffTimer)) {
+ del_timer(&usbvision->powerOffTimer);
+ }
+}
+
+/*
+ * usbvision_begin_streaming()
+ * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
+ * idea about the rest
+ */
+int usbvision_begin_streaming(struct usb_usbvision *usbvision)
+{
+ int errCode = 0;
+
+ if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+ usbvision_init_compression(usbvision);
+ }
+ errCode = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, USBVISION_NOHVALID |
+ usbvision->Vin_Reg2_Preset);
+ return errCode;
+}
+
+/*
+ * usbvision_restart_isoc()
+ * Not sure yet if touching here PWR_REG make loose the config
+ */
+
+int usbvision_restart_isoc(struct usb_usbvision *usbvision)
+{
+ int ret;
+
+ if (
+ (ret =
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID)) < 0)
+ return ret;
+ if (
+ (ret =
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID |
+ USBVISION_RES2)) < 0)
+ return ret;
+ if (
+ (ret =
+ usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
+ USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
+ usbvision->Vin_Reg2_Preset)) < 0) return ret;
+
+ /* TODO: schedule timeout */
+ while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) && 0x01) != 1);
+
+ return 0;
+}
+
+int usbvision_audio_off(struct usb_usbvision *usbvision)
+{
+ if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) {
+ printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n");
+ return -1;
+ }
+ usbvision->AudioMute = 0;
+ usbvision->AudioChannel = USBVISION_AUDIO_MUTE;
+ return 0;
+}
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel)
+{
+ if (!usbvision->AudioMute) {
+ if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) {
+ printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n");
+ return -1;
+ }
+ }
+ usbvision->AudioChannel = AudioChannel;
+ return 0;
+}
+
+int usbvision_setup(struct usb_usbvision *usbvision,int format)
+{
+ usbvision_set_video_format(usbvision, format);
+ usbvision_set_dram_settings(usbvision);
+ usbvision_set_compress_params(usbvision);
+ usbvision_set_input(usbvision);
+ usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT);
+ usbvision_restart_isoc(usbvision);
+
+ /* cosas del PCM */
+ return USBVISION_IS_OPERATIONAL(usbvision);
+}
+
+
+int usbvision_sbuf_alloc(struct usb_usbvision *usbvision)
+{
+ int i, errCode = 0;
+ const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE;
+
+ /* Clean pointers so we know if we allocated something */
+ for (i = 0; i < USBVISION_NUMSBUF; i++)
+ usbvision->sbuf[i].data = NULL;
+
+ for (i = 0; i < USBVISION_NUMSBUF; i++) {
+ usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL);
+ if (usbvision->sbuf[i].data == NULL) {
+ err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size);
+ errCode = -ENOMEM;
+ break;
+ }
+ }
+ return errCode;
+}
+
+
+void usbvision_sbuf_free(struct usb_usbvision *usbvision)
+{
+ int i;
+
+ for (i = 0; i < USBVISION_NUMSBUF; i++) {
+ if (usbvision->sbuf[i].data != NULL) {
+ kfree(usbvision->sbuf[i].data);
+ usbvision->sbuf[i].data = NULL;
+ }
+ }
+}
+
+/*
+ * usbvision_init_isoc()
+ *
+ */
+int usbvision_init_isoc(struct usb_usbvision *usbvision)
+{
+ struct usb_device *dev = usbvision->dev;
+ int bufIdx, errCode, regValue;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EFAULT;
+
+ usbvision->curFrame = NULL;
+ scratch_reset(usbvision);
+
+ /* Alternate interface 1 is is the biggest frame size */
+ errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive);
+ if (errCode < 0) {
+ usbvision->last_error = errCode;
+ return -EBUSY;
+ }
+
+ regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+ usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
+ PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);
+
+ usbvision->usb_bandwidth = regValue >> 1;
+ PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);
+
+
+
+ /* We double buffer the Iso lists */
+
+ for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+ int j, k;
+ struct urb *urb;
+
+ urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (urb == NULL) {
+ err("%s: usb_alloc_urb() failed", __FUNCTION__);
+ return -ENOMEM;
+ }
+ usbvision->sbuf[bufIdx].urb = urb;
+ urb->dev = dev;
+ urb->context = usbvision;
+ urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->interval = 1;
+ urb->transfer_buffer = usbvision->sbuf[bufIdx].data;
+ urb->complete = usbvision_isocIrq;
+ urb->number_of_packets = USBVISION_URB_FRAMES;
+ urb->transfer_buffer_length =
+ usbvision->isocPacketSize * USBVISION_URB_FRAMES;
+ for (j = k = 0; j < USBVISION_URB_FRAMES; j++,
+ k += usbvision->isocPacketSize) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length = usbvision->isocPacketSize;
+ }
+ }
+
+
+ /* Submit all URBs */
+ for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+ errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL);
+ if (errCode) {
+ err("%s: usb_submit_urb(%d) failed: error %d", __FUNCTION__, bufIdx, errCode);
+ }
+ }
+
+ usbvision->streaming = Stream_Idle;
+ PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp);
+ return 0;
+}
+
+/*
+ * usbvision_stop_isoc()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ */
+void usbvision_stop_isoc(struct usb_usbvision *usbvision)
+{
+ int bufIdx, errCode, regValue;
+
+ if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL))
+ return;
+
+ /* Unschedule all of the iso td's */
+ for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+ usb_kill_urb(usbvision->sbuf[bufIdx].urb);
+ usb_free_urb(usbvision->sbuf[bufIdx].urb);
+ usbvision->sbuf[bufIdx].urb = NULL;
+ }
+
+
+ PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __FUNCTION__);
+ usbvision->streaming = Stream_Off;
+
+ if (!usbvision->remove_pending) {
+
+ /* Set packet size to 0 */
+ errCode = usb_set_interface(usbvision->dev, usbvision->iface,
+ usbvision->ifaceAltInactive);
+ if (errCode < 0) {
+ err("%s: usb_set_interface() failed: error %d", __FUNCTION__, errCode);
+ usbvision->last_error = errCode;
+ }
+ regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+ usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
+ PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);
+
+ usbvision->usb_bandwidth = regValue >> 1;
+ PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);
+ }
+}
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
+{
+ int mode[4];
+ int audio[]= {1, 0, 0, 0};
+ struct v4l2_routing route;
+ //channel 0 is TV with audiochannel 1 (tuner mono)
+ //channel 1 is Composite with audio channel 0 (line in)
+ //channel 2 is S-Video with audio channel 0 (line in)
+ //channel 3 is additional video inputs to the device with audio channel 0 (line in)
+
+ RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs);
+ usbvision->ctl_input = channel;
+ route.input = SAA7115_COMPOSITE1;
+ call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
+ call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input);
+
+ // set the new channel
+ // Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video
+ // Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red
+
+ switch (usbvision_device_data[usbvision->DevModel].Codec) {
+ case CODEC_SAA7113:
+ if (SwitchSVideoInput) { // To handle problems with S-Video Input for some devices. Use SwitchSVideoInput parameter when loading the module.
+ mode[2] = 1;
+ }
+ else {
+ mode[2] = 7;
+ }
+ if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+ mode[0] = 0; mode[1] = 2; mode[3] = 3; // Special for four input devices
+ }
+ else {
+ mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices
+ }
+ break;
+ case CODEC_SAA7111:
+ mode[0] = 0; mode[1] = 1; mode[2] = 7; //modes for saa7111
+ break;
+ default:
+ mode[0] = 0; mode[1] = 1; mode[2] = 7; //default modes
+ }
+ route.input = mode[channel];
+ call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
+ usbvision->channel = channel;
+ usbvision_set_audio(usbvision, audio[channel]);
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
new file mode 100644
index 0000000..0f3fba7
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -0,0 +1,571 @@
+/*
+ * I2C_ALGO_USB.C
+ * i2c algorithm for USB-I2C Bridges
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/slab.h>
+#include <linux/version.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include "usbvision.h"
+
+#define DBG_I2C 1<<0
+#define DBG_ALGO 1<<1
+
+static int i2c_debug = 0;
+
+module_param (i2c_debug, int, 0644); // debug_i2c_usb mode of the device driver
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define PDEBUG(level, fmt, args...) \
+ if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+
+static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+ short len);
+static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+ short len);
+
+static inline int try_write_address(struct i2c_adapter *i2c_adap,
+ unsigned char addr, int retries)
+{
+ struct i2c_algo_usb_data *adap = i2c_adap->algo_data;
+ void *data;
+ int i, ret = -1;
+ char buf[4];
+
+ data = i2c_get_adapdata(i2c_adap);
+ buf[0] = 0x00;
+ for (i = 0; i <= retries; i++) {
+ ret = (usbvision_i2c_write(data, addr, buf, 1));
+ if (ret == 1)
+ break; /* success! */
+ udelay(5 /*adap->udelay */ );
+ if (i == retries) /* no success */
+ break;
+ udelay(adap->udelay);
+ }
+ if (i) {
+ PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+ }
+ return ret;
+}
+
+static inline int try_read_address(struct i2c_adapter *i2c_adap,
+ unsigned char addr, int retries)
+{
+ struct i2c_algo_usb_data *adap = i2c_adap->algo_data;
+ void *data;
+ int i, ret = -1;
+ char buf[4];
+
+ data = i2c_get_adapdata(i2c_adap);
+ for (i = 0; i <= retries; i++) {
+ ret = (usbvision_i2c_read(data, addr, buf, 1));
+ if (ret == 1)
+ break; /* success! */
+ udelay(5 /*adap->udelay */ );
+ if (i == retries) /* no success */
+ break;
+ udelay(adap->udelay);
+ }
+ if (i) {
+ PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+ }
+ return ret;
+}
+
+static inline int usb_find_address(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msg, int retries,
+ unsigned char *add)
+{
+ unsigned short flags = msg->flags;
+
+ unsigned char addr;
+ int ret;
+ if ((flags & I2C_M_TEN)) {
+ /* a ten bit address */
+ addr = 0xf0 | ((msg->addr >> 7) & 0x03);
+ /* try extended address code... */
+ ret = try_write_address(i2c_adap, addr, retries);
+ if (ret != 1) {
+ err("died at extended address code, while writing");
+ return -EREMOTEIO;
+ }
+ add[0] = addr;
+ if (flags & I2C_M_RD) {
+ /* okay, now switch into reading mode */
+ addr |= 0x01;
+ ret = try_read_address(i2c_adap, addr, retries);
+ if (ret != 1) {
+ err("died at extended address code, while reading");
+ return -EREMOTEIO;
+ }
+ }
+
+ } else { /* normal 7bit address */
+ addr = (msg->addr << 1);
+ if (flags & I2C_M_RD)
+ addr |= 1;
+ if (flags & I2C_M_REV_DIR_ADDR)
+ addr ^= 1;
+
+ add[0] = addr;
+ if (flags & I2C_M_RD)
+ ret = try_read_address(i2c_adap, addr, retries);
+ else
+ ret = try_write_address(i2c_adap, addr, retries);
+
+ if (ret != 1) {
+ return -EREMOTEIO;
+ }
+ }
+ return 0;
+}
+
+static int
+usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+ struct i2c_msg *pmsg;
+ void *data;
+ int i, ret;
+ unsigned char addr;
+
+ data = i2c_get_adapdata(i2c_adap);
+
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr);
+ if (ret != 0) {
+ PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i);
+ return (ret < 0) ? ret : -EREMOTEIO;
+ }
+
+ if (pmsg->flags & I2C_M_RD) {
+ /* read bytes into buffer */
+ ret = (usbvision_i2c_read(data, addr, pmsg->buf, pmsg->len));
+ if (ret < pmsg->len) {
+ return (ret < 0) ? ret : -EREMOTEIO;
+ }
+ } else {
+ /* write bytes from buffer */
+ ret = (usbvision_i2c_write(data, addr, pmsg->buf, pmsg->len));
+ if (ret < pmsg->len) {
+ return (ret < 0) ? ret : -EREMOTEIO;
+ }
+ }
+ }
+ return num;
+}
+
+static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static u32 usb_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static struct i2c_algorithm i2c_usb_algo = {
+ .master_xfer = usb_xfer,
+ .smbus_xfer = NULL,
+ .algo_control = algo_control,
+ .functionality = usb_func,
+};
+
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap)
+{
+ PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]");
+ PDEBUG(DBG_ALGO, "ALGO debugging is enabled [i2c]");
+
+ /* register new adapter to i2c module... */
+
+ adap->algo = &i2c_usb_algo;
+
+ adap->timeout = 100; /* default values, should */
+ adap->retries = 3; /* be replaced by defines */
+
+ i2c_add_adapter(adap);
+
+ PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name);
+
+ return 0;
+}
+
+
+int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap)
+{
+
+ i2c_del_adapter(adap);
+
+ PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions */
+/* ----------------------------------------------------------------------- */
+static struct i2c_adapter i2c_adap_template;
+static struct i2c_algo_usb_data i2c_algo_template;
+static struct i2c_client i2c_client_template;
+
+int usbvision_init_i2c(struct usb_usbvision *usbvision)
+{
+ memcpy(&usbvision->i2c_adap, &i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ memcpy(&usbvision->i2c_algo, &i2c_algo_template,
+ sizeof(struct i2c_algo_usb_data));
+ memcpy(&usbvision->i2c_client, &i2c_client_template,
+ sizeof(struct i2c_client));
+
+ sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name),
+ " #%d", usbvision->vdev->minor & 0x1f);
+ PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name);
+
+ i2c_set_adapdata(&usbvision->i2c_adap, usbvision);
+ i2c_set_clientdata(&usbvision->i2c_client, usbvision);
+ i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision);
+
+ usbvision->i2c_adap.algo_data = &usbvision->i2c_algo;
+ usbvision->i2c_client.adapter = &usbvision->i2c_adap;
+
+ if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
+ printk(KERN_ERR "usbvision_init_i2c: can't write reg\n");
+ return -EBUSY;
+ }
+
+#ifdef CONFIG_MODULES
+ /* Request the load of the i2c modules we need */
+ switch (usbvision_device_data[usbvision->DevModel].Codec) {
+ case CODEC_SAA7113:
+ request_module("saa7115");
+ break;
+ case CODEC_SAA7111:
+ request_module("saa7115");
+ break;
+ }
+ if (usbvision_device_data[usbvision->DevModel].Tuner == 1) {
+ request_module("tuner");
+ }
+#endif
+
+ return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);
+}
+
+void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
+ void *arg)
+{
+ BUG_ON(NULL == usbvision->i2c_adap.algo_data);
+ i2c_clients_command(&usbvision->i2c_adap, cmd, arg);
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+ struct usb_usbvision *usbvision;
+
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+
+ switch (client->addr << 1) {
+ case 0x43:
+ case 0x4b:
+ {
+ struct tuner_setup tun_setup;
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = TUNER_TDA9887;
+ tun_setup.addr = client->addr;
+
+ call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
+
+ break;
+ }
+ case 0x42:
+ PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.");
+ break;
+ case 0x4a:
+ PDEBUG(DBG_I2C,"attach_inform: saa7113 detected.");
+ break;
+ case 0xa0:
+ PDEBUG(DBG_I2C,"attach_inform: eeprom detected.");
+ break;
+
+ default:
+ {
+ struct tuner_setup tun_setup;
+
+ PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1);
+ usbvision->tuner_addr = client->addr;
+
+ if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) {
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = usbvision->tuner_type;
+ tun_setup.addr = usbvision->tuner_addr;
+ call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+ struct usb_usbvision *usbvision;
+
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+
+ PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name);
+ return 0;
+}
+
+static int
+usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
+ char *buf, short len)
+{
+ int rc, retries;
+
+ for (retries = 5;;) {
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate byte read cycle */
+ /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */
+ /* d3 0=Wr 1=Rd */
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+ (len & 0x07) | 0x18);
+ if (rc < 0)
+ return rc;
+
+ /* Test for Busy and ACK */
+ do {
+ /* USBVISION_SER_CONT -> d4 == 0 busy */
+ rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+ } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */
+ if (rc < 0)
+ return rc;
+
+ /* USBVISION_SER_CONT -> d5 == 1 Not ack */
+ if ((rc & 0x20) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+ if (rc < 0)
+ return rc;
+
+ if (--retries < 0)
+ return -1;
+ }
+
+ switch (len) {
+ case 4:
+ buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4);
+ case 3:
+ buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3);
+ case 2:
+ buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2);
+ case 1:
+ buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1);
+ break;
+ default:
+ printk(KERN_ERR
+ "usbvision_i2c_read_max4: buffer length > 4\n");
+ }
+
+ if (i2c_debug & DBG_I2C) {
+ int idx;
+ for (idx = 0; idx < len; idx++) {
+ PDEBUG(DBG_I2C,"read %x from address %x", (unsigned char)buf[idx], addr);
+ }
+ }
+ return len;
+}
+
+
+static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision,
+ unsigned char addr, const char *buf,
+ short len)
+{
+ int rc, retries;
+ int i;
+ unsigned char value[6];
+ unsigned char ser_cont;
+
+ ser_cont = (len & 0x07) | 0x10;
+
+ value[0] = addr;
+ value[1] = ser_cont;
+ for (i = 0; i < len; i++)
+ value[i + 2] = buf[i];
+
+ for (retries = 5;;) {
+ rc = usb_control_msg(usbvision->dev,
+ usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_SER_ADRS, value,
+ len + 2, HZ);
+
+ if (rc < 0)
+ return rc;
+
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+ (len & 0x07) | 0x10);
+ if (rc < 0)
+ return rc;
+
+ /* Test for Busy and ACK */
+ do {
+ rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+ } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 0x20) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+
+ if (--retries < 0)
+ return -1;
+
+ }
+
+ if (i2c_debug & DBG_I2C) {
+ int idx;
+ for (idx = 0; idx < len; idx++) {
+ PDEBUG(DBG_I2C,"wrote %x at address %x", (unsigned char)buf[idx], addr);
+ }
+ }
+ return len;
+}
+
+static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+ short len)
+{
+ char *bufPtr = buf;
+ int retval;
+ int wrcount = 0;
+ int count;
+ int maxLen = 4;
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
+
+ while (len > 0) {
+ count = (len > maxLen) ? maxLen : len;
+ retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count);
+ if (retval > 0) {
+ len -= count;
+ bufPtr += count;
+ wrcount += count;
+ } else
+ return (retval < 0) ? retval : -EFAULT;
+ }
+ return wrcount;
+}
+
+static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+ short len)
+{
+ char temp[4];
+ int retval, i;
+ int rdcount = 0;
+ int count;
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
+
+ while (len > 0) {
+ count = (len > 3) ? 4 : len;
+ retval = usbvision_i2c_read_max4(usbvision, addr, temp, count);
+ if (retval > 0) {
+ for (i = 0; i < len; i++)
+ buf[rdcount + i] = temp[i];
+ len -= count;
+ rdcount += count;
+ } else
+ return (retval < 0) ? retval : -EFAULT;
+ }
+ return rdcount;
+}
+
+static struct i2c_algo_usb_data i2c_algo_template = {
+ .data = NULL,
+ .inb = usbvision_i2c_read,
+ .outb = usbvision_i2c_write,
+ .udelay = 10,
+ .mdelay = 10,
+ .timeout = 100,
+};
+
+static struct i2c_adapter i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "usbvision",
+ .id = I2C_HW_B_BT848, /* FIXME */
+ .algo = NULL,
+ .algo_data = NULL,
+ .client_register = attach_inform,
+ .client_unregister = detach_inform,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+ .class = I2C_ADAP_CLASS_TV_ANALOG,
+#else
+ .class = I2C_CLASS_TV_ANALOG,
+#endif
+};
+
+static struct i2c_client i2c_client_template = {
+ .name = "usbvision internal",
+};
+
+EXPORT_SYMBOL(usbvision_i2c_usb_add_bus);
+EXPORT_SYMBOL(usbvision_i2c_usb_del_bus);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
new file mode 100644
index 0000000..864446c
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -0,0 +1,2051 @@
+/*
+ * USB USBVISION Video device driver 0.9.9
+ *
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ *
+ * 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.
+ *
+ * Let's call the version 0.... until compression decoding is completely
+ * implemented.
+ *
+ * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
+ * It was based on USB CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt
+ * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
+ * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ *
+ * TODO:
+ * - use submit_urb for all setup packets
+ * - Fix memory settings for nt1004. It is 4 times as big as the
+ * nt1003 memory.
+ * - Add audio on endpoint 3 for nt1004 chip. Seems impossible, needs a codec interface. Which one?
+ * - Clean up the driver.
+ * - optimization for performance.
+ * - Add Videotext capability (VBI). Working on it.....
+ * - Check audio for other devices
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/videodev2.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/audiochip.h>
+
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "usbvision.h"
+
+#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
+#define DRIVER_NAME "usbvision"
+#define DRIVER_ALIAS "USBVision"
+#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
+#define DRIVER_LICENSE "GPL"
+#define USBVISION_DRIVER_VERSION_MAJOR 0
+#define USBVISION_DRIVER_VERSION_MINOR 9
+#define USBVISION_DRIVER_VERSION_PATCHLEVEL 9
+#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL)
+#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL)
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+ #define PDEBUG(level, fmt, args...) \
+ if (video_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+ #define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+#define DBG_IOCTL 1<<0
+#define DBG_IO 1<<1
+#define DBG_PROBE 1<<2
+#define DBG_MMAP 1<<3
+
+//String operations
+#define rmspace(str) while(*str==' ') str++;
+#define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++;
+
+
+static int usbvision_nr = 0; // sequential number of usbvision device
+
+static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
+ { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" },
+ { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" },
+ { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" },
+ { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" },
+ { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 !
+ { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
+};
+
+/* supported tv norms */
+static struct usbvision_tvnorm tvnorms[] = {
+ {
+ .name = "PAL",
+ .id = V4L2_STD_PAL,
+ }, {
+ .name = "NTSC",
+ .id = V4L2_STD_NTSC,
+ }, {
+ .name = "SECAM",
+ .id = V4L2_STD_SECAM,
+ }, {
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ }
+};
+
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+// Function prototypes
+static void usbvision_release(struct usb_usbvision *usbvision);
+
+// Default initalization of device driver parameters
+static int isocMode = ISOC_MODE_COMPRESS; // Set the default format for ISOC endpoint
+static int video_debug = 0; // Set the default Debug Mode of the device driver
+static int PowerOnAtOpen = 1; // Set the default device to power on at startup
+static int video_nr = -1; // Sequential Number of Video Device
+static int radio_nr = -1; // Sequential Number of Radio Device
+static int vbi_nr = -1; // Sequential Number of VBI Device
+static char *CustomDevice=NULL; // Set as nothing....
+
+// Grab parameters for the device driver
+
+#if defined(module_param) // Showing parameters under SYSFS
+module_param(isocMode, int, 0444);
+module_param(video_debug, int, 0444);
+module_param(PowerOnAtOpen, int, 0444);
+module_param(video_nr, int, 0444);
+module_param(radio_nr, int, 0444);
+module_param(vbi_nr, int, 0444);
+module_param(CustomDevice, charp, 0444);
+#else // Old Style
+MODULE_PARAM(isocMode, "i");
+MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver
+MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive
+MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup
+MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently.
+MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
+MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
+MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
+MODULE_PARM(CustomDevice, "s"); // .... CustomDevice
+#endif
+
+MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)");
+MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)");
+MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)");
+MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)");
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
+MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)");
+MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null");
+
+
+// Misc stuff
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_VERSION(USBVISION_VERSION_STRING);
+MODULE_ALIAS(DRIVER_ALIAS);
+
+
+/****************************************************************************************/
+/* SYSFS Code - Copied from the stv680.c usb module. */
+/* Device information is located at /sys/class/video4linux/video0 */
+/* Device parameters information is located at /sys/module/usbvision */
+/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */
+/****************************************************************************************/
+
+
+#define YES_NO(x) ((x) ? "Yes" : "No")
+
+static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_version(struct class_device *cd, char *buf)
+{
+ return sprintf(buf, "%s\n", USBVISION_VERSION_STRING);
+}
+static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_model(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString);
+}
+static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_hue(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = 0;
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_contrast(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = 0;
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_brightness(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = 0;
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = 0;
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_streaming(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0));
+}
+static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);
+
+static ssize_t show_compression(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS));
+}
+static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);
+
+static ssize_t show_device_bridge(struct class_device *class_dev, char *buf)
+{
+ struct video_device *vdev = to_video_device(class_dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%d\n", usbvision->bridgeType);
+}
+static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);
+
+static void usbvision_create_sysfs(struct video_device *vdev)
+{
+ int res;
+ if (vdev) {
+ res=video_device_create_file(vdev, &class_device_attr_version);
+ res=video_device_create_file(vdev, &class_device_attr_model);
+ res=video_device_create_file(vdev, &class_device_attr_hue);
+ res=video_device_create_file(vdev, &class_device_attr_contrast);
+ res=video_device_create_file(vdev, &class_device_attr_brightness);
+ res=video_device_create_file(vdev, &class_device_attr_saturation);
+ res=video_device_create_file(vdev, &class_device_attr_streaming);
+ res=video_device_create_file(vdev, &class_device_attr_compression);
+ res=video_device_create_file(vdev, &class_device_attr_bridge);
+ }
+}
+
+static void usbvision_remove_sysfs(struct video_device *vdev)
+{
+ if (vdev) {
+ video_device_remove_file(vdev, &class_device_attr_version);
+ video_device_remove_file(vdev, &class_device_attr_model);
+ video_device_remove_file(vdev, &class_device_attr_hue);
+ video_device_remove_file(vdev, &class_device_attr_contrast);
+ video_device_remove_file(vdev, &class_device_attr_brightness);
+ video_device_remove_file(vdev, &class_device_attr_saturation);
+ video_device_remove_file(vdev, &class_device_attr_streaming);
+ video_device_remove_file(vdev, &class_device_attr_compression);
+ video_device_remove_file(vdev, &class_device_attr_bridge);
+ }
+}
+
+
+/*
+ * usbvision_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'usbvision->user'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ */
+static int usbvision_v4l2_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+ int errCode = 0;
+
+ PDEBUG(DBG_IO, "open");
+
+
+ usbvision_reset_powerOffTimer(usbvision);
+
+ if (usbvision->user)
+ errCode = -EBUSY;
+ else {
+ /* Allocate memory for the frame buffers */
+ errCode = usbvision_frames_alloc(usbvision);
+ if(!errCode) {
+ /* Allocate memory for the scratch ring buffer */
+ errCode = usbvision_scratch_alloc(usbvision);
+ if(!errCode) {
+ /* Allocate memory for the USB S buffers */
+ errCode = usbvision_sbuf_alloc(usbvision);
+ if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) {
+ /* Allocate intermediate decompression buffers only if needed */
+ errCode = usbvision_decompress_alloc(usbvision);
+ }
+ }
+ }
+ if (errCode) {
+ /* Deallocate all buffers if trouble */
+ usbvision_frames_free(usbvision);
+ usbvision_scratch_free(usbvision);
+ usbvision_sbuf_free(usbvision);
+ usbvision_decompress_free(usbvision);
+ }
+ }
+
+ /* If so far no errors then we shall start the camera */
+ if (!errCode) {
+ down(&usbvision->lock);
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_init_i2c(usbvision);
+ }
+
+ /* Send init sequence only once, it's large! */
+ if (!usbvision->initialized) {
+ int setup_ok = 0;
+ setup_ok = usbvision_setup(usbvision,isocMode);
+ if (setup_ok)
+ usbvision->initialized = 1;
+ else
+ errCode = -EBUSY;
+ }
+
+ if (!errCode) {
+ usbvision_begin_streaming(usbvision);
+ errCode = usbvision_init_isoc(usbvision);
+ /* device needs to be initialized before isoc transfer */
+ usbvision_muxsel(usbvision,0);
+ usbvision->user++;
+ }
+ else {
+ if (PowerOnAtOpen) {
+ usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+ up(&usbvision->lock);
+ }
+
+ if (errCode) {
+ }
+
+ /* prepare queues */
+ usbvision_empty_framequeues(usbvision);
+
+ PDEBUG(DBG_IO, "success");
+ return errCode;
+}
+
+/*
+ * usbvision_v4l2_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in usbvision_v4l2_open().
+ *
+ */
+static int usbvision_v4l2_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+ PDEBUG(DBG_IO, "close");
+ down(&usbvision->lock);
+
+ usbvision_audio_off(usbvision);
+ usbvision_restart_isoc(usbvision);
+ usbvision_stop_isoc(usbvision);
+
+ usbvision_decompress_free(usbvision);
+ usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
+ usbvision_scratch_free(usbvision);
+ usbvision_sbuf_free(usbvision);
+
+ usbvision->user--;
+
+ if (PowerOnAtOpen) {
+ /* power off in a little while to avoid off/on every close/open short sequences */
+ usbvision_set_powerOffTimer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ up(&usbvision->lock);
+
+ if (usbvision->remove_pending) {
+ info("%s: Final disconnect", __FUNCTION__);
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_IO, "success");
+
+
+ return 0;
+}
+
+
+/*
+ * usbvision_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EFAULT;
+
+ switch (cmd) {
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /* ioctls to allow direct acces to the NT100x registers */
+ case VIDIOC_INT_G_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+ int errCode;
+
+ if (reg->i2c_id != 0)
+ return -EINVAL;
+ /* NT100x has a 8-bit register space */
+ errCode = usbvision_read_reg(usbvision, reg->reg&0xff);
+ if (errCode < 0) {
+ err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode);
+ }
+ else {
+ reg->val=(unsigned char)errCode;
+ PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X",
+ (unsigned int)reg->reg, reg->val);
+ errCode = 0; // No error
+ }
+ return errCode;
+ }
+ case VIDIOC_INT_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+ int errCode;
+
+ if (reg->i2c_id != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val);
+ if (errCode < 0) {
+ err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode);
+ }
+ else {
+ PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X",
+ (unsigned int)reg->reg, reg->val);
+ errCode = 0;
+ }
+ return 0;
+ }
+#endif
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *vc=arg;
+
+ memset(vc, 0, sizeof(*vc));
+ strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+ strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString,
+ sizeof(vc->card));
+ strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+ sizeof(vc->bus_info));
+ vc->version = USBVISION_DRIVER_VERSION;
+ vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+ PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP");
+ return 0;
+ }
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *vi = arg;
+ int chan;
+
+ if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) )
+ return -EINVAL;
+ if (usbvision->have_tuner) {
+ chan = vi->index;
+ }
+ else {
+ chan = vi->index + 1; //skip Television string
+ }
+ switch(chan) {
+ case 0:
+ if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+ strcpy(vi->name, "White Video Input");
+ }
+ else {
+ strcpy(vi->name, "Television");
+ vi->type = V4L2_INPUT_TYPE_TUNER;
+ vi->audioset = 1;
+ vi->tuner = chan;
+ vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM;
+ }
+ break;
+ case 1:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+ strcpy(vi->name, "Green Video Input");
+ }
+ else {
+ strcpy(vi->name, "Composite Video Input");
+ }
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 2:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+ strcpy(vi->name, "Yellow Video Input");
+ }
+ else {
+ strcpy(vi->name, "S-Video Input");
+ }
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 3:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(vi->name, "Red Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ }
+ PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x",
+ vi->name, vi->index, vi->tuner,vi->type,(int)vi->std);
+ return 0;
+ }
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ unsigned int i;
+ int ret;
+
+ i = e->index;
+ if (i >= TVNORMS)
+ return -EINVAL;
+ ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
+ tvnorms[e->index].name);
+ e->index = i;
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = arg;
+ *input = usbvision->ctl_input;
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg;
+ if ((*input >= usbvision->video_inputs) || (*input < 0) )
+ return -EINVAL;
+ usbvision->ctl_input = *input;
+
+ down(&usbvision->lock);
+ usbvision_muxsel(usbvision, usbvision->ctl_input);
+ usbvision_set_input(usbvision);
+ usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight);
+ up(&usbvision->lock);
+ return 0;
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+
+ *id = usbvision->tvnorm->id;
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name);
+ return 0;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *id = arg;
+ unsigned int i;
+
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ for (i = 0; i < TVNORMS; i++)
+ if (*id & tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ down(&usbvision->lock);
+ usbvision->tvnorm = &tvnorms[i];
+
+ call_i2c_clients(usbvision, VIDIOC_S_STD,
+ &usbvision->tvnorm->id);
+
+ up(&usbvision->lock);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name);
+ return 0;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = arg;
+
+ if (!usbvision->have_tuner || vt->index) // Only tuner 0
+ return -EINVAL;
+ strcpy(vt->name, "Television");
+ /* Let clients fill in the remainder of this struct */
+ call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc);
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *vt = arg;
+
+ // Only no or one tuner for now
+ if (!usbvision->have_tuner || vt->index)
+ return -EINVAL;
+ /* let clients handle this */
+ call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER");
+ return 0;
+ }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *freq = arg;
+
+ freq->tuner = 0; // Only one tuner
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ freq->frequency = usbvision->freq;
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
+ return 0;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *freq = arg;
+
+ // Only no or one tuner for now
+ if (!usbvision->have_tuner || freq->tuner)
+ return -EINVAL;
+
+ usbvision->freq = freq->frequency;
+ call_i2c_clients(usbvision, cmd, freq);
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
+ return 0;
+ }
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *v = arg;
+ memset(v,0, sizeof(v));
+ strcpy(v->name, "TV");
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO");
+ return 0;
+ }
+ case VIDIOC_S_AUDIO:
+ {
+ struct v4l2_audio *v = arg;
+ if(v->index) {
+ return -EINVAL;
+ }
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO");
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+ int id=ctrl->id;
+
+ memset(ctrl,0,sizeof(*ctrl));
+ ctrl->id=id;
+
+ call_i2c_clients(usbvision, cmd, arg);
+
+ if (ctrl->type)
+ return 0;
+ else
+ return -EINVAL;
+
+ PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type);
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
+ return 0;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+ call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
+ return 0;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *vr = arg;
+ int ret;
+
+ RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES);
+
+ // Check input validity : the user must do a VIDEO CAPTURE and MMAP method.
+ if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (vr->memory != V4L2_MEMORY_MMAP))
+ return -EINVAL;
+
+ if(usbvision->streaming == Stream_On) {
+ if ((ret = usbvision_stream_interrupt(usbvision)))
+ return ret;
+ }
+
+ usbvision_empty_framequeues(usbvision);
+
+ usbvision->curFrame = NULL;
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count);
+ return 0;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *vb = arg;
+ struct usbvision_frame *frame;
+
+ // FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called)
+
+ if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+ if(vb->index>=USBVISION_NUMFRAMES) {
+ return -EINVAL;
+ }
+ // Updating the corresponding frame state
+ vb->flags = 0;
+ frame = &usbvision->frame[vb->index];
+ if(frame->grabstate >= FrameState_Ready)
+ vb->flags |= V4L2_BUF_FLAG_QUEUED;
+ if(frame->grabstate >= FrameState_Done)
+ vb->flags |= V4L2_BUF_FLAG_DONE;
+ if(frame->grabstate == FrameState_Unused)
+ vb->flags |= V4L2_BUF_FLAG_MAPPED;
+ vb->memory = V4L2_MEMORY_MMAP;
+
+ vb->m.offset = vb->index*usbvision->max_frame_size;
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->field = V4L2_FIELD_NONE;
+ vb->length = usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel;
+ vb->timestamp = usbvision->frame[vb->index].timestamp;
+ vb->sequence = usbvision->frame[vb->index].sequence;
+ return 0;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *vb = arg;
+ struct usbvision_frame *frame;
+ unsigned long lock_flags;
+
+ // FIXME : works only on VIDEO_CAPTURE MODE, MMAP.
+ if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+ if(vb->index>=USBVISION_NUMFRAMES) {
+ return -EINVAL;
+ }
+
+ frame = &usbvision->frame[vb->index];
+
+ if (frame->grabstate != FrameState_Unused) {
+ return -EAGAIN;
+ }
+
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = FrameState_Ready;
+ frame->scanstate = ScanState_Scanning;
+ frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */
+
+ vb->flags &= ~V4L2_BUF_FLAG_DONE;
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index);
+ return 0;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *vb = arg;
+ int ret;
+ struct usbvision_frame *f;
+ unsigned long lock_flags;
+
+ if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (list_empty(&(usbvision->outqueue))) {
+ if (usbvision->streaming == Stream_Idle)
+ return -EINVAL;
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ f = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ f->grabstate = FrameState_Unused;
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE;
+ vb->index = f->index;
+ vb->sequence = f->sequence;
+ vb->timestamp = f->timestamp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->bytesused = f->scanlength;
+
+ return 0;
+ }
+ case VIDIOC_STREAMON:
+ {
+ int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ usbvision->streaming = Stream_On;
+
+ call_i2c_clients(usbvision,VIDIOC_STREAMON , &b);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON");
+
+ return 0;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ int *type = arg;
+ int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if(usbvision->streaming == Stream_On) {
+ usbvision_stream_interrupt(usbvision);
+ // Stop all video streamings
+ call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b);
+ }
+ usbvision_empty_framequeues(usbvision);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF");
+ return 0;
+ }
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *vfd = arg;
+
+ if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
+ return -EINVAL;
+ }
+ vfd->flags = 0;
+ vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc);
+ vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
+ memset(vfd->reserved, 0, sizeof(vfd->reserved));
+ return 0;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *vf = arg;
+
+ switch (vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ vf->fmt.pix.width = usbvision->curwidth;
+ vf->fmt.pix.height = usbvision->curheight;
+ vf->fmt.pix.pixelformat = usbvision->palette.format;
+ vf->fmt.pix.bytesperline = usbvision->curwidth*usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight;
+ vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d, format=%s",
+ vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+ return 0;
+ }
+ default:
+ PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type);
+ return -EINVAL;
+ }
+ return 0;
+ }
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *vf = arg;
+ int formatIdx,ret;
+
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ /* Find requested format in available ones */
+ for(formatIdx=0;formatIdx<USBVISION_SUPPORTED_PALETTES;formatIdx++) {
+ if(vf->fmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) {
+ usbvision->palette = usbvision_v4l2_format[formatIdx];
+ break;
+ }
+ }
+ /* robustness */
+ if(formatIdx == USBVISION_SUPPORTED_PALETTES) {
+ return -EINVAL;
+ }
+ RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
+ RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
+
+ vf->fmt.pix.bytesperline = vf->fmt.pix.width*usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height;
+
+ if(cmd == VIDIOC_TRY_FMT) {
+ PDEBUG(DBG_IOCTL, "VIDIOC_TRY_FMT grabdisplay w=%d, h=%d, format=%s",
+ vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+ return 0;
+ }
+
+ /* stop io in case it is already in progress */
+ if(usbvision->streaming == Stream_On) {
+ if ((ret = usbvision_stream_interrupt(usbvision)))
+ return ret;
+ }
+ usbvision_empty_framequeues(usbvision);
+
+ usbvision->curFrame = NULL;
+
+ // by now we are committed to the new data...
+ down(&usbvision->lock);
+ usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
+ up(&usbvision->lock);
+
+ PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s",
+ vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl);
+}
+
+
+static ssize_t usbvision_v4l2_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+ int noblock = file->f_flags & O_NONBLOCK;
+ unsigned long lock_flags;
+
+ int frmx = -1;
+ int ret,i;
+ struct usbvision_frame *frame;
+
+ PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
+ return -EFAULT;
+
+ /* no stream is running, make it running ! */
+ usbvision->streaming = Stream_On;
+ call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL);
+
+ /* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */
+ for(i=0;i<USBVISION_NUMFRAMES;i++) {
+ frame = &usbvision->frame[i];
+ if(frame->grabstate == FrameState_Unused) {
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = FrameState_Ready;
+ frame->scanstate = ScanState_Scanning;
+ frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&frame->frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+ }
+ }
+
+ /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */
+ if (list_empty(&(usbvision->outqueue))) {
+ if(noblock)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ frame = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ /* An error returns an empty frame */
+ if (frame->grabstate == FrameState_Error) {
+ frame->bytes_read = 0;
+ return 0;
+ }
+
+ PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__,
+ frame->index, frame->bytes_read, frame->scanlength);
+
+ /* copy bytes to user space; we allow for partials reads */
+ if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
+ count = frame->scanlength - frame->bytes_read;
+
+ if (copy_to_user(buf, frame->data + frame->bytes_read, count)) {
+ return -EFAULT;
+ }
+
+ frame->bytes_read += count;
+ PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__,
+ (unsigned long)count, frame->bytes_read);
+
+ // For now, forget the frame if it has not been read in one shot.
+/* if (frame->bytes_read >= frame->scanlength) {// All data has been read */
+ frame->bytes_read = 0;
+
+ /* Mark it as available to be used again. */
+ usbvision->frame[frmx].grabstate = FrameState_Unused;
+/* } */
+
+ return count;
+}
+
+static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+ down(&usbvision->lock);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision)) {
+ up(&usbvision->lock);
+ return -EFAULT;
+ }
+
+ if (!(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel)) {
+ up(&usbvision->lock);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+ if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == USBVISION_NUMFRAMES) {
+ PDEBUG(DBG_MMAP, "mmap: user supplied mapping address is out of range");
+ up(&usbvision->lock);
+ return -EINVAL;
+ }
+
+ /* VM_IO is eventually going to replace PageReserved altogether */
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+
+ pos = usbvision->frame[i].data;
+ while (size > 0) {
+
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
+ up(&usbvision->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ up(&usbvision->lock);
+ return 0;
+}
+
+
+/*
+ * Here comes the stuff for radio on usbvision based devices
+ *
+ */
+static int usbvision_radio_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+ struct v4l2_frequency freq;
+ int errCode = 0;
+
+ PDEBUG(DBG_IO, "%s:", __FUNCTION__);
+
+ down(&usbvision->lock);
+
+ if (usbvision->user) {
+ err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__);
+ errCode = -EBUSY;
+ }
+ else {
+ if(PowerOnAtOpen) {
+ usbvision_reset_powerOffTimer(usbvision);
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_init_i2c(usbvision);
+ }
+ }
+
+ // If so far no errors then we shall start the radio
+ usbvision->radio = 1;
+ call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type);
+ freq.frequency = 1517; //SWR3 @ 94.8MHz
+ call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq);
+ usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
+ usbvision->user++;
+ }
+
+ if (errCode) {
+ if (PowerOnAtOpen) {
+ usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+ up(&usbvision->lock);
+ return errCode;
+}
+
+
+static int usbvision_radio_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+ int errCode = 0;
+
+ PDEBUG(DBG_IO, "");
+
+ down(&usbvision->lock);
+
+ usbvision_audio_off(usbvision);
+ usbvision->radio=0;
+ usbvision->user--;
+
+ if (PowerOnAtOpen) {
+ usbvision_set_powerOffTimer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ up(&usbvision->lock);
+
+ if (usbvision->remove_pending) {
+ info("%s: Final disconnect", __FUNCTION__);
+ usbvision_release(usbvision);
+ }
+
+
+ PDEBUG(DBG_IO, "success");
+
+ return errCode;
+}
+
+static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *vc=arg;
+
+ memset(vc, 0, sizeof(*vc));
+ strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+ strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString,
+ sizeof(vc->card));
+ strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+ sizeof(vc->bus_info));
+ vc->version = USBVISION_DRIVER_VERSION;
+ vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+ PDEBUG(DBG_IO, "VIDIOC_QUERYCAP");
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+ int id=ctrl->id;
+
+ memset(ctrl,0,sizeof(*ctrl));
+ ctrl->id=id;
+
+ call_i2c_clients(usbvision, cmd, arg);
+ PDEBUG(DBG_IO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type);
+
+ if (ctrl->type)
+ return 0;
+ else
+ return -EINVAL;
+
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
+ PDEBUG(DBG_IO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+ return 0;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
+ PDEBUG(DBG_IO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+ return 0;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+
+ if (t->index > 0)
+ return -EINVAL;
+
+ memset(t,0,sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ /* Let clients fill in the remainder of this struct */
+ call_i2c_clients(usbvision,VIDIOC_G_TUNER,t);
+ PDEBUG(DBG_IO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc);
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *vt = arg;
+
+ // Only no or one tuner for now
+ if (!usbvision->have_tuner || vt->index)
+ return -EINVAL;
+ /* let clients handle this */
+ call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);
+
+ PDEBUG(DBG_IO, "VIDIOC_S_TUNER");
+ return 0;
+ }
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+
+ memset(a,0,sizeof(*a));
+ strcpy(a->name,"Radio");
+ PDEBUG(DBG_IO, "VIDIOC_G_AUDIO");
+ return 0;
+ }
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_STD:
+ return 0;
+
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ memset(f,0,sizeof(*f));
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = usbvision->freq;
+ call_i2c_clients(usbvision, cmd, f);
+ PDEBUG(DBG_IO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency);
+
+ return 0;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ usbvision->freq = f->frequency;
+ call_i2c_clients(usbvision, cmd, f);
+ PDEBUG(DBG_IO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency);
+
+ return 0;
+ }
+ default:
+ {
+ PDEBUG(DBG_IO, "%s: Unknown command %x", __FUNCTION__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ }
+ return 0;
+}
+
+
+static int usbvision_radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl);
+}
+
+
+/*
+ * Here comes the stuff for vbi on usbvision based devices
+ *
+ */
+static int usbvision_vbi_open(struct inode *inode, struct file *file)
+{
+ /* TODO */
+ return -EINVAL;
+
+}
+
+static int usbvision_vbi_close(struct inode *inode, struct file *file)
+{
+ /* TODO */
+ return -EINVAL;
+}
+
+static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ /* TODO */
+ return -EINVAL;
+}
+
+static int usbvision_vbi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl);
+}
+
+
+//
+// Video registration stuff
+//
+
+// Video template
+static struct file_operations usbvision_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_v4l2_open,
+ .release = usbvision_v4l2_close,
+ .read = usbvision_v4l2_read,
+ .mmap = usbvision_v4l2_mmap,
+ .ioctl = usbvision_v4l2_ioctl,
+ .llseek = no_llseek,
+};
+static struct video_device usbvision_video_template = {
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_USBVISION,
+ .fops = &usbvision_fops,
+ .name = "usbvision-video",
+ .release = video_device_release,
+ .minor = -1,
+};
+
+
+// Radio template
+static struct file_operations usbvision_radio_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_radio_open,
+ .release = usbvision_radio_close,
+ .ioctl = usbvision_radio_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct video_device usbvision_radio_template=
+{
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_USBVISION,
+ .fops = &usbvision_radio_fops,
+ .release = video_device_release,
+ .name = "usbvision-radio",
+ .minor = -1,
+};
+
+
+// vbi template
+static struct file_operations usbvision_vbi_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_vbi_open,
+ .release = usbvision_vbi_close,
+ .ioctl = usbvision_vbi_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct video_device usbvision_vbi_template=
+{
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_USBVISION,
+ .fops = &usbvision_vbi_fops,
+ .release = video_device_release,
+ .name = "usbvision-vbi",
+ .minor = -1,
+};
+
+
+static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+ struct video_device *vdev_template,
+ char *name)
+{
+ struct usb_device *usb_dev = usbvision->dev;
+ struct video_device *vdev;
+
+ if (usb_dev == NULL) {
+ err("%s: usbvision->dev is not set", __FUNCTION__);
+ return NULL;
+ }
+
+ vdev = video_device_alloc();
+ if (NULL == vdev) {
+ return NULL;
+ }
+ *vdev = *vdev_template;
+// vdev->minor = -1;
+ vdev->dev = &usb_dev->dev;
+ snprintf(vdev->name, sizeof(vdev->name), "%s", name);
+ video_set_drvdata(vdev, usbvision);
+ return vdev;
+}
+
+// unregister video4linux devices
+static void usbvision_unregister_video(struct usb_usbvision *usbvision)
+{
+ // vbi Device:
+ if (usbvision->vbi) {
+ PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f);
+ if (usbvision->vbi->minor != -1) {
+ video_unregister_device(usbvision->vbi);
+ }
+ else {
+ video_device_release(usbvision->vbi);
+ }
+ usbvision->vbi = NULL;
+ }
+
+ // Radio Device:
+ if (usbvision->rdev) {
+ PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f);
+ if (usbvision->rdev->minor != -1) {
+ video_unregister_device(usbvision->rdev);
+ }
+ else {
+ video_device_release(usbvision->rdev);
+ }
+ usbvision->rdev = NULL;
+ }
+
+ // Video Device:
+ if (usbvision->vdev) {
+ PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f);
+ if (usbvision->vdev->minor != -1) {
+ video_unregister_device(usbvision->vdev);
+ }
+ else {
+ video_device_release(usbvision->vdev);
+ }
+ usbvision->vdev = NULL;
+ }
+}
+
+// register video4linux devices
+static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
+{
+ // Video Device:
+ usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video");
+ if (usbvision->vdev == NULL) {
+ goto err_exit;
+ }
+ if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) {
+ goto err_exit;
+ }
+ info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f);
+
+ // Radio Device:
+ if (usbvision_device_data[usbvision->DevModel].Radio) {
+ // usbvision has radio
+ usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio");
+ if (usbvision->rdev == NULL) {
+ goto err_exit;
+ }
+ if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) {
+ goto err_exit;
+ }
+ info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f);
+ }
+ // vbi Device:
+ if (usbvision_device_data[usbvision->DevModel].vbi) {
+ usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI");
+ if (usbvision->vdev == NULL) {
+ goto err_exit;
+ }
+ if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) {
+ goto err_exit;
+ }
+ info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f);
+ }
+ // all done
+ return 0;
+
+ err_exit:
+ err("USBVision[%d]: video_register_device() failed", usbvision->nr);
+ usbvision_unregister_video(usbvision);
+ return -1;
+}
+
+/*
+ * usbvision_alloc()
+ *
+ * This code allocates the struct usb_usbvision. It is filled with default values.
+ *
+ * Returns NULL on error, a pointer to usb_usbvision else.
+ *
+ */
+static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
+{
+ struct usb_usbvision *usbvision;
+
+ if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) {
+ goto err_exit;
+ }
+
+ usbvision->dev = dev;
+
+ init_MUTEX(&usbvision->lock); /* to 1 == available */
+
+ // prepare control urb for control messages during interrupts
+ usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (usbvision->ctrlUrb == NULL) {
+ goto err_exit;
+ }
+ init_waitqueue_head(&usbvision->ctrlUrb_wq);
+ init_MUTEX(&usbvision->ctrlUrbLock); /* to 1 == available */
+
+ usbvision_init_powerOffTimer(usbvision);
+
+ return usbvision;
+
+err_exit:
+ if (usbvision && usbvision->ctrlUrb) {
+ usb_free_urb(usbvision->ctrlUrb);
+ }
+ if (usbvision) {
+ kfree(usbvision);
+ }
+ return NULL;
+}
+
+/*
+ * usbvision_release()
+ *
+ * This code does final release of struct usb_usbvision. This happens
+ * after the device is disconnected -and- all clients closed their files.
+ *
+ */
+static void usbvision_release(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ down(&usbvision->lock);
+
+ usbvision_reset_powerOffTimer(usbvision);
+
+ usbvision->initialized = 0;
+
+ up(&usbvision->lock);
+
+ usbvision_remove_sysfs(usbvision->vdev);
+ usbvision_unregister_video(usbvision);
+
+ if (usbvision->ctrlUrb) {
+ usb_free_urb(usbvision->ctrlUrb);
+ }
+
+ kfree(usbvision);
+
+ PDEBUG(DBG_PROBE, "success");
+}
+
+
+/******************************** usb interface *****************************************/
+
+static void usbvision_configure_video(struct usb_usbvision *usbvision)
+{
+ int model,i;
+
+ if (usbvision == NULL)
+ return;
+
+ model = usbvision->DevModel;
+ usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24;
+
+ if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) {
+ usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff;
+ } else {
+ usbvision->Vin_Reg2_Preset = 0;
+ }
+
+ for (i = 0; i < TVNORMS; i++)
+ if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode)
+ break;
+ if (i == TVNORMS)
+ i = 0;
+ usbvision->tvnorm = &tvnorms[i]; /* set default norm */
+
+ usbvision->video_inputs = usbvision_device_data[model].VideoChannels;
+ usbvision->ctl_input = 0;
+
+ /* This should be here to make i2c clients to be able to register */
+ usbvision_audio_off(usbvision); //first switch off audio
+ if (!PowerOnAtOpen) {
+ usbvision_power_on(usbvision); //and then power up the noisy tuner
+ usbvision_init_i2c(usbvision);
+ }
+}
+
+/*
+ * usbvision_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like USBVISION video device
+ *
+ */
+static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ __u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
+ const struct usb_host_interface *interface;
+ struct usb_usbvision *usbvision = NULL;
+ const struct usb_endpoint_descriptor *endpoint;
+ int model;
+
+ PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
+ dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+ /* Is it an USBVISION video dev? */
+ model = 0;
+ for(model = 0; usbvision_device_data[model].idVendor; model++) {
+ if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) {
+ continue;
+ }
+ if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) {
+ continue;
+ }
+
+ info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString);
+ break;
+ }
+
+ if (usbvision_device_data[model].idVendor == 0) {
+ return -ENODEV; //no matching device
+ }
+ if (usbvision_device_data[model].Interface >= 0) {
+ interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0];
+ }
+ else {
+ interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+ }
+ endpoint = &interface->endpoint[1].desc;
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) {
+ err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum);
+ err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes);
+ return -ENODEV;
+ }
+ if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+ err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum);
+ return -ENODEV;
+ }
+
+ usb_get_dev(dev);
+
+ if ((usbvision = usbvision_alloc(dev)) == NULL) {
+ err("%s: couldn't allocate USBVision struct", __FUNCTION__);
+ return -ENOMEM;
+ }
+ if (dev->descriptor.bNumConfigurations > 1) {
+ usbvision->bridgeType = BRIDGE_NT1004;
+ }
+ else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") {
+ usbvision->bridgeType = BRIDGE_NT1005;
+ }
+ else {
+ usbvision->bridgeType = BRIDGE_NT1003;
+ }
+ PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType);
+
+ down(&usbvision->lock);
+
+ usbvision->nr = usbvision_nr++;
+
+ usbvision->have_tuner = usbvision_device_data[model].Tuner;
+ if (usbvision->have_tuner) {
+ usbvision->tuner_type = usbvision_device_data[model].TunerType;
+ }
+
+ usbvision->tuner_addr = ADDR_UNSET;
+
+ usbvision->DevModel = model;
+ usbvision->remove_pending = 0;
+ usbvision->iface = ifnum;
+ usbvision->ifaceAltInactive = 0;
+ usbvision->ifaceAltActive = 1;
+ usbvision->video_endp = endpoint->bEndpointAddress;
+ usbvision->isocPacketSize = 0;
+ usbvision->usb_bandwidth = 0;
+ usbvision->user = 0;
+ usbvision->streaming = Stream_Off;
+ usbvision_register_video(usbvision);
+ usbvision_configure_video(usbvision);
+ up(&usbvision->lock);
+
+
+ usb_set_intfdata (intf, usbvision);
+ usbvision_create_sysfs(usbvision->vdev);
+
+ PDEBUG(DBG_PROBE, "success");
+ return 0;
+}
+
+
+/*
+ * usbvision_disconnect()
+ *
+ * This procedure stops all driver activity, deallocates interface-private
+ * structure (pointed by 'ptr') and after that driver should be removable
+ * with no ill consequences.
+ *
+ */
+static void __devexit usbvision_disconnect(struct usb_interface *intf)
+{
+ struct usb_usbvision *usbvision = usb_get_intfdata(intf);
+
+ PDEBUG(DBG_PROBE, "");
+
+ if (usbvision == NULL) {
+ err("%s: usb_get_intfdata() failed", __FUNCTION__);
+ return;
+ }
+ usb_set_intfdata (intf, NULL);
+
+ down(&usbvision->lock);
+
+ // At this time we ask to cancel outstanding URBs
+ usbvision_stop_isoc(usbvision);
+
+ if (usbvision->power) {
+ usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_power_off(usbvision);
+ }
+ usbvision->remove_pending = 1; // Now all ISO data will be ignored
+
+ usb_put_dev(usbvision->dev);
+ usbvision->dev = NULL; // USB device is no more
+
+ up(&usbvision->lock);
+
+ if (usbvision->user) {
+ info("%s: In use, disconnect pending", __FUNCTION__);
+ wake_up_interruptible(&usbvision->wait_frame);
+ wake_up_interruptible(&usbvision->wait_stream);
+ }
+ else {
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_PROBE, "success");
+
+}
+
+static struct usb_driver usbvision_driver = {
+ .name = "usbvision",
+ .id_table = usbvision_table,
+ .probe = usbvision_probe,
+ .disconnect = usbvision_disconnect
+};
+
+/*
+ * customdevice_process()
+ *
+ * This procedure preprocesses CustomDevice parameter if any
+ *
+ */
+void customdevice_process(void)
+{
+ usbvision_device_data[0]=usbvision_device_data[1];
+ usbvision_table[0]=usbvision_table[1];
+
+ if(CustomDevice)
+ {
+ char *parse=CustomDevice;
+
+ PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice);
+
+ /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
+ usbvision_device_data[0].idVendor;
+ usbvision_device_data[0].idProduct;
+ usbvision_device_data[0].Interface;
+ usbvision_device_data[0].Codec;
+ usbvision_device_data[0].VideoChannels;
+ usbvision_device_data[0].VideoNorm;
+ usbvision_device_data[0].AudioChannels;
+ usbvision_device_data[0].Radio;
+ usbvision_device_data[0].Tuner;
+ usbvision_device_data[0].TunerType;
+ usbvision_device_data[0].Vin_Reg1;
+ usbvision_device_data[0].Vin_Reg2;
+ usbvision_device_data[0].X_Offset;
+ usbvision_device_data[0].Y_Offset;
+ usbvision_device_data[0].Dvi_yuv;
+ usbvision_device_data[0].ModelString;
+ */
+
+ rmspace(parse);
+ usbvision_device_data[0].ModelString="USBVISION Custom Device";
+
+ parse+=2;
+ sscanf(parse,"%x",&usbvision_device_data[0].idVendor);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor);
+ parse+=2;
+ sscanf(parse,"%x",&usbvision_device_data[0].idProduct);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct);
+ sscanf(parse,"%d",&usbvision_device_data[0].Interface);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface);
+ sscanf(parse,"%d",&usbvision_device_data[0].Codec);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec);
+ sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels);
+
+ switch(*parse)
+ {
+ case 'P':
+ PDEBUG(DBG_PROBE, "VideoNorm=PAL");
+ usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
+ break;
+
+ case 'S':
+ PDEBUG(DBG_PROBE, "VideoNorm=SECAM");
+ usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM;
+ break;
+
+ case 'N':
+ PDEBUG(DBG_PROBE, "VideoNorm=NTSC");
+ usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC;
+ break;
+
+ default:
+ PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)");
+ usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
+ break;
+ }
+ goto2next(parse);
+
+ sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels);
+ sscanf(parse,"%d",&usbvision_device_data[0].Radio);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio);
+ sscanf(parse,"%d",&usbvision_device_data[0].Tuner);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner);
+ sscanf(parse,"%d",&usbvision_device_data[0].TunerType);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType);
+ sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1);
+ sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2);
+ sscanf(parse,"%d",&usbvision_device_data[0].X_Offset);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset);
+ sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset);
+ goto2next(parse);
+ PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset);
+ sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv);
+ PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv);
+
+ //add to usbvision_table also
+ usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE;
+ usbvision_table[0].idVendor=usbvision_device_data[0].idVendor;
+ usbvision_table[0].idProduct=usbvision_device_data[0].idProduct;
+
+ }
+}
+
+
+
+/*
+ * usbvision_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ */
+static int __init usbvision_init(void)
+{
+ int errCode;
+
+ PDEBUG(DBG_PROBE, "");
+
+ PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled [video]");
+ PDEBUG(DBG_IO, "IO debugging is enabled [video]");
+ PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]");
+ PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]");
+
+ /* disable planar mode support unless compression enabled */
+ if (isocMode != ISOC_MODE_COMPRESS ) {
+ // FIXME : not the right way to set supported flag
+ usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420
+ usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P
+ }
+
+ customdevice_process();
+
+ errCode = usb_register(&usbvision_driver);
+
+ if (errCode == 0) {
+ info(DRIVER_DESC " : " USBVISION_VERSION_STRING);
+ PDEBUG(DBG_PROBE, "success");
+ }
+ return errCode;
+}
+
+static void __exit usbvision_exit(void)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usb_deregister(&usbvision_driver);
+ PDEBUG(DBG_PROBE, "success");
+}
+
+module_init(usbvision_init);
+module_exit(usbvision_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
new file mode 100644
index 0000000..0e7e3d6
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -0,0 +1,558 @@
+/*
+ * USBVISION.H
+ * usbvision header file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ *
+ * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ * v4l2 conversion by Thierry Merle <thierry.merle@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
+ * 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 __LINUX_USBVISION_H
+#define __LINUX_USBVISION_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <linux/videodev2.h>
+
+#define USBVISION_DEBUG /* Turn on debug messages */
+
+#ifndef VID_HARDWARE_USBVISION
+ #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */
+#endif
+
+#define USBVISION_PWR_REG 0x00
+ #define USBVISION_SSPND_EN (1 << 1)
+ #define USBVISION_RES2 (1 << 2)
+ #define USBVISION_PWR_VID (1 << 5)
+ #define USBVISION_E2_EN (1 << 7)
+#define USBVISION_CONFIG_REG 0x01
+#define USBVISION_ADRS_REG 0x02
+#define USBVISION_ALTER_REG 0x03
+#define USBVISION_FORCE_ALTER_REG 0x04
+#define USBVISION_STATUS_REG 0x05
+#define USBVISION_IOPIN_REG 0x06
+ #define USBVISION_IO_1 (1 << 0)
+ #define USBVISION_IO_2 (1 << 1)
+ #define USBVISION_AUDIO_IN 0
+ #define USBVISION_AUDIO_TV 1
+ #define USBVISION_AUDIO_RADIO 2
+ #define USBVISION_AUDIO_MUTE 3
+#define USBVISION_SER_MODE 0x07
+#define USBVISION_SER_ADRS 0x08
+#define USBVISION_SER_CONT 0x09
+#define USBVISION_SER_DAT1 0x0A
+#define USBVISION_SER_DAT2 0x0B
+#define USBVISION_SER_DAT3 0x0C
+#define USBVISION_SER_DAT4 0x0D
+#define USBVISION_EE_DATA 0x0E
+#define USBVISION_EE_LSBAD 0x0F
+#define USBVISION_EE_CONT 0x10
+#define USBVISION_DRM_CONT 0x12
+ #define USBVISION_REF (1 << 0)
+ #define USBVISION_RES_UR (1 << 2)
+ #define USBVISION_RES_FDL (1 << 3)
+ #define USBVISION_RES_VDW (1 << 4)
+#define USBVISION_DRM_PRM1 0x13
+#define USBVISION_DRM_PRM2 0x14
+#define USBVISION_DRM_PRM3 0x15
+#define USBVISION_DRM_PRM4 0x16
+#define USBVISION_DRM_PRM5 0x17
+#define USBVISION_DRM_PRM6 0x18
+#define USBVISION_DRM_PRM7 0x19
+#define USBVISION_DRM_PRM8 0x1A
+#define USBVISION_VIN_REG1 0x1B
+ #define USBVISION_8_422_SYNC 0x01
+ #define USBVISION_16_422_SYNC 0x02
+ #define USBVISION_VSNC_POL (1 << 3)
+ #define USBVISION_HSNC_POL (1 << 4)
+ #define USBVISION_FID_POL (1 << 5)
+ #define USBVISION_HVALID_PO (1 << 6)
+ #define USBVISION_VCLK_POL (1 << 7)
+#define USBVISION_VIN_REG2 0x1C
+ #define USBVISION_AUTO_FID (1 << 0)
+ #define USBVISION_NONE_INTER (1 << 1)
+ #define USBVISION_NOHVALID (1 << 2)
+ #define USBVISION_UV_ID (1 << 3)
+ #define USBVISION_FIX_2C (1 << 4)
+ #define USBVISION_SEND_FID (1 << 5)
+ #define USBVISION_KEEP_BLANK (1 << 7)
+#define USBVISION_LXSIZE_I 0x1D
+#define USBVISION_MXSIZE_I 0x1E
+#define USBVISION_LYSIZE_I 0x1F
+#define USBVISION_MYSIZE_I 0x20
+#define USBVISION_LX_OFFST 0x21
+#define USBVISION_MX_OFFST 0x22
+#define USBVISION_LY_OFFST 0x23
+#define USBVISION_MY_OFFST 0x24
+#define USBVISION_FRM_RATE 0x25
+#define USBVISION_LXSIZE_O 0x26
+#define USBVISION_MXSIZE_O 0x27
+#define USBVISION_LYSIZE_O 0x28
+#define USBVISION_MYSIZE_O 0x29
+#define USBVISION_FILT_CONT 0x2A
+#define USBVISION_VO_MODE 0x2B
+#define USBVISION_INTRA_CYC 0x2C
+#define USBVISION_STRIP_SZ 0x2D
+#define USBVISION_FORCE_INTRA 0x2E
+#define USBVISION_FORCE_UP 0x2F
+#define USBVISION_BUF_THR 0x30
+#define USBVISION_DVI_YUV 0x31
+#define USBVISION_AUDIO_CONT 0x32
+#define USBVISION_AUD_PK_LEN 0x33
+#define USBVISION_BLK_PK_LEN 0x34
+#define USBVISION_PCM_THR1 0x38
+#define USBVISION_PCM_THR2 0x39
+#define USBVISION_DIST_THR_L 0x3A
+#define USBVISION_DIST_THR_H 0x3B
+#define USBVISION_MAX_DIST_L 0x3C
+#define USBVISION_MAX_DIST_H 0x3D
+#define USBVISION_OP_CODE 0x33
+
+#define MAX_BYTES_PER_PIXEL 4
+
+#define MIN_FRAME_WIDTH 64
+#define MAX_USB_WIDTH 320 //384
+#define MAX_FRAME_WIDTH 320 //384 /*streching sometimes causes crashes*/
+
+#define MIN_FRAME_HEIGHT 48
+#define MAX_USB_HEIGHT 240 //288
+#define MAX_FRAME_HEIGHT 240 //288 /*Streching sometimes causes crashes*/
+
+#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL)
+#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) //bytesize of clipmask
+
+#define USBVISION_URB_FRAMES 32
+#define USBVISION_MAX_ISOC_PACKET_SIZE 959 // NT1003 Specs Document says 1023
+
+#define USBVISION_NUM_HEADERMARKER 20
+#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */
+#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */
+
+#define USBVISION_POWEROFF_TIME 3 * (HZ) // 3 seconds
+
+
+#define FRAMERATE_MIN 0
+#define FRAMERATE_MAX 31
+
+enum {
+ ISOC_MODE_YUV422 = 0x03,
+ ISOC_MODE_YUV420 = 0x14,
+ ISOC_MODE_COMPRESS = 0x60,
+};
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16) + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16) + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
+ int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+ mm_y = (my) - 16; \
+ mm_u = (mu) - 128; \
+ mm_v = (mv) - 128; \
+ mm_yc= mm_y * 76284; \
+ mm_b = (mm_yc + 132252*mm_v ) >> 16; \
+ mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \
+ mm_r = (mm_yc + 104595*mm_u ) >> 16; \
+ mb = LIMIT_RGB(mm_b); \
+ mg = LIMIT_RGB(mm_g); \
+ mr = LIMIT_RGB(mm_r); \
+}
+
+/* Debugging aid */
+#define USBVISION_SAY_AND_WAIT(what) { \
+ wait_queue_head_t wq; \
+ init_waitqueue_head(&wq); \
+ printk(KERN_INFO "Say: %s\n", what); \
+ interruptible_sleep_on_timeout (&wq, HZ*3); \
+}
+
+/*
+ * This macro checks if usbvision is still operational. The 'usbvision'
+ * pointer must be valid, usbvision->dev must be valid, we are not
+ * removing the device and the device has not erred on us.
+ */
+#define USBVISION_IS_OPERATIONAL(udevice) (\
+ (udevice != NULL) && \
+ ((udevice)->dev != NULL) && \
+ ((udevice)->last_error == 0) && \
+ (!(udevice)->remove_pending))
+
+/* I2C structures */
+struct i2c_algo_usb_data {
+ void *data; /* private data for lowlevel routines */
+ int (*inb) (void *data, unsigned char addr, char *buf, short len);
+ int (*outb) (void *data, unsigned char addr, char *buf, short len);
+
+ /* local settings */
+ int udelay;
+ int mdelay;
+ int timeout;
+};
+
+#define I2C_USB_ADAP_MAX 16
+
+/* ----------------------------------------------------------------- */
+/* usbvision video structures */
+/* ----------------------------------------------------------------- */
+enum ScanState {
+ ScanState_Scanning, /* Scanning for header */
+ ScanState_Lines /* Parsing lines */
+};
+
+/* Completion states of the data parser */
+enum ParseState {
+ ParseState_Continue, /* Just parse next item */
+ ParseState_NextFrame, /* Frame done, send it to V4L */
+ ParseState_Out, /* Not enough data for frame */
+ ParseState_EndParse /* End parsing */
+};
+
+enum FrameState {
+ FrameState_Unused, /* Unused (no MCAPTURE) */
+ FrameState_Ready, /* Ready to start grabbing */
+ FrameState_Grabbing, /* In the process of being grabbed into */
+ FrameState_Done, /* Finished grabbing, but not been synced yet */
+ FrameState_DoneHold, /* Are syncing or reading */
+ FrameState_Error, /* Something bad happened while processing */
+};
+
+/* stream states */
+enum StreamState {
+ Stream_Off, /* Driver streaming is completely OFF */
+ Stream_Idle, /* Driver streaming is ready to be put ON by the application */
+ Stream_Interrupt, /* Driver streaming must be interrupted */
+ Stream_On, /* Driver streaming is put ON by the application */
+};
+
+enum IsocState {
+ IsocState_InFrame, /* Isoc packet is member of frame */
+ IsocState_NoFrame, /* Isoc packet is not member of any frame */
+};
+
+struct usb_device;
+
+struct usbvision_sbuf {
+ char *data;
+ struct urb *urb;
+};
+
+#define USBVISION_MAGIC_1 0x55
+#define USBVISION_MAGIC_2 0xAA
+#define USBVISION_HEADER_LENGTH 0x0c
+#define USBVISION_SAA7111_ADDR 0x48
+#define USBVISION_SAA7113_ADDR 0x4a
+#define USBVISION_IIC_LRACK 0x20
+#define USBVISION_IIC_LRNACK 0x30
+#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7)
+
+struct usbvision_v4l2_format_st {
+ int supported;
+ int bytes_per_pixel;
+ int depth;
+ int format;
+ char *desc;
+};
+#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format)
+
+struct usbvision_frame_header {
+ unsigned char magic_1; /* 0 magic */
+ unsigned char magic_2; /* 1 magic */
+ unsigned char headerLength; /* 2 */
+ unsigned char frameNum; /* 3 */
+ unsigned char framePhase; /* 4 */
+ unsigned char frameLatency; /* 5 */
+ unsigned char dataFormat; /* 6 */
+ unsigned char formatParam; /* 7 */
+ unsigned char frameWidthLo; /* 8 */
+ unsigned char frameWidthHi; /* 9 */
+ unsigned char frameHeightLo; /* 10 */
+ unsigned char frameHeightHi; /* 11 */
+ __u16 frameWidth; /* 8 - 9 after endian correction*/
+ __u16 frameHeight; /* 10 - 11 after endian correction*/
+};
+
+/* tvnorms */
+struct usbvision_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ /* mode for saa7113h */
+ int mode;
+};
+
+struct usbvision_frame {
+ char *data; /* Frame buffer */
+ struct usbvision_frame_header isocHeader; /* Header from stream */
+
+ int width; /* Width application is expecting */
+ int height; /* Height */
+ int index; /* Frame index */
+ int frmwidth; /* Width the frame actually is */
+ int frmheight; /* Height */
+
+ volatile int grabstate; /* State of grabbing */
+ int scanstate; /* State of scanning */
+
+ struct list_head frame;
+
+ int curline; /* Line of frame we're working on */
+
+ long scanlength; /* uncompressed, raw data length of frame */
+ long bytes_read; /* amount of scanlength that has been read from data */
+ struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/
+ int v4l2_linesize; /* bytes for one videoline*/
+ struct timeval timestamp;
+ int sequence; // How many video frames we send to user
+};
+
+#define CODEC_SAA7113 7113
+#define CODEC_SAA7111 7111
+#define BRIDGE_NT1003 1003
+#define BRIDGE_NT1004 1004
+#define BRIDGE_NT1005 1005
+
+struct usbvision_device_data_st {
+ int idVendor;
+ int idProduct;
+ int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
+ int Codec;
+ int VideoChannels;
+ __u64 VideoNorm;
+ int AudioChannels;
+ int Radio;
+ int vbi;
+ int Tuner;
+ int TunerType;
+ int Vin_Reg1;
+ int Vin_Reg2;
+ int X_Offset;
+ int Y_Offset;
+ int Dvi_yuv;
+ char *ModelString;
+};
+
+/* Declared on usbvision-cards.c */
+extern struct usbvision_device_data_st usbvision_device_data[];
+extern struct usb_device_id usbvision_table[];
+
+struct usb_usbvision {
+ struct video_device *vdev; /* Video Device */
+ struct video_device *rdev; /* Radio Device */
+ struct video_device *vbi; /* VBI Device */
+
+ /* i2c Declaration Section*/
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_usb_data i2c_algo;
+ struct i2c_client i2c_client;
+
+ struct urb *ctrlUrb;
+ unsigned char ctrlUrbBuffer[8];
+ int ctrlUrbBusy;
+ struct usb_ctrlrequest ctrlUrbSetup;
+ wait_queue_head_t ctrlUrb_wq; // Processes waiting
+ struct semaphore ctrlUrbLock;
+
+ /* configuration part */
+ int have_tuner;
+ int tuner_type;
+ int tuner_addr;
+ int bridgeType; // NT1003, NT1004, NT1005
+ int channel;
+ int radio;
+ int video_inputs; // # of inputs
+ unsigned long freq;
+ int AudioMute;
+ int AudioChannel;
+ int isocMode; // format of video data for the usb isoc-transfer
+ unsigned int nr; // Number of the device
+
+ /* Device structure */
+ struct usb_device *dev;
+ unsigned char iface; /* Video interface number */
+ unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */
+ unsigned char Vin_Reg2_Preset;
+ struct semaphore lock;
+ struct timer_list powerOffTimer;
+ struct work_struct powerOffWork;
+ int power; /* is the device powered on? */
+ int user; /* user count for exclusive use */
+ int initialized; /* Had we already sent init sequence? */
+ int DevModel; /* What type of USBVISION device we got? */
+ enum StreamState streaming; /* Are we streaming Isochronous? */
+ int last_error; /* What calamity struck us? */
+ int curwidth; /* width of the frame the device is currently set to*/
+ int curheight; /* height of the frame the device is currently set to*/
+ int stretch_width; /* stretch-factor for frame width (from usb to screen)*/
+ int stretch_height; /* stretch-factor for frame height (from usb to screen)*/
+ char *fbuf; /* Videodev buffer area for mmap*/
+ int max_frame_size; /* Bytes in one video frame */
+ int fbuf_size; /* Videodev buffer size */
+ spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */
+ struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */
+ wait_queue_head_t wait_frame; /* Processes waiting */
+ wait_queue_head_t wait_stream; /* Processes waiting */
+ struct usbvision_frame *curFrame; // pointer to current frame, set by usbvision_find_header
+ struct usbvision_frame frame[USBVISION_NUMFRAMES]; // frame buffer
+ struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; // S buffering
+ volatile int remove_pending; /* If set then about to exit */
+
+ /* Scratch space from the Isochronous Pipe.*/
+ unsigned char *scratch;
+ int scratch_read_ptr;
+ int scratch_write_ptr;
+ int scratch_headermarker[USBVISION_NUM_HEADERMARKER];
+ int scratch_headermarker_read_ptr;
+ int scratch_headermarker_write_ptr;
+ enum IsocState isocstate;
+ struct usbvision_v4l2_format_st palette;
+
+ struct v4l2_capability vcap; /* Video capabilities */
+ unsigned int ctl_input; /* selected input */
+ struct usbvision_tvnorm *tvnorm; /* selected tv norm */
+ unsigned char video_endp; /* 0x82 for USBVISION devices based */
+
+ // Decompression stuff:
+ unsigned char *IntraFrameBuffer; /* Buffer for reference frame */
+ int BlockPos; //for test only
+ int requestIntra; // 0 = normal; 1 = intra frame is requested;
+ int lastIsocFrameNum; // check for lost isoc frames
+ int isocPacketSize; // need to calculate usedBandwidth
+ int usedBandwidth; // used bandwidth 0-100%, need to set comprLevel
+ int comprLevel; // How strong (100) or weak (0) is compression
+ int lastComprLevel; // How strong (100) or weak (0) was compression
+ int usb_bandwidth; /* Mbit/s */
+
+ /* Statistics that can be overlayed on the screen */
+ unsigned long isocUrbCount; // How many URBs we received so far
+ unsigned long urb_length; /* Length of last URB */
+ unsigned long isocDataCount; /* How many bytes we received */
+ unsigned long header_count; /* How many frame headers we found */
+ unsigned long scratch_ovf_count; /* How many times we overflowed scratch */
+ unsigned long isocSkipCount; /* How many empty ISO packets received */
+ unsigned long isocErrCount; /* How many bad ISO packets received */
+ unsigned long isocPacketCount; // How many packets we totally got
+ unsigned long timeInIrq; // How long do we need for interrupt
+ int isocMeasureBandwidthCount;
+ int frame_num; // How many video frames we send to user
+ int maxStripLen; // How big is the biggest strip
+ int comprBlockPos;
+ int stripLenErrors; // How many times was BlockPos greater than StripLen
+ int stripMagicErrors;
+ int stripLineNumberErrors;
+ int ComprBlockTypes[4];
+};
+
+
+/* --------------------------------------------------------------- */
+/* defined in usbvision-i2c.c */
+/* i2c-algo-usb declaration */
+/* --------------------------------------------------------------- */
+
+int usbvision_i2c_usb_add_bus(struct i2c_adapter *);
+int usbvision_i2c_usb_del_bus(struct i2c_adapter *);
+
+static inline void *i2c_get_algo_usb_data (struct i2c_algo_usb_data *dev)
+{
+ return dev->data;
+}
+
+static inline void i2c_set_algo_usb_data (struct i2c_algo_usb_data *dev, void *data)
+{
+ dev->data = data;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions */
+/* ----------------------------------------------------------------------- */
+int usbvision_init_i2c(struct usb_usbvision *usbvision);
+void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg);
+
+/* defined in usbvision-core.c */
+void *usbvision_rvmalloc(unsigned long size);
+void usbvision_rvfree(void *mem, unsigned long size);
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg);
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+ unsigned char value);
+
+int usbvision_frames_alloc(struct usb_usbvision *usbvision);
+void usbvision_frames_free(struct usb_usbvision *usbvision);
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision);
+void usbvision_scratch_free(struct usb_usbvision *usbvision);
+int usbvision_sbuf_alloc(struct usb_usbvision *usbvision);
+void usbvision_sbuf_free(struct usb_usbvision *usbvision);
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision);
+void usbvision_decompress_free(struct usb_usbvision *usbvision);
+
+int usbvision_setup(struct usb_usbvision *usbvision,int format);
+int usbvision_init_isoc(struct usb_usbvision *usbvision);
+int usbvision_restart_isoc(struct usb_usbvision *usbvision);
+void usbvision_stop_isoc(struct usb_usbvision *usbvision);
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel);
+int usbvision_audio_off(struct usb_usbvision *usbvision);
+
+int usbvision_begin_streaming(struct usb_usbvision *usbvision);
+void usbvision_empty_framequeues(struct usb_usbvision *dev);
+int usbvision_stream_interrupt(struct usb_usbvision *dev);
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel);
+int usbvision_set_input(struct usb_usbvision *usbvision);
+int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height);
+
+void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision);
+void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision);
+void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision);
+int usbvision_power_off(struct usb_usbvision *usbvision);
+int usbvision_power_on(struct usb_usbvision *usbvision);
+
+#endif /* __LINUX_USBVISION_H */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c
index 1d899e2..8a13e59 100644
--- a/drivers/media/video/v4l1-compat.c
+++ b/drivers/media/video/v4l1-compat.c
@@ -350,6 +350,7 @@ v4l_compat_translate_ioctl(struct inode *inode,
struct video_buffer *buffer = arg;
memset(buffer, 0, sizeof(*buffer));
+ memset(&fbuf2, 0, sizeof(fbuf2));
err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
if (err < 0) {
@@ -616,6 +617,7 @@ v4l_compat_translate_ioctl(struct inode *inode,
case VIDIOCSPICT: /* set tone controls & partial capture format */
{
struct video_picture *pict = arg;
+ memset(&fbuf2, 0, sizeof(fbuf2));
set_v4l_control(inode, file,
V4L2_CID_BRIGHTNESS, pict->brightness, drv);
@@ -708,12 +710,22 @@ v4l_compat_translate_ioctl(struct inode *inode,
}
case VIDIOCSTUNER: /* select a tuner input */
{
- err = 0;
+ struct video_tuner *tun = arg;
+ struct v4l2_tuner t;
+ memset(&t,0,sizeof(t));
+
+ t.index=tun->tuner;
+
+ err = drv(inode, file, VIDIOC_S_INPUT, &t);
+ if (err < 0)
+ dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err);
+
break;
}
case VIDIOCGFREQ: /* get frequency */
{
unsigned long *freq = arg;
+ memset(&freq2,0,sizeof(freq2));
freq2.tuner = 0;
err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
@@ -726,8 +738,8 @@ v4l_compat_translate_ioctl(struct inode *inode,
case VIDIOCSFREQ: /* set frequency */
{
unsigned long *freq = arg;
+ memset(&freq2,0,sizeof(freq2));
- freq2.tuner = 0;
drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
freq2.frequency = *freq;
err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2);
@@ -738,6 +750,7 @@ v4l_compat_translate_ioctl(struct inode *inode,
case VIDIOCGAUDIO: /* get audio properties/controls */
{
struct video_audio *aud = arg;
+ memset(&aud2,0,sizeof(aud2));
err = drv(inode, file, VIDIOC_G_AUDIO, &aud2);
if (err < 0) {
@@ -898,6 +911,7 @@ v4l_compat_translate_ioctl(struct inode *inode,
{
int *i = arg;
+ memset(&buf2,0,sizeof(buf2));
buf2.index = *i;
buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(inode, file, VIDIOC_QUERYBUF, &buf2);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 78d28b0..752c82c 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -87,6 +87,78 @@ MODULE_LICENSE("GPL");
*/
+char *v4l2_norm_to_name(v4l2_std_id id)
+{
+ char *name;
+
+ switch (id) {
+ case V4L2_STD_PAL:
+ name="PAL"; break;
+ case V4L2_STD_PAL_BG:
+ name="PAL-BG"; break;
+ case V4L2_STD_PAL_DK:
+ name="PAL-DK"; break;
+ case V4L2_STD_PAL_B:
+ name="PAL-B"; break;
+ case V4L2_STD_PAL_B1:
+ name="PAL-B1"; break;
+ case V4L2_STD_PAL_G:
+ name="PAL-G"; break;
+ case V4L2_STD_PAL_H:
+ name="PAL-H"; break;
+ case V4L2_STD_PAL_I:
+ name="PAL-I"; break;
+ case V4L2_STD_PAL_D:
+ name="PAL-D"; break;
+ case V4L2_STD_PAL_D1:
+ name="PAL-D1"; break;
+ case V4L2_STD_PAL_K:
+ name="PAL-K"; break;
+ case V4L2_STD_PAL_M:
+ name="PAL-M"; break;
+ case V4L2_STD_PAL_N:
+ name="PAL-N"; break;
+ case V4L2_STD_PAL_Nc:
+ name="PAL-Nc"; break;
+ case V4L2_STD_PAL_60:
+ name="PAL-60"; break;
+ case V4L2_STD_NTSC:
+ name="NTSC"; break;
+ case V4L2_STD_NTSC_M:
+ name="NTSC-M"; break;
+ case V4L2_STD_NTSC_M_JP:
+ name="NTSC-M-JP"; break;
+ case V4L2_STD_NTSC_443:
+ name="NTSC-443"; break;
+ case V4L2_STD_NTSC_M_KR:
+ name="NTSC-M-KR"; break;
+ case V4L2_STD_SECAM:
+ name="SECAM"; break;
+ case V4L2_STD_SECAM_DK:
+ name="SECAM-DK"; break;
+ case V4L2_STD_SECAM_B:
+ name="SECAM-B"; break;
+ case V4L2_STD_SECAM_D:
+ name="SECAM-D"; break;
+ case V4L2_STD_SECAM_G:
+ name="SECAM-G"; break;
+ case V4L2_STD_SECAM_H:
+ name="SECAM-H"; break;
+ case V4L2_STD_SECAM_K:
+ name="SECAM-K"; break;
+ case V4L2_STD_SECAM_K1:
+ name="SECAM-K1"; break;
+ case V4L2_STD_SECAM_L:
+ name="SECAM-L"; break;
+ case V4L2_STD_SECAM_LC:
+ name="SECAM-LC"; break;
+ default:
+ name="Unknown"; break;
+ }
+
+ return name;
+}
+
/* Fill in the fields of a v4l2_standard structure according to the
'id' and 'transmission' parameters. Returns negative on error. */
int v4l2_video_std_construct(struct v4l2_standard *vs,
@@ -184,11 +256,13 @@ char *v4l2_field_names[] = {
};
char *v4l2_type_names[] = {
- [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap",
- [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over",
- [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out",
- [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
- [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
+ [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap",
+ [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out",
+ [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
+ [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
+ [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
+ [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out",
};
static char *v4l2_memory_names[] = {
@@ -1451,6 +1525,7 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
/* ----------------------------------------------------------------- */
+EXPORT_SYMBOL(v4l2_norm_to_name);
EXPORT_SYMBOL(v4l2_video_std_construct);
EXPORT_SYMBOL(v4l2_prio_init);
diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c
index 2ae3fb2..290e641 100644
--- a/drivers/media/video/videocodec.c
+++ b/drivers/media/video/videocodec.c
@@ -346,7 +346,7 @@ videocodec_build_table (void)
size);
kfree(videocodec_buf);
- videocodec_buf = (char *) kmalloc(size, GFP_KERNEL);
+ videocodec_buf = kmalloc(size, GFP_KERNEL);
i = 0;
i += scnprintf(videocodec_buf + i, size - 1,
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 41ec0c4..6a0e8ca 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -342,7 +342,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
dbgarg (cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
"bytesused=%d, flags=0x%08d, "
- "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n",
+ "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n",
(p->timestamp.tv_sec/3600),
(int)(p->timestamp.tv_sec/60)%60,
(int)(p->timestamp.tv_sec%60),
@@ -352,7 +352,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
p->bytesused,p->flags,
p->field,p->sequence,
prt_names(p->memory,v4l2_memory_names),
- p->m.userptr);
+ p->m.userptr, p->length);
dbgarg2 ("timecode= %02d:%02d:%02d type=%d, "
"flags=0x%08d, frames=%d, userbits=0x%08x\n",
tc->hours,tc->minutes,tc->seconds,
@@ -369,9 +369,13 @@ static inline void dbgrect(struct video_device *vfd, char *s,
static inline void v4l_print_pix_fmt (struct video_device *vfd,
struct v4l2_pix_format *fmt)
{
- dbgarg2 ("width=%d, height=%d, format=0x%08x, field=%s, "
+ dbgarg2 ("width=%d, height=%d, format=%c%c%c%c, field=%s, "
"bytesperline=%d sizeimage=%d, colorspace=%d\n",
- fmt->width,fmt->height,fmt->pixelformat,
+ fmt->width,fmt->height,
+ (fmt->pixelformat & 0xff),
+ (fmt->pixelformat >> 8) & 0xff,
+ (fmt->pixelformat >> 16) & 0xff,
+ (fmt->pixelformat >> 24) & 0xff,
prt_names(fmt->field,v4l2_field_names_FIXME),
fmt->bytesperline,fmt->sizeimage,fmt->colorspace);
};
@@ -428,6 +432,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
v4l_print_ioctl(vfd->name, cmd);
}
+ if (_IOC_TYPE(cmd)=='v')
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ __video_do_ioctl);
+
switch(cmd) {
/* --- capabilities ------------------------------------------ */
case VIDIOC_QUERYCAP:
@@ -526,12 +534,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
}
if (!ret)
dbgarg (cmd, "index=%d, type=%d, flags=%d, "
- "description=%s,"
- " pixelformat=0x%8x\n",
+ "pixelformat=%c%c%c%c, description='%s'\n",
f->index, f->type, f->flags,
- f->description,
- f->pixelformat);
-
+ (f->pixelformat & 0xff),
+ (f->pixelformat >> 8) & 0xff,
+ (f->pixelformat >> 16) & 0xff,
+ (f->pixelformat >> 24) & 0xff,
+ f->description);
break;
}
case VIDIOC_G_FMT:
@@ -829,20 +838,85 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *p = arg;
- unsigned int index = p->index;
+ v4l2_std_id id = vfd->tvnorms,curr_id=0;
+ unsigned int index = p->index,i;
- if (!vfd->tvnormsize) {
- printk (KERN_WARNING "%s: no TV norms defined!\n",
- vfd->name);
+ if (index<0) {
+ ret=-EINVAL;
break;
}
- if (index<0 || index >= vfd->tvnormsize) {
- ret=-EINVAL;
- break;
+ /* Return norm array on a canonical way */
+ for (i=0;i<= index && id; i++) {
+ if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) {
+ curr_id = V4L2_STD_PAL;
+ } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) {
+ curr_id = V4L2_STD_PAL_BG;
+ } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) {
+ curr_id = V4L2_STD_PAL_DK;
+ } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) {
+ curr_id = V4L2_STD_PAL_B;
+ } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) {
+ curr_id = V4L2_STD_PAL_B1;
+ } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) {
+ curr_id = V4L2_STD_PAL_G;
+ } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) {
+ curr_id = V4L2_STD_PAL_H;
+ } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) {
+ curr_id = V4L2_STD_PAL_I;
+ } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) {
+ curr_id = V4L2_STD_PAL_D;
+ } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) {
+ curr_id = V4L2_STD_PAL_D1;
+ } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) {
+ curr_id = V4L2_STD_PAL_K;
+ } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) {
+ curr_id = V4L2_STD_PAL_M;
+ } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) {
+ curr_id = V4L2_STD_PAL_N;
+ } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) {
+ curr_id = V4L2_STD_PAL_Nc;
+ } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) {
+ curr_id = V4L2_STD_PAL_60;
+ } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
+ curr_id = V4L2_STD_NTSC;
+ } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) {
+ curr_id = V4L2_STD_NTSC_M;
+ } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) {
+ curr_id = V4L2_STD_NTSC_M_JP;
+ } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) {
+ curr_id = V4L2_STD_NTSC_443;
+ } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) {
+ curr_id = V4L2_STD_NTSC_M_KR;
+ } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
+ curr_id = V4L2_STD_SECAM;
+ } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) {
+ curr_id = V4L2_STD_SECAM_DK;
+ } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) {
+ curr_id = V4L2_STD_SECAM_B;
+ } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) {
+ curr_id = V4L2_STD_SECAM_D;
+ } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) {
+ curr_id = V4L2_STD_SECAM_G;
+ } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) {
+ curr_id = V4L2_STD_SECAM_H;
+ } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) {
+ curr_id = V4L2_STD_SECAM_K;
+ } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) {
+ curr_id = V4L2_STD_SECAM_K1;
+ } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) {
+ curr_id = V4L2_STD_SECAM_L;
+ } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) {
+ curr_id = V4L2_STD_SECAM_LC;
+ } else {
+ break;
+ }
+ id &= ~curr_id;
}
- v4l2_video_std_construct(p, vfd->tvnorms[p->index].id,
- vfd->tvnorms[p->index].name);
+ if (i<=index)
+ return -EINVAL;
+
+ v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id));
p->index = index;
dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, "
@@ -868,39 +942,23 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
}
case VIDIOC_S_STD:
{
- v4l2_std_id *id = arg;
- unsigned int i;
-
- if (!vfd->tvnormsize) {
- printk (KERN_WARNING "%s: no TV norms defined!\n",
- vfd->name);
- break;
- }
+ v4l2_std_id *id = arg,norm;
dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id);
- /* First search for exact match */
- for (i = 0; i < vfd->tvnormsize; i++)
- if (*id == vfd->tvnorms[i].id)
- break;
- /* Then for a generic video std that contains desired std */
- if (i == vfd->tvnormsize)
- for (i = 0; i < vfd->tvnormsize; i++)
- if (*id & vfd->tvnorms[i].id)
- break;
- if (i == vfd->tvnormsize) {
+ norm = (*id) & vfd->tvnorms;
+ if ( vfd->tvnorms && !norm) /* Check if std is supported */
break;
- }
/* Calls the specific handler */
if (vfd->vidioc_s_std)
- ret=vfd->vidioc_s_std(file, fh, i);
+ ret=vfd->vidioc_s_std(file, fh, &norm);
else
ret=-EINVAL;
/* Updates standard information */
- if (!ret)
- vfd->current_norm=*id;
+ if (ret>=0)
+ vfd->current_norm=norm;
break;
}
@@ -1088,9 +1146,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_AUDIO:
{
struct v4l2_audio *p=arg;
+ __u32 index=p->index;
if (!vfd->vidioc_g_audio)
break;
+
+ memset(p,0,sizeof(*p));
+ p->index=index;
dbgarg(cmd, "Get for index=%d\n", p->index);
ret=vfd->vidioc_g_audio(file, fh, p);
if (!ret)
@@ -1288,25 +1350,12 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_g_parm(file, fh, p);
} else {
struct v4l2_standard s;
- int i;
-
- if (!vfd->tvnormsize) {
- printk (KERN_WARNING "%s: no TV norms defined!\n",
- vfd->name);
- break;
- }
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- for (i = 0; i < vfd->tvnormsize; i++)
- if (vfd->tvnorms[i].id == vfd->current_norm)
- break;
- if (i >= vfd->tvnormsize)
- return -EINVAL;
-
v4l2_video_std_construct(&s, vfd->current_norm,
- vfd->tvnorms[i].name);
+ v4l2_norm_to_name(vfd->current_norm));
memset(p,0,sizeof(*p));
@@ -1329,8 +1378,14 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *p=arg;
+ __u32 index=p->index;
+
if (!vfd->vidioc_g_tuner)
break;
+
+ memset(p,0,sizeof(*p));
+ p->index=index;
+
ret=vfd->vidioc_g_tuner(file, fh, p);
if (!ret)
dbgarg (cmd, "index=%d, name=%s, type=%d, "
@@ -1363,6 +1418,9 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
struct v4l2_frequency *p=arg;
if (!vfd->vidioc_g_frequency)
break;
+
+ memset(p,0,sizeof(*p));
+
ret=vfd->vidioc_g_frequency(file, fh, p);
if (!ret)
dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n",
@@ -1396,12 +1454,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_log_status(file, fh);
break;
}
-
- /* --- Others --------------------------------------------- */
-
- default:
- ret=v4l_compat_translate_ioctl(inode,file,cmd,arg,__video_do_ioctl);
- }
+ } /* switch */
if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
if (ret<0) {
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 6b6dff4..a373c14 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -782,7 +782,7 @@ static int vino_i2c_add_bus(void)
static int vino_i2c_del_bus(void)
{
- return i2c_sgi_del_bus(&vino_i2c_adapter);
+ return i2c_del_adapter(&vino_i2c_adapter);
}
static int i2c_camera_command(unsigned int cmd, void *arg)
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 9986de5..474ddb7 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -1044,16 +1044,8 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
return (0);
}
-static struct v4l2_tvnorm tvnorms[] = {
- {
- .name = "NTSC-M",
- .id = V4L2_STD_NTSC_M,
- }
-};
-
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id a)
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *i)
{
-
return 0;
}
@@ -1333,8 +1325,8 @@ static struct video_device vivi = {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
- .tvnorms = tvnorms,
- .tvnormsize = ARRAY_SIZE(tvnorms),
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M,
};
/* -----------------------------------------------------------------
Initialization and module stuff
@@ -1361,8 +1353,6 @@ static int __init vivi_init(void)
dev->vidq.timeout.data = (unsigned long)dev;
init_timer(&dev->vidq.timeout);
- vivi.current_norm = tvnorms[0].id;
-
ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr);
printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret);
return ret;
diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c
index 653822c..4d1eb2f 100644
--- a/drivers/media/video/zoran_card.c
+++ b/drivers/media/video/zoran_card.c
@@ -849,7 +849,7 @@ zoran_register_i2c (struct zoran *zr)
static void
zoran_unregister_i2c (struct zoran *zr)
{
- i2c_bit_del_bus((&zr->i2c_adapter));
+ i2c_del_adapter(&zr->i2c_adapter);
}
/* Check a zoran_params struct for correctness, insert default params */
diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c
deleted file mode 100644
index 0cbf564..0000000
--- a/drivers/media/video/zr36120.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*
- zr36120.c - Zoran 36120/36125 based framegrabbers
-
- Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
- 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/delay.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/signal.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <linux/sched.h>
-#include <linux/video_decoder.h>
-
-#include <asm/uaccess.h>
-
-#include "tuner.h"
-#include "zr36120.h"
-#include "zr36120_mem.h"
-
-/* mark an required function argument unused - lintism */
-#define UNUSED(x) (void)(x)
-
-/* sensible default */
-#ifndef CARDTYPE
-#define CARDTYPE 0
-#endif
-
-/* Anybody who uses more than four? */
-#define ZORAN_MAX 4
-
-static unsigned int triton1=0; /* triton1 chipset? */
-static unsigned int cardtype[ZORAN_MAX]={ [ 0 ... ZORAN_MAX-1 ] = CARDTYPE };
-static int video_nr = -1;
-static int vbi_nr = -1;
-
-static struct pci_device_id zr36120_pci_tbl[] = {
- { PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, zr36120_pci_tbl);
-
-MODULE_AUTHOR("Pauline Middelink <middelin@polyware.nl>");
-MODULE_DESCRIPTION("Zoran ZR36120 based framegrabber");
-MODULE_LICENSE("GPL");
-
-module_param(triton1, uint, 0);
-module_param_array(cardtype, uint, NULL, 0);
-module_param(video_nr, int, 0);
-module_param(vbi_nr, int, 0);
-
-static int zoran_cards;
-static struct zoran zorans[ZORAN_MAX];
-
-/*
- * the meaning of each element can be found in zr36120.h
- * Determining the value of gpdir/gpval can be tricky. The
- * best way is to run the card under the original software
- * and read the values from the general purpose registers
- * 0x28 and 0x2C. How you do that is left as an exercise
- * to the impatient reader :)
- */
-#define T 1 /* to separate the bools from the ints */
-#define F 0
-static struct tvcard tvcards[] = {
- /* reported working by <middelin@polyware.nl> */
-/*0*/ { "Trust Victor II",
- 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
- /* reported working by <Michael.Paxton@aihw.gov.au> */
-/*1*/ { "Aitech WaveWatcher TV-PCI",
- 3, 0, T, F, T, T, 0x7F, 0x80, { 1, TUNER(3), SVHS(6) }, { 0 } },
- /* reported working by ? */
-/*2*/ { "Genius Video Wonder PCI Video Capture Card",
- 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
- /* reported working by <Pascal.Gabriel@wanadoo.fr> */
-/*3*/ { "Guillemot Maxi-TV PCI",
- 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
- /* reported working by "Craig Whitmore <lennon@igrin.co.nz> */
-/*4*/ { "Quadrant Buster",
- 3, 3, T, F, T, T, 0x7F, 0x80, { SVHS(1), TUNER(2), 3 }, { 1, 2, 3 } },
- /* a debug entry which has all inputs mapped */
-/*5*/ { "ZR36120 based framegrabber (all inputs enabled)",
- 6, 0, T, T, T, T, 0x7F, 0x80, { 1, 2, 3, 4, 5, 6 }, { 0 } }
-};
-#undef T
-#undef F
-#define NRTVCARDS (sizeof(tvcards)/sizeof(tvcards[0]))
-
-#ifdef __sparc__
-#define ENDIANESS 0
-#else
-#define ENDIANESS ZORAN_VFEC_LE
-#endif
-
-static struct { const char name[8]; uint mode; uint bpp; } palette2fmt[] = {
-/* n/a */ { "n/a", 0, 0 },
-/* GREY */ { "GRAY", 0, 0 },
-/* HI240 */ { "HI240", 0, 0 },
-/* RGB565 */ { "RGB565", ZORAN_VFEC_RGB_RGB565|ENDIANESS, 2 },
-/* RGB24 */ { "RGB24", ZORAN_VFEC_RGB_RGB888|ENDIANESS|ZORAN_VFEC_PACK24, 3 },
-/* RGB32 */ { "RGB32", ZORAN_VFEC_RGB_RGB888|ENDIANESS, 4 },
-/* RGB555 */ { "RGB555", ZORAN_VFEC_RGB_RGB555|ENDIANESS, 2 },
-/* YUV422 */ { "YUV422", ZORAN_VFEC_RGB_YUV422|ENDIANESS, 2 },
-/* YUYV */ { "YUYV", 0, 0 },
-/* UYVY */ { "UYVY", 0, 0 },
-/* YUV420 */ { "YUV420", 0, 0 },
-/* YUV411 */ { "YUV411", 0, 0 },
-/* RAW */ { "RAW", 0, 0 },
-/* YUV422P */ { "YUV422P", 0, 0 },
-/* YUV411P */ { "YUV411P", 0, 0 }};
-#define NRPALETTES (sizeof(palette2fmt)/sizeof(palette2fmt[0]))
-#undef ENDIANESS
-
-/* ----------------------------------------------------------------------- */
-/* ZORAN chipset detector */
-/* shamelessly stolen from bttv.c */
-/* Reason for beeing here: we need to detect if we are running on a */
-/* Triton based chipset, and if so, enable a certain bit */
-/* ----------------------------------------------------------------------- */
-static
-void __init handle_chipset(void)
-{
- /* Just in case some nut set this to something dangerous */
- if (triton1)
- triton1 = ZORAN_VDC_TRICOM;
-
- if (pci_pci_problems & PCIPCI_TRITON) {
- printk(KERN_INFO "zoran: Host bridge 82437FX Triton PIIX\n");
- triton1 = ZORAN_VDC_TRICOM;
- }
-}
-
-/* ----------------------------------------------------------------------- */
-/* ZORAN functions */
-/* ----------------------------------------------------------------------- */
-
-static void zoran_set_geo(struct zoran* ztv, struct vidinfo* i);
-
-#if 0 /* unused */
-static
-void zoran_dump(struct zoran *ztv)
-{
- char str[256];
- char *p=str; /* shut up, gcc! */
- int i;
-
- for (i=0; i<0x60; i+=4) {
- if ((i % 16) == 0) {
- if (i) printk("%s\n",str);
- p = str;
- p+= sprintf(str, KERN_DEBUG " %04x: ",i);
- }
- p += sprintf(p, "%08x ",zrread(i));
- }
-}
-#endif /* unused */
-
-static
-void reap_states(struct zoran* ztv)
-{
- /* count frames */
- ztv->fieldnr++;
-
- /*
- * Are we busy at all?
- * This depends on if there is a workqueue AND the
- * videotransfer is enabled on the chip...
- */
- if (ztv->workqueue && (zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN))
- {
- struct vidinfo* newitem;
-
- /* did we get a complete frame? */
- if (zrread(ZORAN_VSTR) & ZORAN_VSTR_GRAB)
- return;
-
-DEBUG(printk(CARD_DEBUG "completed %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue));
-
- /* we are done with this buffer, tell everyone */
- ztv->workqueue->status = FBUFFER_DONE;
- ztv->workqueue->fieldnr = ztv->fieldnr;
- /* not good, here for BTTV_FIELDNR reasons */
- ztv->lastfieldnr = ztv->fieldnr;
-
- switch (ztv->workqueue->kindof) {
- case FBUFFER_GRAB:
- wake_up_interruptible(&ztv->grabq);
- break;
- case FBUFFER_VBI:
- wake_up_interruptible(&ztv->vbiq);
- break;
- default:
- printk(CARD_INFO "somebody killed the workqueue (kindof=%d)!\n",CARD,ztv->workqueue->kindof);
- }
-
- /* item completed, skip to next item in queue */
- write_lock(&ztv->lock);
- newitem = ztv->workqueue->next;
- ztv->workqueue->next = 0; /* mark completed */
- ztv->workqueue = newitem;
- write_unlock(&ztv->lock);
- }
-
- /*
- * ok, so it seems we have nothing in progress right now.
- * Lets see if we can find some work.
- */
- if (ztv->workqueue)
- {
- struct vidinfo* newitem;
-again:
-
-DEBUG(printk(CARD_DEBUG "starting %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue));
-
- /* loadup the frame settings */
- read_lock(&ztv->lock);
- zoran_set_geo(ztv,ztv->workqueue);
- read_unlock(&ztv->lock);
-
- switch (ztv->workqueue->kindof) {
- case FBUFFER_GRAB:
- case FBUFFER_VBI:
- zrand(~ZORAN_OCR_OVLEN, ZORAN_OCR);
- zror(ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR);
- zror(ZORAN_VDC_VIDEN,ZORAN_VDC);
-
- /* start single-shot grab */
- zror(ZORAN_VSTR_GRAB, ZORAN_VSTR);
- break;
- default:
- printk(CARD_INFO "what is this doing on the queue? (kindof=%d)\n",CARD,ztv->workqueue->kindof);
- write_lock(&ztv->lock);
- newitem = ztv->workqueue->next;
- ztv->workqueue->next = 0;
- ztv->workqueue = newitem;
- write_unlock(&ztv->lock);
- if (newitem)
- goto again; /* yeah, sure.. */
- }
- /* bye for now */
- return;
- }
-DEBUG(printk(CARD_DEBUG "nothing in queue\n",CARD));
-
- /*
- * What? Even the workqueue is empty? Am i really here
- * for nothing? Did i come all that way to... do nothing?
- */
-
- /* do we need to overlay? */
- if (test_bit(STATE_OVERLAY, &ztv->state))
- {
- /* are we already overlaying? */
- if (!(zrread(ZORAN_OCR) & ZORAN_OCR_OVLEN) ||
- !(zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN))
- {
-DEBUG(printk(CARD_DEBUG "starting overlay\n",CARD));
-
- read_lock(&ztv->lock);
- zoran_set_geo(ztv,&ztv->overinfo);
- read_unlock(&ztv->lock);
-
- zror(ZORAN_OCR_OVLEN, ZORAN_OCR);
- zrand(~ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR);
- zror(ZORAN_VDC_VIDEN,ZORAN_VDC);
- }
-
- /*
- * leave overlaying on, but turn interrupts off.
- */
- zrand(~ZORAN_ICR_EN,ZORAN_ICR);
- return;
- }
-
- /* do we have any VBI idle time processing? */
- if (test_bit(STATE_VBI, &ztv->state))
- {
- struct vidinfo* item;
- struct vidinfo* lastitem;
-
- /* protect the workqueue */
- write_lock(&ztv->lock);
- lastitem = ztv->workqueue;
- if (lastitem)
- while (lastitem->next) lastitem = lastitem->next;
- for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
- if (item->next == 0 && item->status == FBUFFER_FREE)
- {
-DEBUG(printk(CARD_DEBUG "%p added to queue\n",CARD,item));
- item->status = FBUFFER_BUSY;
- if (!lastitem)
- ztv->workqueue = item;
- else
- lastitem->next = item;
- lastitem = item;
- }
- write_unlock(&ztv->lock);
- if (ztv->workqueue)
- goto again; /* hey, _i_ graduated :) */
- }
-
- /*
- * Then we must be realy IDLE
- */
-DEBUG(printk(CARD_DEBUG "turning off\n",CARD));
- /* nothing further to do, disable DMA and further IRQs */
- zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
- zrand(~ZORAN_ICR_EN,ZORAN_ICR);
-}
-
-static
-void zoran_irq(int irq, void *dev_id)
-{
- u32 stat,estat;
- int count = 0;
- struct zoran *ztv = dev_id;
-
- UNUSED(irq);
- for (;;) {
- /* get/clear interrupt status bits */
- stat=zrread(ZORAN_ISR);
- estat=stat & zrread(ZORAN_ICR);
- if (!estat)
- return;
- zrwrite(estat,ZORAN_ISR);
- IDEBUG(printk(CARD_DEBUG "estat %08x\n",CARD,estat));
- IDEBUG(printk(CARD_DEBUG " stat %08x\n",CARD,stat));
-
- if (estat & ZORAN_ISR_CODE)
- {
- IDEBUG(printk(CARD_DEBUG "CodReplIRQ\n",CARD));
- }
- if (estat & ZORAN_ISR_GIRQ0)
- {
- IDEBUG(printk(CARD_DEBUG "GIRQ0\n",CARD));
- if (!ztv->card->usegirq1)
- reap_states(ztv);
- }
- if (estat & ZORAN_ISR_GIRQ1)
- {
- IDEBUG(printk(CARD_DEBUG "GIRQ1\n",CARD));
- if (ztv->card->usegirq1)
- reap_states(ztv);
- }
-
- count++;
- if (count > 10)
- printk(CARD_ERR "irq loop %d (%x)\n",CARD,count,estat);
- if (count > 20)
- {
- zrwrite(0, ZORAN_ICR);
- printk(CARD_ERR "IRQ lockup, cleared int mask\n",CARD);
- }
- }
-}
-
-static
-int zoran_muxsel(struct zoran* ztv, int channel, int norm)
-{
- int rv;
-
- /* set the new video norm */
- rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm);
- if (rv)
- return rv;
- ztv->norm = norm;
-
- /* map the given channel to the cards decoder's channel */
- channel = ztv->card->video_mux[channel] & CHANNEL_MASK;
-
- /* set the new channel */
- rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &channel);
- return rv;
-}
-
-/* Tell the interrupt handler what to to. */
-static
-void zoran_cap(struct zoran* ztv, int on)
-{
-DEBUG(printk(CARD_DEBUG "zoran_cap(%d) state=%x\n",CARD,on,ztv->state));
-
- if (on) {
- ztv->running = 1;
-
- /*
- * turn interrupts (back) on. The DMA will be enabled
- * inside the irq handler when it detects a restart.
- */
- zror(ZORAN_ICR_EN,ZORAN_ICR);
- }
- else {
- /*
- * turn both interrupts and DMA off
- */
- zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
- zrand(~ZORAN_ICR_EN,ZORAN_ICR);
-
- ztv->running = 0;
- }
-}
-
-static ulong dmask[] = {
- 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8,
- 0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80,
- 0xFFFFFF00, 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800,
- 0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000,
- 0xFFFF0000, 0xFFFE0000, 0xFFFC0000, 0xFFF80000,
- 0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000,
- 0xFF000000, 0xFE000000, 0xFC000000, 0xF8000000,
- 0xF0000000, 0xE0000000, 0xC0000000, 0x80000000
-};
-
-static
-void zoran_built_overlay(struct zoran* ztv, int count, struct video_clip *vcp)
-{
- ulong* mtop;
- int ystep = (ztv->vidXshift + ztv->vidWidth+31)/32; /* next DWORD */
- int i;
-
-DEBUG(printk(KERN_DEBUG " overlay at %p, ystep=%d, clips=%d\n",ztv->overinfo.overlay,ystep,count));
-
- for (i=0; i<count; i++) {
- struct video_clip *vp = vcp+i;
- UNUSED(vp);
-DEBUG(printk(KERN_DEBUG " %d: clip(%d,%d,%d,%d)\n", i,vp->x,vp->y,vp->width,vp->height));
- }
-
- /*
- * activate the visible portion of the screen
- * Note we take some shortcuts here, because we
- * know the width can never be < 32. (I.e. a DWORD)
- * We also assume the overlay starts somewhere in
- * the FIRST dword.
- */
- {
- int start = ztv->vidXshift;
- ulong firstd = dmask[start];
- ulong lastd = ~dmask[(start + ztv->overinfo.w) & 31];
- mtop = ztv->overinfo.overlay;
- for (i=0; i<ztv->overinfo.h; i++) {
- int w = ztv->vidWidth;
- ulong* line = mtop;
- if (start & 31) {
- *line++ = firstd;
- w -= 32-(start&31);
- }
- memset(line, ~0, w/8);
- if (w & 31)
- line[w/32] = lastd;
- mtop += ystep;
- }
- }
-
- /* process clipping regions */
- for (i=0; i<count; i++) {
- int h;
- if (vcp->x < 0 || (uint)vcp->x > ztv->overinfo.w ||
- vcp->y < 0 || vcp->y > ztv->overinfo.h ||
- vcp->width < 0 || (uint)(vcp->x+vcp->width) > ztv->overinfo.w ||
- vcp->height < 0 || (vcp->y+vcp->height) > ztv->overinfo.h)
- {
- DEBUG(printk(CARD_DEBUG "invalid clipzone (%d,%d,%d,%d) not in (0,0,%d,%d), adapting\n",CARD,vcp->x,vcp->y,vcp->width,vcp->height,ztv->overinfo.w,ztv->overinfo.h));
- if (vcp->x < 0) vcp->x = 0;
- if ((uint)vcp->x > ztv->overinfo.w) vcp->x = ztv->overinfo.w;
- if (vcp->y < 0) vcp->y = 0;
- if (vcp->y > ztv->overinfo.h) vcp->y = ztv->overinfo.h;
- if (vcp->width < 0) vcp->width = 0;
- if ((uint)(vcp->x+vcp->width) > ztv->overinfo.w) vcp->width = ztv->overinfo.w - vcp->x;
- if (vcp->height < 0) vcp->height = 0;
- if (vcp->y+vcp->height > ztv->overinfo.h) vcp->height = ztv->overinfo.h - vcp->y;
-// continue;
- }
-
- mtop = &ztv->overinfo.overlay[vcp->y*ystep];
- for (h=0; h<=vcp->height; h++) {
- int w;
- int x = ztv->vidXshift + vcp->x;
- for (w=0; w<=vcp->width; w++) {
- clear_bit(x&31, &mtop[x/32]);
- x++;
- }
- mtop += ystep;
- }
- ++vcp;
- }
-
- mtop = ztv->overinfo.overlay;
- zrwrite(virt_to_bus(mtop), ZORAN_MTOP);
- zrwrite(virt_to_bus(mtop+ystep), ZORAN_MBOT);
- zraor((ztv->vidInterlace*ystep)<<0,~ZORAN_OCR_MASKSTRIDE,ZORAN_OCR);
-}
-
-struct tvnorm
-{
- u16 Wt, Wa, Ht, Ha, HStart, VStart;
-};
-
-static struct tvnorm tvnorms[] = {
- /* PAL-BDGHI */
-/* { 864, 720, 625, 576, 131, 21 },*/
-/*00*/ { 864, 768, 625, 576, 81, 17 },
- /* NTSC */
-/*01*/ { 858, 720, 525, 480, 121, 10 },
- /* SECAM */
-/*02*/ { 864, 720, 625, 576, 131, 21 },
- /* BW50 */
-/*03*/ { 864, 720, 625, 576, 131, 21 },
- /* BW60 */
-/*04*/ { 858, 720, 525, 480, 121, 10 }
-};
-#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
-
-/*
- * Program the chip for a setup as described in the vidinfo struct.
- *
- * Side-effects: calculates vidXshift, vidInterlace,
- * vidHeight, vidWidth which are used in a later stage
- * to calculate the overlay mask
- *
- * This is an internal function, as such it does not check the
- * validity of the struct members... Spectaculair crashes will
- * follow /very/ quick when you're wrong and the chip right :)
- */
-static
-void zoran_set_geo(struct zoran* ztv, struct vidinfo* i)
-{
- ulong top, bot;
- int stride;
- int winWidth, winHeight;
- int maxWidth, maxHeight, maxXOffset, maxYOffset;
- long vfec;
-
-DEBUG(printk(CARD_DEBUG "set_geo(rect=(%d,%d,%d,%d), norm=%d, format=%d, bpp=%d, bpl=%d, busadr=%lx, overlay=%p)\n",CARD,i->x,i->y,i->w,i->h,ztv->norm,i->format,i->bpp,i->bpl,i->busadr,i->overlay));
-
- /*
- * make sure the DMA transfers are inhibited during our
- * reprogramming of the chip
- */
- zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
-
- maxWidth = tvnorms[ztv->norm].Wa;
- maxHeight = tvnorms[ztv->norm].Ha/2;
- maxXOffset = tvnorms[ztv->norm].HStart;
- maxYOffset = tvnorms[ztv->norm].VStart;
-
- /* setup vfec register (keep ExtFl,TopField and VCLKPol settings) */
- vfec = (zrread(ZORAN_VFEC) & (ZORAN_VFEC_EXTFL|ZORAN_VFEC_TOPFIELD|ZORAN_VFEC_VCLKPOL)) |
- (palette2fmt[i->format].mode & (ZORAN_VFEC_RGB|ZORAN_VFEC_ERRDIF|ZORAN_VFEC_LE|ZORAN_VFEC_PACK24));
-
- /*
- * Set top, bottom ptrs. Since these must be DWORD aligned,
- * possible adjust the x and the width of the window.
- * so the endposition stay the same. The vidXshift will make
- * sure we are not writing pixels before the requested x.
- */
- ztv->vidXshift = 0;
- winWidth = i->w;
- if (winWidth < 0)
- winWidth = -winWidth;
- top = i->busadr + i->x*i->bpp + i->y*i->bpl;
- if (top & 3) {
- ztv->vidXshift = (top & 3) / i->bpp;
- winWidth += ztv->vidXshift;
- DEBUG(printk(KERN_DEBUG " window-x shifted %d pixels left\n",ztv->vidXshift));
- top &= ~3;
- }
-
- /*
- * bottom points to next frame but in interleaved mode we want
- * to 'mix' the 2 frames to one capture, so 'bot' points to one
- * (physical) line below the top line.
- */
- bot = top + i->bpl;
- zrwrite(top,ZORAN_VTOP);
- zrwrite(bot,ZORAN_VBOT);
-
- /*
- * Make sure the winWidth is DWORD aligned too,
- * thereby automaticly making sure the stride to the
- * next line is DWORD aligned too (as required by spec).
- */
- if ((winWidth*i->bpp) & 3) {
-DEBUG(printk(KERN_DEBUG " window-width enlarged by %d pixels\n",(winWidth*i->bpp) & 3));
- winWidth += (winWidth*i->bpp) & 3;
- }
-
- /* determine the DispMode and stride */
- if (i->h >= 0 && i->h <= maxHeight) {
- /* single frame grab suffices for this height. */
- vfec |= ZORAN_VFEC_DISPMOD;
- ztv->vidInterlace = 0;
- stride = i->bpl - (winWidth*i->bpp);
- winHeight = i->h;
- }
- else {
- /* interleaving needed for this height */
- ztv->vidInterlace = 1;
- stride = i->bpl*2 - (winWidth*i->bpp);
- winHeight = i->h/2;
- }
- if (winHeight < 0) /* can happen for VBI! */
- winHeight = -winHeight;
-
- /* safety net, sometimes bpl is too short??? */
- if (stride<0) {
-DEBUG(printk(CARD_DEBUG "WARNING stride = %d\n",CARD,stride));
- stride = 0;
- }
-
- zraor((winHeight<<12)|(winWidth<<0),~(ZORAN_VDC_VIDWINHT|ZORAN_VDC_VIDWINWID), ZORAN_VDC);
- zraor(stride<<16,~ZORAN_VSTR_DISPSTRIDE,ZORAN_VSTR);
-
- /* remember vidWidth, vidHeight for overlay calculations */
- ztv->vidWidth = winWidth;
- ztv->vidHeight = winHeight;
-DEBUG(printk(KERN_DEBUG " top=%08lx, bottom=%08lx\n",top,bot));
-DEBUG(printk(KERN_DEBUG " winWidth=%d, winHeight=%d\n",winWidth,winHeight));
-DEBUG(printk(KERN_DEBUG " maxWidth=%d, maxHeight=%d\n",maxWidth,maxHeight));
-DEBUG(printk(KERN_DEBUG " stride=%d\n",stride));
-
- /*
- * determine horizontal scales and crops
- */
- if (i->w < 0) {
- int Hstart = 1;
- int Hend = Hstart + winWidth;
-DEBUG(printk(KERN_DEBUG " Y: scale=0, start=%d, end=%d\n", Hstart, Hend));
- zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH);
- }
- else {
- int Wa = maxWidth;
- int X = (winWidth*64+Wa-1)/Wa;
- int We = winWidth*64/X;
- int HorDcm = 64-X;
- int hcrop1 = 2*(Wa-We)/4;
- /*
- * BUGFIX: Juha Nurmela <junki@qn-lpr2-165.quicknet.inet.fi>
- * found the solution to the color phase shift.
- * See ChangeLog for the full explanation)
- */
- int Hstart = (maxXOffset + hcrop1) | 1;
- int Hend = Hstart + We - 1;
-
-DEBUG(printk(KERN_DEBUG " X: scale=%d, start=%d, end=%d\n", HorDcm, Hstart, Hend));
-
- zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH);
- vfec |= HorDcm<<14;
-
- if (HorDcm<16)
- vfec |= ZORAN_VFEC_HFILTER_1; /* no filter */
- else if (HorDcm<32)
- vfec |= ZORAN_VFEC_HFILTER_3; /* 3 tap filter */
- else if (HorDcm<48)
- vfec |= ZORAN_VFEC_HFILTER_4; /* 4 tap filter */
- else vfec |= ZORAN_VFEC_HFILTER_5; /* 5 tap filter */
- }
-
- /*
- * Determine vertical scales and crops
- *
- * when height is negative, we want to read starting at line 0
- * One day someone might need access to these lines...
- */
- if (i->h < 0) {
- int Vstart = 0;
- int Vend = Vstart + winHeight;
-DEBUG(printk(KERN_DEBUG " Y: scale=0, start=%d, end=%d\n", Vstart, Vend));
- zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV);
- }
- else {
- int Ha = maxHeight;
- int Y = (winHeight*64+Ha-1)/Ha;
- int He = winHeight*64/Y;
- int VerDcm = 64-Y;
- int vcrop1 = 2*(Ha-He)/4;
- int Vstart = maxYOffset + vcrop1;
- int Vend = Vstart + He - 1;
-
-DEBUG(printk(KERN_DEBUG " Y: scale=%d, start=%d, end=%d\n", VerDcm, Vstart, Vend));
- zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV);
- vfec |= VerDcm<<8;
- }
-
-DEBUG(printk(KERN_DEBUG " F: format=%d(=%s)\n",i->format,palette2fmt[i->format].name));
-
- /* setup the requested format */
- zrwrite(vfec, ZORAN_VFEC);
-}
-
-static
-void zoran_common_open(struct zoran* ztv, int flags)
-{
- UNUSED(flags);
-
- /* already opened? */
- if (ztv->users++ != 0)
- return;
-
- /* unmute audio */
- /* /what/ audio? */
-
- ztv->state = 0;
-
- /* setup the encoder to the initial values */
- ztv->picture.colour=254<<7;
- ztv->picture.brightness=128<<8;
- ztv->picture.hue=128<<8;
- ztv->picture.contrast=216<<7;
- i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &ztv->picture);
-
- /* default to the composite input since my camera is there */
- zoran_muxsel(ztv, 0, VIDEO_MODE_PAL);
-}
-
-static
-void zoran_common_close(struct zoran* ztv)
-{
- if (--ztv->users != 0)
- return;
-
- /* mute audio */
- /* /what/ audio? */
-
- /* stop the chip */
- zoran_cap(ztv, 0);
-}
-
-/*
- * Open a zoran card. Right now the flags are just a hack
- */
-static int zoran_open(struct video_device *dev, int flags)
-{
- struct zoran *ztv = (struct zoran*)dev;
- struct vidinfo* item;
- char* pos;
-
- DEBUG(printk(CARD_DEBUG "open(dev,%d)\n",CARD,flags));
-
- /*********************************************
- * We really should be doing lazy allocing...
- *********************************************/
- /* allocate a frame buffer */
- if (!ztv->fbuffer)
- ztv->fbuffer = bmalloc(ZORAN_MAX_FBUFSIZE);
- if (!ztv->fbuffer) {
- /* could not get a buffer, bail out */
- return -ENOBUFS;
- }
- /* at this time we _always_ have a framebuffer */
- memset(ztv->fbuffer,0,ZORAN_MAX_FBUFSIZE);
-
- if (!ztv->overinfo.overlay)
- ztv->overinfo.overlay = kmalloc(1024*1024/8, GFP_KERNEL);
- if (!ztv->overinfo.overlay) {
- /* could not get an overlay buffer, bail out */
- bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE);
- return -ENOBUFS;
- }
- /* at this time we _always_ have a overlay */
-
- /* clear buffer status, and give them a DMAable address */
- pos = ztv->fbuffer;
- for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
- {
- item->status = FBUFFER_FREE;
- item->memadr = pos;
- item->busadr = virt_to_bus(pos);
- pos += ZORAN_MAX_FBUFFER;
- }
-
- /* do the common part of all open's */
- zoran_common_open(ztv, flags);
-
- return 0;
-}
-
-static
-void zoran_close(struct video_device* dev)
-{
- struct zoran *ztv = (struct zoran*)dev;
-
- DEBUG(printk(CARD_DEBUG "close(dev)\n",CARD));
-
- /* driver specific closure */
- clear_bit(STATE_OVERLAY, &ztv->state);
-
- zoran_common_close(ztv);
-
- /*
- * This is sucky but right now I can't find a good way to
- * be sure its safe to free the buffer. We wait 5-6 fields
- * which is more than sufficient to be sure.
- */
- msleep(100); /* Wait 1/10th of a second */
-
- /* free the allocated framebuffer */
- bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE);
- ztv->fbuffer = 0;
- kfree(ztv->overinfo.overlay);
- ztv->overinfo.overlay = 0;
-
-}
-
-/*
- * This read function could be used reentrant in a SMP situation.
- *
- * This is made possible by the spinlock which is kept till we
- * found and marked a buffer for our own use. The lock must
- * be released as soon as possible to prevent lock contention.
- */
-static
-long zoran_read(struct video_device* dev, char* buf, unsigned long count, int nonblock)
-{
- struct zoran *ztv = (struct zoran*)dev;
- unsigned long max;
- struct vidinfo* unused = 0;
- struct vidinfo* done = 0;
-
- DEBUG(printk(CARD_DEBUG "zoran_read(%p,%ld,%d)\n",CARD,buf,count,nonblock));
-
- /* find ourself a free or completed buffer */
- for (;;) {
- struct vidinfo* item;
-
- write_lock_irq(&ztv->lock);
- for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
- {
- if (!unused && item->status == FBUFFER_FREE)
- unused = item;
- if (!done && item->status == FBUFFER_DONE)
- done = item;
- }
- if (done || unused)
- break;
-
- /* no more free buffers, wait for them. */
- write_unlock_irq(&ztv->lock);
- if (nonblock)
- return -EWOULDBLOCK;
- interruptible_sleep_on(&ztv->grabq);
- if (signal_pending(current))
- return -EINTR;
- }
-
- /* Do we have 'ready' data? */
- if (!done) {
- /* no? than this will take a while... */
- if (nonblock) {
- write_unlock_irq(&ztv->lock);
- return -EWOULDBLOCK;
- }
-
- /* mark the unused buffer as wanted */
- unused->status = FBUFFER_BUSY;
- unused->w = 320;
- unused->h = 240;
- unused->format = VIDEO_PALETTE_RGB24;
- unused->bpp = palette2fmt[unused->format].bpp;
- unused->bpl = unused->w * unused->bpp;
- unused->next = 0;
- { /* add to tail of queue */
- struct vidinfo* oldframe = ztv->workqueue;
- if (!oldframe) ztv->workqueue = unused;
- else {
- while (oldframe->next) oldframe = oldframe->next;
- oldframe->next = unused;
- }
- }
- write_unlock_irq(&ztv->lock);
-
- /* tell the state machine we want it filled /NOW/ */
- zoran_cap(ztv, 1);
-
- /* wait till this buffer gets grabbed */
- wait_event_interruptible(ztv->grabq,
- (unused->status != FBUFFER_BUSY));
- /* see if a signal did it */
- if (signal_pending(current))
- return -EINTR;
- done = unused;
- }
- else
- write_unlock_irq(&ztv->lock);
-
- /* Yes! we got data! */
- max = done->bpl * done->h;
- if (count > max)
- count = max;
- if (copy_to_user((void*)buf, done->memadr, count))
- count = -EFAULT;
-
- /* keep the engine running */
- done->status = FBUFFER_FREE;
-// zoran_cap(ztv,1);
-
- /* tell listeners this buffer became free */
- wake_up_interruptible(&ztv->grabq);
-
- /* goodbye */
- DEBUG(printk(CARD_DEBUG "zoran_read() returns %lu\n",CARD,count));
- return count;
-}
-
-static
-long zoran_write(struct video_device* dev, const char* buf, unsigned long count, int nonblock)
-{
- struct zoran *ztv = (struct zoran *)dev;
- UNUSED(ztv); UNUSED(dev); UNUSED(buf); UNUSED(count); UNUSED(nonblock);
- DEBUG(printk(CARD_DEBUG "zoran_write\n",CARD));
- return -EINVAL;
-}
-
-static
-unsigned int zoran_poll(struct video_device *dev, struct file *file, poll_table *wait)
-{
- struct zoran *ztv = (struct zoran *)dev;
- struct vidinfo* item;
- unsigned int mask = 0;
-
- poll_wait(file, &ztv->grabq, wait);
-
- for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
- if (item->status == FBUFFER_DONE)
- {
- mask |= (POLLIN | POLLRDNORM);
- break;
- }
-
- DEBUG(printk(CARD_DEBUG "zoran_poll()=%x\n",CARD,mask));
-
- return mask;
-}
-
-/* append a new clipregion to the vector of video_clips */
-static
-void new_clip(struct video_window* vw, struct video_clip* vcp, int x, int y, int w, int h)
-{
- vcp[vw->clipcount].x = x;
- vcp[vw->clipcount].y = y;
- vcp[vw->clipcount].width = w;
- vcp[vw->clipcount].height = h;
- vw->clipcount++;
-}
-
-static
-int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg)
-{
- struct zoran* ztv = (struct zoran*)dev;
-
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability c;
- DEBUG(printk(CARD_DEBUG "VIDIOCGCAP\n",CARD));
-
- strcpy(c.name,ztv->video_dev.name);
- c.type = VID_TYPE_CAPTURE|
- VID_TYPE_OVERLAY|
- VID_TYPE_CLIPPING|
- VID_TYPE_FRAMERAM|
- VID_TYPE_SCALES;
- if (ztv->have_tuner)
- c.type |= VID_TYPE_TUNER;
- if (pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
- c.type &= ~VID_TYPE_OVERLAY;
- if (ztv->have_decoder) {
- c.channels = ztv->card->video_inputs;
- c.audios = ztv->card->audio_inputs;
- } else
- /* no decoder -> no channels */
- c.channels = c.audios = 0;
- c.maxwidth = 768;
- c.maxheight = 576;
- c.minwidth = 32;
- c.minheight = 32;
- if (copy_to_user(arg,&c,sizeof(c)))
- return -EFAULT;
- break;
- }
-
- case VIDIOCGCHAN:
- {
- struct video_channel v;
- int mux;
- if (copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCGCHAN(%d)\n",CARD,v.channel));
- v.flags=VIDEO_VC_AUDIO
-#ifdef VIDEO_VC_NORM
- |VIDEO_VC_NORM
-#endif
- ;
- v.tuners=0;
- v.type=VIDEO_TYPE_CAMERA;
-#ifdef I_EXPECT_POSSIBLE_NORMS_IN_THE_API
- v.norm=VIDEO_MODE_PAL|
- VIDEO_MODE_NTSC|
- VIDEO_MODE_SECAM;
-#else
- v.norm=VIDEO_MODE_PAL;
-#endif
- /* too many inputs? no decoder -> no channels */
- if (!ztv->have_decoder || v.channel < 0 || v.channel >= ztv->card->video_inputs)
- return -EINVAL;
-
- /* now determine the name of the channel */
- mux = ztv->card->video_mux[v.channel];
- if (mux & IS_TUNER) {
- /* lets assume only one tuner, yes? */
- strcpy(v.name,"Television");
- v.type = VIDEO_TYPE_TV;
- if (ztv->have_tuner) {
- v.flags |= VIDEO_VC_TUNER;
- v.tuners = 1;
- }
- }
- else if (mux & IS_SVHS)
- sprintf(v.name,"S-Video-%d",v.channel);
- else
- sprintf(v.name,"CVBS-%d",v.channel);
-
- if (copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- break;
- }
- case VIDIOCSCHAN:
- { /* set video channel */
- struct video_channel v;
- if (copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSCHAN(%d,%d)\n",CARD,v.channel,v.norm));
-
- /* too many inputs? no decoder -> no channels */
- if (!ztv->have_decoder || v.channel >= ztv->card->video_inputs || v.channel < 0)
- return -EINVAL;
-
- if (v.norm != VIDEO_MODE_PAL &&
- v.norm != VIDEO_MODE_NTSC &&
- v.norm != VIDEO_MODE_SECAM &&
- v.norm != VIDEO_MODE_AUTO)
- return -EOPNOTSUPP;
-
- /* make it happen, nr1! */
- return zoran_muxsel(ztv,v.channel,v.norm);
- }
-
- case VIDIOCGTUNER:
- {
- struct video_tuner v;
- if (copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCGTUNER(%d)\n",CARD,v.tuner));
-
- /* Only no or one tuner for now */
- if (!ztv->have_tuner || v.tuner)
- return -EINVAL;
-
- strcpy(v.name,"Television");
- v.rangelow = 0;
- v.rangehigh = ~0;
- v.flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
- v.mode = ztv->norm;
- v.signal = 0xFFFF; /* unknown */
-
- if (copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- break;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner v;
- if (copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSTUNER(%d,%d)\n",CARD,v.tuner,v.mode));
-
- /* Only no or one tuner for now */
- if (!ztv->have_tuner || v.tuner)
- return -EINVAL;
-
- /* and it only has certain valid modes */
- if( v.mode != VIDEO_MODE_PAL &&
- v.mode != VIDEO_MODE_NTSC &&
- v.mode != VIDEO_MODE_SECAM)
- return -EOPNOTSUPP;
-
- /* engage! */
- return zoran_muxsel(ztv,v.tuner,v.mode);
- }
-
- case VIDIOCGPICT:
- {
- struct video_picture p = ztv->picture;
- DEBUG(printk(CARD_DEBUG "VIDIOCGPICT\n",CARD));
- p.depth = ztv->depth;
- switch (p.depth) {
- case 8: p.palette=VIDEO_PALETTE_YUV422;
- break;
- case 15: p.palette=VIDEO_PALETTE_RGB555;
- break;
- case 16: p.palette=VIDEO_PALETTE_RGB565;
- break;
- case 24: p.palette=VIDEO_PALETTE_RGB24;
- break;
- case 32: p.palette=VIDEO_PALETTE_RGB32;
- break;
- }
- if (copy_to_user(arg, &p, sizeof(p)))
- return -EFAULT;
- break;
- }
- case VIDIOCSPICT:
- {
- struct video_picture p;
- if (copy_from_user(&p, arg,sizeof(p)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSPICT(%d,%d,%d,%d,%d,%d,%d)\n",CARD,p.brightness,p.hue,p.colour,p.contrast,p.whiteness,p.depth,p.palette));
-
- /* depth must match with framebuffer */
- if (p.depth != ztv->depth)
- return -EINVAL;
-
- /* check if palette matches this bpp */
- if (p.palette>NRPALETTES ||
- palette2fmt[p.palette].bpp != ztv->overinfo.bpp)
- return -EINVAL;
-
- write_lock_irq(&ztv->lock);
- ztv->overinfo.format = p.palette;
- ztv->picture = p;
- write_unlock_irq(&ztv->lock);
-
- /* tell the decoder */
- i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p);
- break;
- }
-
- case VIDIOCGWIN:
- {
- struct video_window vw;
- DEBUG(printk(CARD_DEBUG "VIDIOCGWIN\n",CARD));
- read_lock(&ztv->lock);
- vw.x = ztv->overinfo.x;
- vw.y = ztv->overinfo.y;
- vw.width = ztv->overinfo.w;
- vw.height = ztv->overinfo.h;
- vw.chromakey= 0;
- vw.flags = 0;
- if (ztv->vidInterlace)
- vw.flags|=VIDEO_WINDOW_INTERLACE;
- read_unlock(&ztv->lock);
- if (copy_to_user(arg,&vw,sizeof(vw)))
- return -EFAULT;
- break;
- }
- case VIDIOCSWIN:
- {
- struct video_window vw;
- struct video_clip *vcp;
- int on;
- if (copy_from_user(&vw,arg,sizeof(vw)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSWIN(%d,%d,%d,%d,%x,%d)\n",CARD,vw.x,vw.y,vw.width,vw.height,vw.flags,vw.clipcount));
-
- if (vw.flags)
- return -EINVAL;
-
- if (vw.clipcount <0 || vw.clipcount>256)
- return -EDOM; /* Too many! */
-
- /*
- * Do any clips.
- */
- vcp = vmalloc(sizeof(struct video_clip)*(vw.clipcount+4));
- if (vcp==NULL)
- return -ENOMEM;
- if (vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount)) {
- vfree(vcp);
- return -EFAULT;
- }
-
- on = ztv->running;
- if (on)
- zoran_cap(ztv, 0);
-
- /*
- * strange, it seems xawtv sometimes calls us with 0
- * width and/or height. Ignore these values
- */
- if (vw.x == 0)
- vw.x = ztv->overinfo.x;
- if (vw.y == 0)
- vw.y = ztv->overinfo.y;
-
- /* by now we are committed to the new data... */
- write_lock_irq(&ztv->lock);
- ztv->overinfo.x = vw.x;
- ztv->overinfo.y = vw.y;
- ztv->overinfo.w = vw.width;
- ztv->overinfo.h = vw.height;
- write_unlock_irq(&ztv->lock);
-
- /*
- * Impose display clips
- */
- if (vw.x+vw.width > ztv->swidth)
- new_clip(&vw, vcp, ztv->swidth-vw.x, 0, vw.width-1, vw.height-1);
- if (vw.y+vw.height > ztv->sheight)
- new_clip(&vw, vcp, 0, ztv->sheight-vw.y, vw.width-1, vw.height-1);
-
- /* built the requested clipping zones */
- zoran_set_geo(ztv, &ztv->overinfo);
- zoran_built_overlay(ztv, vw.clipcount, vcp);
- vfree(vcp);
-
- /* if we were on, restart the video engine */
- if (on)
- zoran_cap(ztv, 1);
- break;
- }
-
- case VIDIOCCAPTURE:
- {
- int v;
- if (get_user(v, (int *)arg))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCCAPTURE(%d)\n",CARD,v));
-
- if (v==0) {
- clear_bit(STATE_OVERLAY, &ztv->state);
- zoran_cap(ztv, 1);
- }
- else {
- /* is VIDIOCSFBUF, VIDIOCSWIN done? */
- if (ztv->overinfo.busadr==0 || ztv->overinfo.w==0 || ztv->overinfo.h==0)
- return -EINVAL;
-
- set_bit(STATE_OVERLAY, &ztv->state);
- zoran_cap(ztv, 1);
- }
- break;
- }
-
- case VIDIOCGFBUF:
- {
- struct video_buffer v;
- DEBUG(printk(CARD_DEBUG "VIDIOCGFBUF\n",CARD));
- read_lock(&ztv->lock);
- v.base = (void *)ztv->overinfo.busadr;
- v.height = ztv->sheight;
- v.width = ztv->swidth;
- v.depth = ztv->depth;
- v.bytesperline = ztv->overinfo.bpl;
- read_unlock(&ztv->lock);
- if(copy_to_user(arg, &v,sizeof(v)))
- return -EFAULT;
- break;
- }
- case VIDIOCSFBUF:
- {
- struct video_buffer v;
- if(!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (pcipci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
- return -ENXIO;
- if (copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSFBUF(%p,%d,%d,%d,%d)\n",CARD,v.base, v.width,v.height,v.depth,v.bytesperline));
-
- if (v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32)
- return -EINVAL;
- if (v.bytesperline<1)
- return -EINVAL;
- if (ztv->running)
- return -EBUSY;
- write_lock_irq(&ztv->lock);
- ztv->overinfo.busadr = (ulong)v.base;
- ztv->sheight = v.height;
- ztv->swidth = v.width;
- ztv->depth = v.depth; /* bits per pixel */
- ztv->overinfo.bpp = ((v.depth+1)&0x38)/8;/* bytes per pixel */
- ztv->overinfo.bpl = v.bytesperline; /* bytes per line */
- write_unlock_irq(&ztv->lock);
- break;
- }
-
- case VIDIOCKEY:
- {
- /* Will be handled higher up .. */
- break;
- }
-
- case VIDIOCSYNC:
- {
- int i;
- if (get_user(i, (int *) arg))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d)\n",CARD,i));
- if (i<0 || i>ZORAN_MAX_FBUFFERS)
- return -EINVAL;
- switch (ztv->grabinfo[i].status) {
- case FBUFFER_FREE:
- return -EINVAL;
- case FBUFFER_BUSY:
- /* wait till this buffer gets grabbed */
- wait_event_interruptible(ztv->grabq,
- (ztv->grabinfo[i].status != FBUFFER_BUSY));
- /* see if a signal did it */
- if (signal_pending(current))
- return -EINTR;
- /* don't fall through; a DONE buffer is not UNUSED */
- break;
- case FBUFFER_DONE:
- ztv->grabinfo[i].status = FBUFFER_FREE;
- /* tell ppl we have a spare buffer */
- wake_up_interruptible(&ztv->grabq);
- break;
- }
- DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d) returns\n",CARD,i));
- break;
- }
-
- case VIDIOCMCAPTURE:
- {
- struct video_mmap vm;
- struct vidinfo* frame;
- if (copy_from_user(&vm,arg,sizeof(vm)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCMCAPTURE(%d,(%d,%d),%d)\n",CARD,vm.frame,vm.width,vm.height,vm.format));
- if (vm.frame<0 || vm.frame>ZORAN_MAX_FBUFFERS ||
- vm.width<32 || vm.width>768 ||
- vm.height<32 || vm.height>576 ||
- vm.format>NRPALETTES ||
- palette2fmt[vm.format].mode == 0)
- return -EINVAL;
-
- /* we are allowed to take over UNUSED and DONE buffers */
- frame = &ztv->grabinfo[vm.frame];
- if (frame->status == FBUFFER_BUSY)
- return -EBUSY;
-
- /* setup the other parameters if they are given */
- write_lock_irq(&ztv->lock);
- frame->w = vm.width;
- frame->h = vm.height;
- frame->format = vm.format;
- frame->bpp = palette2fmt[frame->format].bpp;
- frame->bpl = frame->w*frame->bpp;
- frame->status = FBUFFER_BUSY;
- frame->next = 0;
- { /* add to tail of queue */
- struct vidinfo* oldframe = ztv->workqueue;
- if (!oldframe) ztv->workqueue = frame;
- else {
- while (oldframe->next) oldframe = oldframe->next;
- oldframe->next = frame;
- }
- }
- write_unlock_irq(&ztv->lock);
- zoran_cap(ztv, 1);
- break;
- }
-
- case VIDIOCGMBUF:
- {
- struct video_mbuf mb;
- int i;
- DEBUG(printk(CARD_DEBUG "VIDIOCGMBUF\n",CARD));
- mb.size = ZORAN_MAX_FBUFSIZE;
- mb.frames = ZORAN_MAX_FBUFFERS;
- for (i=0; i<ZORAN_MAX_FBUFFERS; i++)
- mb.offsets[i] = i*ZORAN_MAX_FBUFFER;
- if(copy_to_user(arg, &mb,sizeof(mb)))
- return -EFAULT;
- break;
- }
-
- case VIDIOCGUNIT:
- {
- struct video_unit vu;
- DEBUG(printk(CARD_DEBUG "VIDIOCGUNIT\n",CARD));
- vu.video = ztv->video_dev.minor;
- vu.vbi = ztv->vbi_dev.minor;
- vu.radio = VIDEO_NO_UNIT;
- vu.audio = VIDEO_NO_UNIT;
- vu.teletext = VIDEO_NO_UNIT;
- if(copy_to_user(arg, &vu,sizeof(vu)))
- return -EFAULT;
- break;
- }
-
- case VIDIOCGFREQ:
- {
- unsigned long v = ztv->tuner_freq;
- if (copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCGFREQ\n",CARD));
- break;
- }
- case VIDIOCSFREQ:
- {
- unsigned long v;
- if (copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSFREQ\n",CARD));
-
- if (ztv->have_tuner) {
- int fixme = v;
- if (i2c_control_device(&(ztv->i2c), I2C_DRIVERID_TUNER, TUNER_SET_TVFREQ, &fixme) < 0)
- return -EAGAIN;
- }
- ztv->tuner_freq = v;
- break;
- }
-
- /* Why isn't this in the API?
- * And why doesn't it take a buffer number?
- case BTTV_FIELDNR:
- {
- unsigned long v = ztv->lastfieldnr;
- if (copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "BTTV_FIELDNR\n",CARD));
- break;
- }
- */
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static
-int zoran_mmap(struct vm_area_struct *vma, struct video_device* dev, const char* adr, unsigned long size)
-{
- struct zoran* ztv = (struct zoran*)dev;
- unsigned long start = (unsigned long)adr;
- unsigned long pos;
-
- DEBUG(printk(CARD_DEBUG "zoran_mmap(0x%p,%ld)\n",CARD,adr,size));
-
- /* sanity checks */
- if (size > ZORAN_MAX_FBUFSIZE || !ztv->fbuffer)
- return -EINVAL;
-
- /* start mapping the whole shabang to user memory */
- pos = (unsigned long)ztv->fbuffer;
- while (size>0) {
- unsigned long pfn = virt_to_phys((void*)pos) >> PAGE_SHIFT;
- if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- return 0;
-}
-
-static struct video_device zr36120_template=
-{
- .owner = THIS_MODULE,
- .name = "UNSET",
- .type = VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY,
- .hardware = VID_HARDWARE_ZR36120,
- .open = zoran_open,
- .close = zoran_close,
- .read = zoran_read,
- .write = zoran_write,
- .poll = zoran_poll,
- .ioctl = zoran_ioctl,
- .compat_ioctl = v4l_compat_ioctl32,
- .mmap = zoran_mmap,
- .minor = -1,
-};
-
-static
-int vbi_open(struct video_device *dev, int flags)
-{
- struct zoran *ztv = dev->priv;
- struct vidinfo* item;
-
- DEBUG(printk(CARD_DEBUG "vbi_open(dev,%d)\n",CARD,flags));
-
- /*
- * During VBI device open, we continiously grab VBI-like
- * data in the vbi buffer when we have nothing to do.
- * Only when there is an explicit request for VBI data
- * (read call) we /force/ a read.
- */
-
- /* allocate buffers */
- for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
- {
- item->status = FBUFFER_FREE;
-
- /* alloc */
- if (!item->memadr) {
- item->memadr = bmalloc(ZORAN_VBI_BUFSIZE);
- if (!item->memadr) {
- /* could not get a buffer, bail out */
- while (item != ztv->readinfo) {
- item--;
- bfree(item->memadr, ZORAN_VBI_BUFSIZE);
- item->memadr = 0;
- item->busadr = 0;
- }
- return -ENOBUFS;
- }
- }
-
- /* determine the DMAable address */
- item->busadr = virt_to_bus(item->memadr);
- }
-
- /* do the common part of all open's */
- zoran_common_open(ztv, flags);
-
- set_bit(STATE_VBI, &ztv->state);
- /* start read-ahead */
- zoran_cap(ztv, 1);
-
- return 0;
-}
-
-static
-void vbi_close(struct video_device *dev)
-{
- struct zoran *ztv = dev->priv;
- struct vidinfo* item;
-
- DEBUG(printk(CARD_DEBUG "vbi_close(dev)\n",CARD));
-
- /* driver specific closure */
- clear_bit(STATE_VBI, &ztv->state);
-
- zoran_common_close(ztv);
-
- /*
- * This is sucky but right now I can't find a good way to
- * be sure its safe to free the buffer. We wait 5-6 fields
- * which is more than sufficient to be sure.
- */
- msleep(100); /* Wait 1/10th of a second */
-
- for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
- {
- if (item->memadr)
- bfree(item->memadr, ZORAN_VBI_BUFSIZE);
- item->memadr = 0;
- }
-
-}
-
-/*
- * This read function could be used reentrant in a SMP situation.
- *
- * This is made possible by the spinlock which is kept till we
- * found and marked a buffer for our own use. The lock must
- * be released as soon as possible to prevent lock contention.
- */
-static
-long vbi_read(struct video_device* dev, char* buf, unsigned long count, int nonblock)
-{
- struct zoran *ztv = dev->priv;
- unsigned long max;
- struct vidinfo* unused = 0;
- struct vidinfo* done = 0;
-
- DEBUG(printk(CARD_DEBUG "vbi_read(0x%p,%ld,%d)\n",CARD,buf,count,nonblock));
-
- /* find ourself a free or completed buffer */
- for (;;) {
- struct vidinfo* item;
-
- write_lock_irq(&ztv->lock);
- for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) {
- if (!unused && item->status == FBUFFER_FREE)
- unused = item;
- if (!done && item->status == FBUFFER_DONE)
- done = item;
- }
- if (done || unused)
- break;
-
- /* no more free buffers, wait for them. */
- write_unlock_irq(&ztv->lock);
- if (nonblock)
- return -EWOULDBLOCK;
- interruptible_sleep_on(&ztv->vbiq);
- if (signal_pending(current))
- return -EINTR;
- }
-
- /* Do we have 'ready' data? */
- if (!done) {
- /* no? than this will take a while... */
- if (nonblock) {
- write_unlock_irq(&ztv->lock);
- return -EWOULDBLOCK;
- }
-
- /* mark the unused buffer as wanted */
- unused->status = FBUFFER_BUSY;
- unused->next = 0;
- { /* add to tail of queue */
- struct vidinfo* oldframe = ztv->workqueue;
- if (!oldframe) ztv->workqueue = unused;
- else {
- while (oldframe->next) oldframe = oldframe->next;
- oldframe->next = unused;
- }
- }
- write_unlock_irq(&ztv->lock);
-
- /* tell the state machine we want it filled /NOW/ */
- zoran_cap(ztv, 1);
-
- /* wait till this buffer gets grabbed */
- wait_event_interruptible(ztv->vbiq,
- (unused->status != FBUFFER_BUSY));
- /* see if a signal did it */
- if (signal_pending(current))
- return -EINTR;
- done = unused;
- }
- else
- write_unlock_irq(&ztv->lock);
-
- /* Yes! we got data! */
- max = done->bpl * -done->h;
- if (count > max)
- count = max;
-
- /* check if the user gave us enough room to write the data */
- if (!access_ok(VERIFY_WRITE, buf, count)) {
- count = -EFAULT;
- goto out;
- }
-
- /*
- * Now transform/strip the data from YUV to Y-only
- * NB. Assume the Y is in the LSB of the YUV data.
- */
- {
- unsigned char* optr = buf;
- unsigned char* eptr = buf+count;
-
- /* are we beeing accessed from an old driver? */
- if (count == 2*19*2048) {
- /*
- * Extreme HACK, old VBI programs expect 2048 points
- * of data, and we only got 864 orso. Double each
- * datapoint and clear the rest of the line.
- * This way we have appear to have a
- * sample_frequency of 29.5 Mc.
- */
- int x,y;
- unsigned char* iptr = done->memadr+1;
- for (y=done->h; optr<eptr && y<0; y++)
- {
- /* copy to doubled data to userland */
- for (x=0; optr+1<eptr && x<-done->w; x++)
- {
- unsigned char a = iptr[x*2];
- __put_user(a, optr++);
- __put_user(a, optr++);
- }
- /* and clear the rest of the line */
- for (x*=2; optr<eptr && x<done->bpl; x++)
- __put_user(0, optr++);
- /* next line */
- iptr += done->bpl;
- }
- }
- else {
- /*
- * Other (probably newer) programs asked
- * us what geometry we are using, and are
- * reading the correct size.
- */
- int x,y;
- unsigned char* iptr = done->memadr+1;
- for (y=done->h; optr<eptr && y<0; y++)
- {
- /* copy to doubled data to userland */
- for (x=0; optr<eptr && x<-done->w; x++)
- __put_user(iptr[x*2], optr++);
- /* and clear the rest of the line */
- for (;optr<eptr && x<done->bpl; x++)
- __put_user(0, optr++);
- /* next line */
- iptr += done->bpl;
- }
- }
-
- /* API compliance:
- * place the framenumber (half fieldnr) in the last long
- */
- __put_user(done->fieldnr/2, ((ulong*)eptr)[-1]);
- }
-
- /* keep the engine running */
- done->status = FBUFFER_FREE;
- zoran_cap(ztv, 1);
-
- /* tell listeners this buffer just became free */
- wake_up_interruptible(&ztv->vbiq);
-
- /* goodbye */
-out:
- DEBUG(printk(CARD_DEBUG "vbi_read() returns %lu\n",CARD,count));
- return count;
-}
-
-static
-unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait)
-{
- struct zoran *ztv = dev->priv;
- struct vidinfo* item;
- unsigned int mask = 0;
-
- poll_wait(file, &ztv->vbiq, wait);
-
- for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
- if (item->status == FBUFFER_DONE)
- {
- mask |= (POLLIN | POLLRDNORM);
- break;
- }
-
- DEBUG(printk(CARD_DEBUG "vbi_poll()=%x\n",CARD,mask));
-
- return mask;
-}
-
-static
-int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
- struct zoran* ztv = dev->priv;
-
- switch (cmd) {
- case VIDIOCGVBIFMT:
- {
- struct vbi_format f;
- DEBUG(printk(CARD_DEBUG "VIDIOCGVBIINFO\n",CARD));
- f.sampling_rate = 14750000UL;
- f.samples_per_line = -ztv->readinfo[0].w;
- f.sample_format = VIDEO_PALETTE_RAW;
- f.start[0] = f.start[1] = ztv->readinfo[0].y;
- f.start[1] += 312;
- f.count[0] = f.count[1] = -ztv->readinfo[0].h;
- f.flags = VBI_INTERLACED;
- if (copy_to_user(arg,&f,sizeof(f)))
- return -EFAULT;
- break;
- }
- case VIDIOCSVBIFMT:
- {
- struct vbi_format f;
- int i;
- if (copy_from_user(&f, arg,sizeof(f)))
- return -EFAULT;
- DEBUG(printk(CARD_DEBUG "VIDIOCSVBIINFO(%d,%d,%d,%d,%d,%d,%d,%x)\n",CARD,f.sampling_rate,f.samples_per_line,f.sample_format,f.start[0],f.start[1],f.count[0],f.count[1],f.flags));
-
- /* lots of parameters are fixed... (PAL) */
- if (f.sampling_rate != 14750000UL ||
- f.samples_per_line > 864 ||
- f.sample_format != VIDEO_PALETTE_RAW ||
- f.start[0] < 0 ||
- f.start[0] != f.start[1]-312 ||
- f.count[0] != f.count[1] ||
- f.start[0]+f.count[0] >= 288 ||
- f.flags != VBI_INTERLACED)
- return -EINVAL;
-
- write_lock_irq(&ztv->lock);
- ztv->readinfo[0].y = f.start[0];
- ztv->readinfo[0].w = -f.samples_per_line;
- ztv->readinfo[0].h = -f.count[0];
- ztv->readinfo[0].bpl = f.samples_per_line*ztv->readinfo[0].bpp;
- for (i=1; i<ZORAN_VBI_BUFFERS; i++)
- ztv->readinfo[i] = ztv->readinfo[i];
- write_unlock_irq(&ztv->lock);
- break;
- }
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static struct video_device vbi_template=
-{
- .owner = THIS_MODULE,
- .name = "UNSET",
- .type = VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
- .hardware = VID_HARDWARE_ZR36120,
- .open = vbi_open,
- .close = vbi_close,
- .read = vbi_read,
- .write = zoran_write,
- .poll = vbi_poll,
- .ioctl = vbi_ioctl,
- .minor = -1,
-};
-
-/*
- * Scan for a Zoran chip, request the irq and map the io memory
- */
-static
-int __init find_zoran(void)
-{
- int result;
- struct zoran *ztv;
- struct pci_dev *dev = NULL;
- unsigned char revision;
- int zoran_num = 0;
-
- while ((dev = pci_get_device(PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120, dev)))
- {
- /* Ok, a ZR36120/ZR36125 found! */
- ztv = &zorans[zoran_num];
- ztv->dev = dev;
-
- if (pci_enable_device(dev))
- continue;
-
- pci_read_config_byte(dev, PCI_CLASS_REVISION, &revision);
- printk(KERN_INFO "zoran: Zoran %x (rev %d) ",
- dev->device, revision);
- printk("bus: %d, devfn: %d, irq: %d, ",
- dev->bus->number, dev->devfn, dev->irq);
- printk("memory: 0x%08lx.\n", ztv->zoran_adr);
-
- ztv->zoran_mem = ioremap(ztv->zoran_adr, 0x1000);
- DEBUG(printk(KERN_DEBUG "zoran: mapped-memory at 0x%p\n",ztv->zoran_mem));
-
- result = request_irq(dev->irq, zoran_irq,
- IRQF_SHARED|IRQF_DISABLED,"zoran", ztv);
- if (result==-EINVAL)
- {
- iounmap(ztv->zoran_mem);
- printk(KERN_ERR "zoran: Bad irq number or handler\n");
- continue;
- }
- if (result==-EBUSY)
- printk(KERN_ERR "zoran: IRQ %d busy, change your PnP config in BIOS\n",dev->irq);
- if (result < 0) {
- iounmap(ztv->zoran_mem);
- continue;
- }
- /* Enable bus-mastering */
- pci_set_master(dev);
- /* Keep a reference */
- pci_dev_get(dev);
- zoran_num++;
- }
- if(zoran_num)
- printk(KERN_INFO "zoran: %d Zoran card(s) found.\n",zoran_num);
- return zoran_num;
-}
-
-static
-int __init init_zoran(int card)
-{
- struct zoran *ztv = &zorans[card];
- int i;
-
- /* if the given cardtype valid? */
- if (cardtype[card]>=NRTVCARDS) {
- printk(KERN_INFO "invalid cardtype(%d) detected\n",cardtype[card]);
- return -1;
- }
-
- /* reset the zoran */
- zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI);
- udelay(10);
- zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI);
- udelay(10);
-
- /* zoran chip specific details */
- ztv->card = tvcards+cardtype[card]; /* point to the selected card */
- ztv->norm = 0; /* PAL */
- ztv->tuner_freq = 0;
-
- /* videocard details */
- ztv->swidth = 800;
- ztv->sheight = 600;
- ztv->depth = 16;
-
- /* State details */
- ztv->fbuffer = 0;
- ztv->overinfo.kindof = FBUFFER_OVERLAY;
- ztv->overinfo.status = FBUFFER_FREE;
- ztv->overinfo.x = 0;
- ztv->overinfo.y = 0;
- ztv->overinfo.w = 768; /* 640 */
- ztv->overinfo.h = 576; /* 480 */
- ztv->overinfo.format = VIDEO_PALETTE_RGB565;
- ztv->overinfo.bpp = palette2fmt[ztv->overinfo.format].bpp;
- ztv->overinfo.bpl = ztv->overinfo.bpp*ztv->swidth;
- ztv->overinfo.busadr = 0;
- ztv->overinfo.memadr = 0;
- ztv->overinfo.overlay = 0;
- for (i=0; i<ZORAN_MAX_FBUFFERS; i++) {
- ztv->grabinfo[i] = ztv->overinfo;
- ztv->grabinfo[i].kindof = FBUFFER_GRAB;
- }
- init_waitqueue_head(&ztv->grabq);
-
- /* VBI details */
- ztv->readinfo[0] = ztv->overinfo;
- ztv->readinfo[0].kindof = FBUFFER_VBI;
- ztv->readinfo[0].w = -864;
- ztv->readinfo[0].h = -38;
- ztv->readinfo[0].format = VIDEO_PALETTE_YUV422;
- ztv->readinfo[0].bpp = palette2fmt[ztv->readinfo[0].format].bpp;
- ztv->readinfo[0].bpl = 1024*ztv->readinfo[0].bpp;
- for (i=1; i<ZORAN_VBI_BUFFERS; i++)
- ztv->readinfo[i] = ztv->readinfo[0];
- init_waitqueue_head(&ztv->vbiq);
-
- /* maintenance data */
- ztv->have_decoder = 0;
- ztv->have_tuner = 0;
- ztv->tuner_type = 0;
- ztv->running = 0;
- ztv->users = 0;
- rwlock_init(&ztv->lock);
- ztv->workqueue = 0;
- ztv->fieldnr = 0;
- ztv->lastfieldnr = 0;
-
- if (triton1)
- zrand(~ZORAN_VDC_TRICOM, ZORAN_VDC);
-
- /* external FL determines TOP frame */
- zror(ZORAN_VFEC_EXTFL, ZORAN_VFEC);
-
- /* set HSpol */
- if (ztv->card->hsync_pos)
- zrwrite(ZORAN_VFEH_HSPOL, ZORAN_VFEH);
- /* set VSpol */
- if (ztv->card->vsync_pos)
- zrwrite(ZORAN_VFEV_VSPOL, ZORAN_VFEV);
-
- /* Set the proper General Purpuse register bits */
- /* implicit: no softreset, 0 waitstates */
- zrwrite(ZORAN_PCI_SOFTRESET|(ztv->card->gpdir<<0),ZORAN_PCI);
- /* implicit: 3 duration and recovery PCI clocks on guest 0-3 */
- zrwrite(ztv->card->gpval<<24,ZORAN_GUEST);
-
- /* clear interrupt status */
- zrwrite(~0, ZORAN_ISR);
-
- /*
- * i2c template
- */
- ztv->i2c = zoran_i2c_bus_template;
- sprintf(ztv->i2c.name,"zoran-%d",card);
- ztv->i2c.data = ztv;
-
- /*
- * Now add the template and register the device unit
- */
- ztv->video_dev = zr36120_template;
- strcpy(ztv->video_dev.name, ztv->i2c.name);
- ztv->video_dev.priv = ztv;
- if (video_register_device(&ztv->video_dev, VFL_TYPE_GRABBER, video_nr) < 0)
- return -1;
-
- ztv->vbi_dev = vbi_template;
- strcpy(ztv->vbi_dev.name, ztv->i2c.name);
- ztv->vbi_dev.priv = ztv;
- if (video_register_device(&ztv->vbi_dev, VFL_TYPE_VBI, vbi_nr) < 0) {
- video_unregister_device(&ztv->video_dev);
- return -1;
- }
- i2c_register_bus(&ztv->i2c);
-
- /* set interrupt mask - the PIN enable will be set later */
- zrwrite(ZORAN_ICR_GIRQ0|ZORAN_ICR_GIRQ1|ZORAN_ICR_CODE, ZORAN_ICR);
-
- printk(KERN_INFO "%s: installed %s\n",ztv->i2c.name,ztv->card->name);
- return 0;
-}
-
-static
-void release_zoran(int max)
-{
- struct zoran *ztv;
- int i;
-
- for (i=0;i<max; i++)
- {
- ztv = &zorans[i];
-
- /* turn off all capturing, DMA and IRQs */
- /* reset the zoran */
- zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI);
- udelay(10);
- zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI);
- udelay(10);
-
- /* first disable interrupts before unmapping the memory! */
- zrwrite(0, ZORAN_ICR);
- zrwrite(0xffffffffUL,ZORAN_ISR);
-
- /* free it */
- free_irq(ztv->dev->irq,ztv);
-
- /* unregister i2c_bus */
- i2c_unregister_bus((&ztv->i2c));
-
- /* unmap and free memory */
- if (ztv->zoran_mem)
- iounmap(ztv->zoran_mem);
-
- /* Drop PCI device */
- pci_dev_put(ztv->dev);
-
- video_unregister_device(&ztv->video_dev);
- video_unregister_device(&ztv->vbi_dev);
- }
-}
-
-void __exit zr36120_exit(void)
-{
- release_zoran(zoran_cards);
-}
-
-int __init zr36120_init(void)
-{
- int card;
-
- handle_chipset();
- zoran_cards = find_zoran();
- if (zoran_cards <= 0)
- return -EIO;
-
- /* initialize Zorans */
- for (card=0; card<zoran_cards; card++) {
- if (init_zoran(card) < 0) {
- /* only release the zorans we have registered */
- release_zoran(card);
- return -EIO;
- }
- }
- return 0;
-}
-
-module_init(zr36120_init);
-module_exit(zr36120_exit);
diff --git a/drivers/media/video/zr36120.h b/drivers/media/video/zr36120.h
deleted file mode 100644
index a71e485..0000000
--- a/drivers/media/video/zr36120.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- zr36120.h - Zoran 36120/36125 based framegrabbers
-
- Copyright (C) 1998-1999 Pauline Middelink (middelin@polyware.nl)
-
- 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 _ZR36120_H
-#define _ZR36120_H
-
-#ifdef __KERNEL__
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-#include <linux/i2c-old.h>
-#include <linux/videodev.h>
-
-#include <asm/io.h>
-
-/*
- * Debug macro's, place an x behind the ) for actual debug-compilation
- * E.g. #define DEBUG(x...) x
- */
-#define DEBUG(x...) /* Debug driver */
-#define IDEBUG(x...) /* Debug interrupt handler */
-#define PDEBUG 0 /* Debug PCI writes */
-
-/* defined in zr36120_i2c */
-extern struct i2c_bus zoran_i2c_bus_template;
-
-#define ZORAN_MAX_FBUFFERS 2
-#define ZORAN_MAX_FBUFFER (768*576*2)
-#define ZORAN_MAX_FBUFSIZE (ZORAN_MAX_FBUFFERS*ZORAN_MAX_FBUFFER)
-
-#define ZORAN_VBI_BUFFERS 2
-#define ZORAN_VBI_BUFSIZE (22*1024*2)
-
-struct tvcard {
- char* name; /* name of the cardtype */
- int video_inputs; /* number of channels defined in video_mux */
- int audio_inputs; /* number of channels defined in audio_mux */
- __u32 swapi2c:1, /* need to swap i2c wires SDA/SCL? */
- usegirq1:1, /* VSYNC at GIRQ1 instead of GIRQ0? */
- vsync_pos:1, /* positive VSYNC signal? */
- hsync_pos:1, /* positive HSYNC signal? */
- gpdir:8, /* General Purpose Direction register */
- gpval:8; /* General Purpose Value register */
- int video_mux[6]; /* mapping channel number to physical input */
-#define IS_TUNER 0x80
-#define IS_SVHS 0x40
-#define CHANNEL_MASK 0x3F
- int audio_mux[6]; /* mapping channel number to physical input */
-};
-#define TUNER(x) ((x)|IS_TUNER)
-#define SVHS(x) ((x)|IS_SVHS)
-
-struct vidinfo {
- struct vidinfo* next; /* next active buffer */
- uint kindof;
-#define FBUFFER_OVERLAY 0
-#define FBUFFER_GRAB 1
-#define FBUFFER_VBI 2
- uint status;
-#define FBUFFER_FREE 0
-#define FBUFFER_BUSY 1
-#define FBUFFER_DONE 2
- ulong fieldnr; /* # of field, not framer! */
- uint x,y;
- int w,h; /* w,h can be negative! */
- uint format; /* index in palette2fmt[] */
- uint bpp; /* lookup from palette2fmt[] */
- uint bpl; /* calc: width * bpp */
- ulong busadr; /* bus addr for DMA engine */
- char* memadr; /* kernel addr for making copies */
- ulong* overlay; /* kernel addr of overlay mask */
-};
-
-struct zoran
-{
- struct video_device video_dev;
-#define CARD_DEBUG KERN_DEBUG "%s(%lu): "
-#define CARD_INFO KERN_INFO "%s(%lu): "
-#define CARD_ERR KERN_ERR "%s(%lu): "
-#define CARD ztv->video_dev.name,ztv->fieldnr
-
- /* zoran chip specific details */
- struct i2c_bus i2c; /* i2c registration data */
- struct pci_dev* dev; /* ptr to PCI device */
- ulong zoran_adr; /* bus address of IO memory */
- char* zoran_mem; /* kernel address of IO memory */
- struct tvcard* card; /* the cardtype */
- uint norm; /* 0=PAL, 1=NTSC, 2=SECAM */
- uint tuner_freq; /* Current freq in kHz */
- struct video_picture picture; /* Current picture params */
-
- /* videocard details */
- uint swidth; /* screen width */
- uint sheight; /* screen height */
- uint depth; /* depth in bits */
-
- /* State details */
- char* fbuffer; /* framebuffers for mmap */
- struct vidinfo overinfo; /* overlay data */
- struct vidinfo grabinfo[ZORAN_MAX_FBUFFERS]; /* grabbing data*/
- wait_queue_head_t grabq; /* grabbers queue */
-
- /* VBI details */
- struct video_device vbi_dev;
- struct vidinfo readinfo[2]; /* VBI data - flip buffers */
- wait_queue_head_t vbiq; /* vbi queue */
-
- /* maintenance data */
- int have_decoder; /* did we detect a mux? */
- int have_tuner; /* did we detect a tuner? */
- int users; /* howmany video/vbi open? */
- int tuner_type; /* tuner type, when found */
- int running; /* are we rolling? */
- rwlock_t lock;
- long state; /* what is requested of us? */
-#define STATE_OVERLAY 0
-#define STATE_VBI 1
- struct vidinfo* workqueue; /* buffers to grab, head is active */
- ulong fieldnr; /* #field, ticked every VSYNC */
- ulong lastfieldnr; /* #field, ticked every GRAB */
-
- int vidInterlace; /* calculated */
- int vidXshift; /* calculated */
- uint vidWidth; /* calculated */
- uint vidHeight; /* calculated */
-};
-
-#define zrwrite(dat,adr) writel((dat),(char *) (ztv->zoran_mem+(adr)))
-#define zrread(adr) readl(ztv->zoran_mem+(adr))
-
-#if PDEBUG == 0
-#define zrand(dat,adr) zrwrite((dat) & zrread(adr), adr)
-#define zror(dat,adr) zrwrite((dat) | zrread(adr), adr)
-#define zraor(dat,mask,adr) zrwrite( ((dat)&~(mask)) | ((mask)&zrread(adr)), adr)
-#else
-#define zrand(dat, adr) \
-do { \
- ulong data = (dat) & zrread((adr)); \
- zrwrite(data, (adr)); \
- if (0 != (~(dat) & zrread((adr)))) \
- printk(KERN_DEBUG "zoran: zrand at %d(%d) detected set bits(%x)\n", __LINE__, (adr), (dat)); \
-} while(0)
-
-#define zror(dat, adr) \
-do { \
- ulong data = (dat) | zrread((adr)); \
- zrwrite(data, (adr)); \
- if ((dat) != ((dat) & zrread(adr))) \
- printk(KERN_DEBUG "zoran: zror at %d(%d) detected unset bits(%x)\n", __LINE__, (adr), (dat)); \
-} while(0)
-
-#define zraor(dat, mask, adr) \
-do { \
- ulong data; \
- if ((dat) & (mask)) \
- printk(KERN_DEBUG "zoran: zraor at %d(%d) detected bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \
- data = ((dat)&~(mask)) | ((mask) & zrread((adr))); \
- zrwrite(data,(adr)); \
- if ( (dat) != (~(mask) & zrread((adr))) ) \
- printk(KERN_DEBUG "zoran: zraor at %d(%d) could not set all bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \
-} while(0)
-#endif
-
-#endif
-
-/* zoran PCI address space */
-#define ZORAN_VFEH 0x000 /* Video Front End Horizontal Conf. */
-#define ZORAN_VFEH_HSPOL (1<<30)
-#define ZORAN_VFEH_HSTART (0x3FF<<10)
-#define ZORAN_VFEH_HEND (0x3FF<<0)
-
-#define ZORAN_VFEV 0x004 /* Video Front End Vertical Conf. */
-#define ZORAN_VFEV_VSPOL (1<<30)
-#define ZORAN_VFEV_VSTART (0x3FF<<10)
-#define ZORAN_VFEV_VEND (0x3FF<<0)
-
-#define ZORAN_VFEC 0x008 /* Video Front End Scaler and Pixel */
-#define ZORAN_VFEC_EXTFL (1<<26)
-#define ZORAN_VFEC_TOPFIELD (1<<25)
-#define ZORAN_VFEC_VCLKPOL (1<<24)
-#define ZORAN_VFEC_HFILTER (7<<21)
-#define ZORAN_VFEC_HFILTER_1 (0<<21) /* no lumi, 3-tap chromo */
-#define ZORAN_VFEC_HFILTER_2 (1<<21) /* 3-tap lumi, 3-tap chromo */
-#define ZORAN_VFEC_HFILTER_3 (2<<21) /* 4-tap lumi, 4-tap chromo */
-#define ZORAN_VFEC_HFILTER_4 (3<<21) /* 5-tap lumi, 4-tap chromo */
-#define ZORAN_VFEC_HFILTER_5 (4<<21) /* 4-tap lumi, 4-tap chromo */
-#define ZORAN_VFEC_DUPFLD (1<<20)
-#define ZORAN_VFEC_HORDCM (63<<14)
-#define ZORAN_VFEC_VERDCM (63<<8)
-#define ZORAN_VFEC_DISPMOD (1<<6)
-#define ZORAN_VFEC_RGB (3<<3)
-#define ZORAN_VFEC_RGB_YUV422 (0<<3)
-#define ZORAN_VFEC_RGB_RGB888 (1<<3)
-#define ZORAN_VFEC_RGB_RGB565 (2<<3)
-#define ZORAN_VFEC_RGB_RGB555 (3<<3)
-#define ZORAN_VFEC_ERRDIF (1<<2)
-#define ZORAN_VFEC_PACK24 (1<<1)
-#define ZORAN_VFEC_LE (1<<0)
-
-#define ZORAN_VTOP 0x00C /* Video Display "Top" */
-
-#define ZORAN_VBOT 0x010 /* Video Display "Bottom" */
-
-#define ZORAN_VSTR 0x014 /* Video Display Stride */
-#define ZORAN_VSTR_DISPSTRIDE (0xFFFF<<16)
-#define ZORAN_VSTR_VIDOVF (1<<8)
-#define ZORAN_VSTR_SNAPSHOT (1<<1)
-#define ZORAN_VSTR_GRAB (1<<0)
-
-#define ZORAN_VDC 0x018 /* Video Display Conf. */
-#define ZORAN_VDC_VIDEN (1<<31)
-#define ZORAN_VDC_MINPIX (0x1F<<25)
-#define ZORAN_VDC_TRICOM (1<<24)
-#define ZORAN_VDC_VIDWINHT (0x3FF<<12)
-#define ZORAN_VDC_VIDWINWID (0x3FF<<0)
-
-#define ZORAN_MTOP 0x01C /* Masking Map "Top" */
-
-#define ZORAN_MBOT 0x020 /* Masking Map "Bottom" */
-
-#define ZORAN_OCR 0x024 /* Overlay Control */
-#define ZORAN_OCR_OVLEN (1<<15)
-#define ZORAN_OCR_MASKSTRIDE (0xFF<<0)
-
-#define ZORAN_PCI 0x028 /* System, PCI and GPP Control */
-#define ZORAN_PCI_SOFTRESET (1<<24)
-#define ZORAN_PCI_WAITSTATE (3<<16)
-#define ZORAN_PCI_GENPURDIR (0xFF<<0)
-
-#define ZORAN_GUEST 0x02C /* GuestBus Control */
-
-#define ZORAN_CSOURCE 0x030 /* Code Source Address */
-
-#define ZORAN_CTRANS 0x034 /* Code Transfer Control */
-
-#define ZORAN_CMEM 0x038 /* Code Memory Pointer */
-
-#define ZORAN_ISR 0x03C /* Interrupt Status Register */
-#define ZORAN_ISR_CODE (1<<28)
-#define ZORAN_ISR_GIRQ0 (1<<29)
-#define ZORAN_ISR_GIRQ1 (1<<30)
-
-#define ZORAN_ICR 0x040 /* Interrupt Control Register */
-#define ZORAN_ICR_EN (1<<24)
-#define ZORAN_ICR_CODE (1<<28)
-#define ZORAN_ICR_GIRQ0 (1<<29)
-#define ZORAN_ICR_GIRQ1 (1<<30)
-
-#define ZORAN_I2C 0x044 /* I2C-Bus */
-#define ZORAN_I2C_SCL (1<<1)
-#define ZORAN_I2C_SDA (1<<0)
-
-#define ZORAN_POST 0x48 /* PostOffice */
-#define ZORAN_POST_PEN (1<<25)
-#define ZORAN_POST_TIME (1<<24)
-#define ZORAN_POST_DIR (1<<23)
-#define ZORAN_POST_GUESTID (3<<20)
-#define ZORAN_POST_GUEST (7<<16)
-#define ZORAN_POST_DATA (0xFF<<0)
-
-#endif
diff --git a/drivers/media/video/zr36120_i2c.c b/drivers/media/video/zr36120_i2c.c
deleted file mode 100644
index 21fde43..0000000
--- a/drivers/media/video/zr36120_i2c.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- zr36120_i2c.c - Zoran 36120/36125 based framegrabbers
-
- Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
- 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/types.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-
-#include <linux/video_decoder.h>
-#include <asm/uaccess.h>
-
-#include "tuner.h"
-#include "zr36120.h"
-
-/* ----------------------------------------------------------------------- */
-/* I2C functions */
-/* ----------------------------------------------------------------------- */
-
-/* software I2C functions */
-
-#define I2C_DELAY 10
-
-static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data)
-{
- struct zoran *ztv = (struct zoran*)bus->data;
- unsigned int b = 0;
- if (data) b |= ztv->card->swapi2c ? ZORAN_I2C_SCL : ZORAN_I2C_SDA;
- if (ctrl) b |= ztv->card->swapi2c ? ZORAN_I2C_SDA : ZORAN_I2C_SCL;
- zrwrite(b, ZORAN_I2C);
- udelay(I2C_DELAY);
-}
-
-static int i2c_getdataline(struct i2c_bus *bus)
-{
- struct zoran *ztv = (struct zoran*)bus->data;
- if (ztv->card->swapi2c)
- return zrread(ZORAN_I2C) & ZORAN_I2C_SCL;
- return zrread(ZORAN_I2C) & ZORAN_I2C_SDA;
-}
-
-static
-void attach_inform(struct i2c_bus *bus, int id)
-{
- struct zoran *ztv = (struct zoran*)bus->data;
- struct video_decoder_capability dc;
- int rv;
-
- switch (id) {
- case I2C_DRIVERID_VIDEODECODER:
- DEBUG(printk(CARD_INFO "decoder attached\n",CARD));
-
- /* fetch the capabilities of the decoder */
- rv = i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_CAPABILITIES, &dc);
- if (rv) {
- DEBUG(printk(CARD_DEBUG "decoder is not V4L aware!\n",CARD));
- break;
- }
- DEBUG(printk(CARD_DEBUG "capabilities %d %d %d\n",CARD,dc.flags,dc.inputs,dc.outputs));
-
- /* Test if the decoder can de VBI transfers */
- if (dc.flags & 16 /*VIDEO_DECODER_VBI*/)
- ztv->have_decoder = 2;
- else
- ztv->have_decoder = 1;
- break;
- case I2C_DRIVERID_TUNER:
- ztv->have_tuner = 1;
- DEBUG(printk(CARD_INFO "tuner attached\n",CARD));
- if (ztv->tuner_type >= 0)
- {
- if (i2c_control_device(&ztv->i2c,I2C_DRIVERID_TUNER,TUNER_SET_TYPE,&ztv->tuner_type)<0)
- DEBUG(printk(CARD_INFO "attach_inform; tuner won't be set to type %d\n",CARD,ztv->tuner_type));
- }
- break;
- default:
- DEBUG(printk(CARD_INFO "attach_inform; unknown device id=%d\n",CARD,id));
- break;
- }
-}
-
-static
-void detach_inform(struct i2c_bus *bus, int id)
-{
- struct zoran *ztv = (struct zoran*)bus->data;
-
- switch (id) {
- case I2C_DRIVERID_VIDEODECODER:
- ztv->have_decoder = 0;
- DEBUG(printk(CARD_INFO "decoder detached\n",CARD));
- break;
- case I2C_DRIVERID_TUNER:
- ztv->have_tuner = 0;
- DEBUG(printk(CARD_INFO "tuner detached\n",CARD));
- break;
- default:
- DEBUG(printk(CARD_INFO "detach_inform; unknown device id=%d\n",CARD,id));
- break;
- }
-}
-
-struct i2c_bus zoran_i2c_bus_template =
-{
- "ZR36120",
- I2C_BUSID_ZORAN,
- NULL,
-
- SPIN_LOCK_UNLOCKED,
-
- attach_inform,
- detach_inform,
-
- i2c_setlines,
- i2c_getdataline,
- NULL,
- NULL
-};
diff --git a/drivers/media/video/zr36120_mem.c b/drivers/media/video/zr36120_mem.c
deleted file mode 100644
index 416eaa9..0000000
--- a/drivers/media/video/zr36120_mem.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- zr36120_mem.c - Zoran 36120/36125 based framegrabbers
-
- Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
- 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/mm.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/io.h>
-#ifdef CONFIG_BIGPHYS_AREA
-#include <linux/bigphysarea.h>
-#endif
-
-#include "zr36120.h"
-#include "zr36120_mem.h"
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-void* bmalloc(unsigned long size)
-{
- void* mem;
-#ifdef CONFIG_BIGPHYS_AREA
- mem = bigphysarea_alloc_pages(size/PAGE_SIZE, 1, GFP_KERNEL);
-#else
- /*
- * The following function got a lot of memory at boottime,
- * so we know its always there...
- */
- mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));
-#endif
- if (mem) {
- unsigned long adr = (unsigned long)mem;
- while (size > 0) {
- SetPageReserved(virt_to_page(phys_to_virt(adr)));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- }
- return mem;
-}
-
-void bfree(void* mem, unsigned long size)
-{
- if (mem) {
- unsigned long adr = (unsigned long)mem;
- unsigned long siz = size;
- while (siz > 0) {
- ClearPageReserved(virt_to_page(phys_to_virt(adr)));
- adr += PAGE_SIZE;
- siz -= PAGE_SIZE;
- }
-#ifdef CONFIG_BIGPHYS_AREA
- bigphysarea_free_pages(mem);
-#else
- free_pages((unsigned long)mem,get_order(size));
-#endif
- }
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zr36120_mem.h b/drivers/media/video/zr36120_mem.h
deleted file mode 100644
index aad117a..0000000
--- a/drivers/media/video/zr36120_mem.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* either kmalloc() or bigphysarea() alloced memory - continuous */
-void* bmalloc(unsigned long size);
-void bfree(void* mem, unsigned long size);
diff --git a/drivers/message/i2o/core.h b/drivers/message/i2o/core.h
index dc388a3..cbe384f 100644
--- a/drivers/message/i2o/core.h
+++ b/drivers/message/i2o/core.h
@@ -18,7 +18,7 @@ extern struct i2o_driver i2o_exec_driver;
extern int i2o_exec_lct_get(struct i2o_controller *);
extern int __init i2o_exec_init(void);
-extern void __exit i2o_exec_exit(void);
+extern void i2o_exec_exit(void);
/* driver */
extern struct bus_type i2o_bus_type;
@@ -26,7 +26,7 @@ extern struct bus_type i2o_bus_type;
extern int i2o_driver_dispatch(struct i2o_controller *, u32);
extern int __init i2o_driver_init(void);
-extern void __exit i2o_driver_exit(void);
+extern void i2o_driver_exit(void);
/* PCI */
extern int __init i2o_pci_init(void);
diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c
index 9104b65..d3235f2 100644
--- a/drivers/message/i2o/driver.c
+++ b/drivers/message/i2o/driver.c
@@ -362,7 +362,7 @@ int __init i2o_driver_init(void)
*
* Unregisters the I2O bus and frees driver array.
*/
-void __exit i2o_driver_exit(void)
+void i2o_driver_exit(void)
{
bus_unregister(&i2o_bus_type);
kfree(i2o_drivers);
diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c
index 902753b..a539d3b 100644
--- a/drivers/message/i2o/exec-osm.c
+++ b/drivers/message/i2o/exec-osm.c
@@ -595,7 +595,7 @@ int __init i2o_exec_init(void)
*
* Unregisters the Exec OSM from the I2O core.
*/
-void __exit i2o_exec_exit(void)
+void i2o_exec_exit(void)
{
i2o_driver_unregister(&i2o_exec_driver);
};
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index 1de30d7..e33d446 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -186,7 +186,7 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
if (!dev)
return -ENXIO;
- ops = (u8 *) kmalloc(kcmd.oplen, GFP_KERNEL);
+ ops = kmalloc(kcmd.oplen, GFP_KERNEL);
if (!ops)
return -ENOMEM;
@@ -199,7 +199,7 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
* It's possible to have a _very_ large table
* and that the user asks for all of it at once...
*/
- res = (u8 *) kmalloc(65536, GFP_KERNEL);
+ res = kmalloc(65536, GFP_KERNEL);
if (!res) {
kfree(ops);
return -ENOMEM;
diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c
index 4633dbc..08a33c3 100644
--- a/drivers/mmc/at91_mci.c
+++ b/drivers/mmc/at91_mci.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/mmc/at91_mci.c - ATMEL AT91RM9200 MCI Driver
+ * linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver
*
* Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
*
@@ -11,7 +11,7 @@
*/
/*
- This is the AT91RM9200 MCI driver that has been tested with both MMC cards
+ This is the AT91 MCI driver that has been tested with both MMC cards
and SD-cards. Boards that support write protect are now supported.
The CCAT91SBC001 board does not support SD cards.
@@ -38,8 +38,8 @@
controller to manage the transfers.
A read is done from the controller directly to the scatterlist passed in from the request.
- Due to a bug in the controller, when a read is completed, all the words are byte
- swapped in the scatterlist buffers.
+ Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte
+ swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug.
The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY
@@ -72,6 +72,7 @@
#include <asm/irq.h>
#include <asm/mach/mmc.h>
#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/at91_mci.h>
#include <asm/arch/at91_pdc.h>
@@ -80,34 +81,18 @@
#undef SUPPORT_4WIRE
-static struct clk *mci_clk;
+#define FL_SENT_COMMAND (1 << 0)
+#define FL_SENT_STOP (1 << 1)
-#define FL_SENT_COMMAND (1 << 0)
-#define FL_SENT_STOP (1 << 1)
+#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \
+ | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \
+ | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)
+#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg))
+#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg))
/*
- * Read from a MCI register.
- */
-static inline unsigned long at91_mci_read(unsigned int reg)
-{
- void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI;
-
- return __raw_readl(mci_base + reg);
-}
-
-/*
- * Write to a MCI register.
- */
-static inline void at91_mci_write(unsigned int reg, unsigned long value)
-{
- void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI;
-
- __raw_writel(value, mci_base + reg);
-}
-
-/*
* Low level type for this driver
*/
struct at91mci_host
@@ -116,9 +101,14 @@ struct at91mci_host
struct mmc_command *cmd;
struct mmc_request *request;
+ void __iomem *baseaddr;
+ int irq;
+
struct at91_mmc_data *board;
int present;
+ struct clk *mci_clk;
+
/*
* Flag indicating when the command has been sent. This is used to
* work out whether or not to send the stop
@@ -158,7 +148,6 @@ static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
for (i = 0; i < len; i++) {
struct scatterlist *sg;
int amount;
- int index;
unsigned int *sgbuffer;
sg = &data->sg[i];
@@ -166,10 +155,15 @@ static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
amount = min(size, sg->length);
size -= amount;
- amount /= 4;
- for (index = 0; index < amount; index++)
- *dmabuf++ = swab32(sgbuffer[index]);
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
+ int index;
+
+ for (index = 0; index < (amount / 4); index++)
+ *dmabuf++ = swab32(sgbuffer[index]);
+ }
+ else
+ memcpy(dmabuf, sgbuffer, amount);
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
@@ -217,13 +211,13 @@ static void at91mci_pre_dma_read(struct at91mci_host *host)
/* Check to see if this needs filling */
if (i == 0) {
- if (at91_mci_read(AT91_PDC_RCR) != 0) {
+ if (at91_mci_read(host, AT91_PDC_RCR) != 0) {
pr_debug("Transfer active in current\n");
continue;
}
}
else {
- if (at91_mci_read(AT91_PDC_RNCR) != 0) {
+ if (at91_mci_read(host, AT91_PDC_RNCR) != 0) {
pr_debug("Transfer active in next\n");
continue;
}
@@ -240,12 +234,12 @@ static void at91mci_pre_dma_read(struct at91mci_host *host)
pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length);
if (i == 0) {
- at91_mci_write(AT91_PDC_RPR, sg->dma_address);
- at91_mci_write(AT91_PDC_RCR, sg->length / 4);
+ at91_mci_write(host, AT91_PDC_RPR, sg->dma_address);
+ at91_mci_write(host, AT91_PDC_RCR, sg->length / 4);
}
else {
- at91_mci_write(AT91_PDC_RNPR, sg->dma_address);
- at91_mci_write(AT91_PDC_RNCR, sg->length / 4);
+ at91_mci_write(host, AT91_PDC_RNPR, sg->dma_address);
+ at91_mci_write(host, AT91_PDC_RNCR, sg->length / 4);
}
}
@@ -276,8 +270,6 @@ static void at91mci_post_dma_read(struct at91mci_host *host)
while (host->in_use_index < host->transfer_index) {
unsigned int *buffer;
- int index;
- int len;
struct scatterlist *sg;
@@ -295,11 +287,13 @@ static void at91mci_post_dma_read(struct at91mci_host *host)
data->bytes_xfered += sg->length;
- len = sg->length / 4;
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
+ int index;
- for (index = 0; index < len; index++) {
- buffer[index] = swab32(buffer[index]);
+ for (index = 0; index < (sg->length / 4); index++)
+ buffer[index] = swab32(buffer[index]);
}
+
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
flush_dcache_page(sg->page);
}
@@ -308,8 +302,8 @@ static void at91mci_post_dma_read(struct at91mci_host *host)
if (host->transfer_index < data->sg_len)
at91mci_pre_dma_read(host);
else {
- at91_mci_write(AT91_MCI_IER, AT91_MCI_RXBUFF);
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
}
pr_debug("post dma read done\n");
@@ -326,11 +320,11 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
pr_debug("Handling the transmit\n");
/* Disable the transfer */
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
/* Now wait for cmd ready */
- at91_mci_write(AT91_MCI_IDR, AT91_MCI_TXBUFE);
- at91_mci_write(AT91_MCI_IER, AT91_MCI_NOTBUSY);
+ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
cmd = host->cmd;
if (!cmd) return;
@@ -344,21 +338,23 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
/*
* Enable the controller
*/
-static void at91_mci_enable(void)
+static void at91_mci_enable(struct at91mci_host *host)
{
- at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN);
- at91_mci_write(AT91_MCI_IDR, 0xFFFFFFFF);
- at91_mci_write(AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
- at91_mci_write(AT91_MCI_MR, 0x834A);
- at91_mci_write(AT91_MCI_SDCR, 0x0);
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+ at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+ at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
+
+ /* use Slot A or B (only one at same time) */
+ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
}
/*
* Disable the controller
*/
-static void at91_mci_disable(void)
+static void at91_mci_disable(struct at91mci_host *host)
{
- at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
}
/*
@@ -378,13 +374,13 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_
/* Not sure if this is needed */
#if 0
- if ((at91_mci_read(AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
+ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
pr_debug("Clearing timeout\n");
- at91_mci_write(AT91_MCI_ARGR, 0);
- at91_mci_write(AT91_MCI_CMDR, AT91_MCI_OPDCMD);
- while (!(at91_mci_read(AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
+ at91_mci_write(host, AT91_MCI_ARGR, 0);
+ at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD);
+ while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
/* spin */
- pr_debug("Clearing: SR = %08X\n", at91_mci_read(AT91_MCI_SR));
+ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
}
}
#endif
@@ -431,32 +427,32 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_
/*
* Set the arguments and send the command
*/
- pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08lX)\n",
- cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(AT91_MCI_MR));
+ pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n",
+ cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR));
if (!data) {
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS);
- at91_mci_write(AT91_PDC_RPR, 0);
- at91_mci_write(AT91_PDC_RCR, 0);
- at91_mci_write(AT91_PDC_RNPR, 0);
- at91_mci_write(AT91_PDC_RNCR, 0);
- at91_mci_write(AT91_PDC_TPR, 0);
- at91_mci_write(AT91_PDC_TCR, 0);
- at91_mci_write(AT91_PDC_TNPR, 0);
- at91_mci_write(AT91_PDC_TNCR, 0);
-
- at91_mci_write(AT91_MCI_ARGR, cmd->arg);
- at91_mci_write(AT91_MCI_CMDR, cmdr);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS);
+ at91_mci_write(host, AT91_PDC_RPR, 0);
+ at91_mci_write(host, AT91_PDC_RCR, 0);
+ at91_mci_write(host, AT91_PDC_RNPR, 0);
+ at91_mci_write(host, AT91_PDC_RNCR, 0);
+ at91_mci_write(host, AT91_PDC_TPR, 0);
+ at91_mci_write(host, AT91_PDC_TCR, 0);
+ at91_mci_write(host, AT91_PDC_TNPR, 0);
+ at91_mci_write(host, AT91_PDC_TNCR, 0);
+
+ at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+ at91_mci_write(host, AT91_MCI_CMDR, cmdr);
return AT91_MCI_CMDRDY;
}
- mr = at91_mci_read(AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */
- at91_mci_write(AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */
+ at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
/*
* Disable the PDC controller
*/
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
if (cmdr & AT91_MCI_TRCMD_START) {
data->bytes_xfered = 0;
@@ -485,8 +481,8 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_
pr_debug("Transmitting %d bytes\n", host->total_length);
- at91_mci_write(AT91_PDC_TPR, host->physical_address);
- at91_mci_write(AT91_PDC_TCR, host->total_length / 4);
+ at91_mci_write(host, AT91_PDC_TPR, host->physical_address);
+ at91_mci_write(host, AT91_PDC_TCR, host->total_length / 4);
ier = AT91_MCI_TXBUFE;
}
}
@@ -496,14 +492,14 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_
* the data sheet says
*/
- at91_mci_write(AT91_MCI_ARGR, cmd->arg);
- at91_mci_write(AT91_MCI_CMDR, cmdr);
+ at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+ at91_mci_write(host, AT91_MCI_CMDR, cmdr);
if (cmdr & AT91_MCI_TRCMD_START) {
if (cmdr & AT91_MCI_TRDIR)
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTEN);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTEN);
else
- at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTEN);
+ at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTEN);
}
return ier;
}
@@ -520,7 +516,7 @@ static void at91mci_process_command(struct at91mci_host *host, struct mmc_comman
pr_debug("setting ier to %08X\n", ier);
/* Stop on errors or the required value */
- at91_mci_write(AT91_MCI_IER, 0xffff0000 | ier);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
}
/*
@@ -548,19 +544,19 @@ static void at91mci_completed_command(struct at91mci_host *host)
struct mmc_command *cmd = host->cmd;
unsigned int status;
- at91_mci_write(AT91_MCI_IDR, 0xffffffff);
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
- cmd->resp[0] = at91_mci_read(AT91_MCI_RSPR(0));
- cmd->resp[1] = at91_mci_read(AT91_MCI_RSPR(1));
- cmd->resp[2] = at91_mci_read(AT91_MCI_RSPR(2));
- cmd->resp[3] = at91_mci_read(AT91_MCI_RSPR(3));
+ cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
+ cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
+ cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
+ cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
if (host->buffer) {
dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address);
host->buffer = NULL;
}
- status = at91_mci_read(AT91_MCI_SR);
+ status = at91_mci_read(host, AT91_MCI_SR);
pr_debug("Status = %08X [%08X %08X %08X %08X]\n",
status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
@@ -611,18 +607,18 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
int clkdiv;
struct at91mci_host *host = mmc_priv(mmc);
- unsigned long at91_master_clock = clk_get_rate(mci_clk);
+ unsigned long at91_master_clock = clk_get_rate(host->mci_clk);
host->bus_mode = ios->bus_mode;
if (ios->clock == 0) {
/* Disable the MCI controller */
- at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS);
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
clkdiv = 0;
}
else {
/* Enable the MCI controller */
- at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN);
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
if ((at91_master_clock % (ios->clock * 2)) == 0)
clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
@@ -634,25 +630,25 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) {
pr_debug("MMC: Setting controller bus width to 4\n");
- at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
}
else {
pr_debug("MMC: Setting controller bus width to 1\n");
- at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
}
/* Set the clock divider */
- at91_mci_write(AT91_MCI_MR, (at91_mci_read(AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
+ at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
/* maybe switch power to the card */
if (host->board->vcc_pin) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
- at91_set_gpio_output(host->board->vcc_pin, 0);
+ at91_set_gpio_value(host->board->vcc_pin, 0);
break;
case MMC_POWER_UP:
case MMC_POWER_ON:
- at91_set_gpio_output(host->board->vcc_pin, 1);
+ at91_set_gpio_value(host->board->vcc_pin, 1);
break;
}
}
@@ -665,39 +661,40 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
{
struct at91mci_host *host = devid;
int completed = 0;
+ unsigned int int_status, int_mask;
- unsigned int int_status;
+ int_status = at91_mci_read(host, AT91_MCI_SR);
+ int_mask = at91_mci_read(host, AT91_MCI_IMR);
+
+ pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask,
+ int_status & int_mask);
+
+ int_status = int_status & int_mask;
- int_status = at91_mci_read(AT91_MCI_SR);
- pr_debug("MCI irq: status = %08X, %08lX, %08lX\n", int_status, at91_mci_read(AT91_MCI_IMR),
- int_status & at91_mci_read(AT91_MCI_IMR));
-
- if ((int_status & at91_mci_read(AT91_MCI_IMR)) & 0xffff0000)
+ if (int_status & AT91_MCI_ERRORS) {
completed = 1;
+
+ if (int_status & AT91_MCI_UNRE)
+ pr_debug("MMC: Underrun error\n");
+ if (int_status & AT91_MCI_OVRE)
+ pr_debug("MMC: Overrun error\n");
+ if (int_status & AT91_MCI_DTOE)
+ pr_debug("MMC: Data timeout\n");
+ if (int_status & AT91_MCI_DCRCE)
+ pr_debug("MMC: CRC error in data\n");
+ if (int_status & AT91_MCI_RTOE)
+ pr_debug("MMC: Response timeout\n");
+ if (int_status & AT91_MCI_RENDE)
+ pr_debug("MMC: Response end bit error\n");
+ if (int_status & AT91_MCI_RCRCE)
+ pr_debug("MMC: Response CRC error\n");
+ if (int_status & AT91_MCI_RDIRE)
+ pr_debug("MMC: Response direction error\n");
+ if (int_status & AT91_MCI_RINDE)
+ pr_debug("MMC: Response index error\n");
+ } else {
+ /* Only continue processing if no errors */
- int_status &= at91_mci_read(AT91_MCI_IMR);
-
- if (int_status & AT91_MCI_UNRE)
- pr_debug("MMC: Underrun error\n");
- if (int_status & AT91_MCI_OVRE)
- pr_debug("MMC: Overrun error\n");
- if (int_status & AT91_MCI_DTOE)
- pr_debug("MMC: Data timeout\n");
- if (int_status & AT91_MCI_DCRCE)
- pr_debug("MMC: CRC error in data\n");
- if (int_status & AT91_MCI_RTOE)
- pr_debug("MMC: Response timeout\n");
- if (int_status & AT91_MCI_RENDE)
- pr_debug("MMC: Response end bit error\n");
- if (int_status & AT91_MCI_RCRCE)
- pr_debug("MMC: Response CRC error\n");
- if (int_status & AT91_MCI_RDIRE)
- pr_debug("MMC: Response direction error\n");
- if (int_status & AT91_MCI_RINDE)
- pr_debug("MMC: Response index error\n");
-
- /* Only continue processing if no errors */
- if (!completed) {
if (int_status & AT91_MCI_TXBUFE) {
pr_debug("TX buffer empty\n");
at91_mci_handle_transmitted(host);
@@ -705,12 +702,11 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
if (int_status & AT91_MCI_RXBUFF) {
pr_debug("RX buffer full\n");
- at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
}
- if (int_status & AT91_MCI_ENDTX) {
+ if (int_status & AT91_MCI_ENDTX)
pr_debug("Transmit has ended\n");
- }
if (int_status & AT91_MCI_ENDRX) {
pr_debug("Receive has ended\n");
@@ -719,37 +715,33 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
if (int_status & AT91_MCI_NOTBUSY) {
pr_debug("Card is ready\n");
- at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
}
- if (int_status & AT91_MCI_DTIP) {
+ if (int_status & AT91_MCI_DTIP)
pr_debug("Data transfer in progress\n");
- }
- if (int_status & AT91_MCI_BLKE) {
+ if (int_status & AT91_MCI_BLKE)
pr_debug("Block transfer has ended\n");
- }
- if (int_status & AT91_MCI_TXRDY) {
+ if (int_status & AT91_MCI_TXRDY)
pr_debug("Ready to transmit\n");
- }
- if (int_status & AT91_MCI_RXRDY) {
+ if (int_status & AT91_MCI_RXRDY)
pr_debug("Ready to receive\n");
- }
if (int_status & AT91_MCI_CMDRDY) {
pr_debug("Command ready\n");
completed = 1;
}
}
- at91_mci_write(AT91_MCI_IDR, int_status);
if (completed) {
pr_debug("Completed command\n");
- at91_mci_write(AT91_MCI_IDR, 0xffffffff);
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
at91mci_completed_command(host);
- }
+ } else
+ at91_mci_write(host, AT91_MCI_IDR, int_status);
return IRQ_HANDLED;
}
@@ -769,7 +761,7 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
present ? "insert" : "remove");
if (!present) {
pr_debug("****** Resetting SD-card bus width ******\n");
- at91_mci_write(AT91_MCI_SDCR, 0);
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
}
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
}
@@ -806,15 +798,22 @@ static int at91_mci_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct at91mci_host *host;
+ struct resource *res;
int ret;
pr_debug("Probe MCI devices\n");
- at91_mci_disable();
- at91_mci_enable();
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME))
+ return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
if (!mmc) {
pr_debug("Failed to allocate mmc host\n");
+ release_mem_region(res->start, res->end - res->start + 1);
return -ENOMEM;
}
@@ -833,30 +832,51 @@ static int at91_mci_probe(struct platform_device *pdev)
#ifdef SUPPORT_4WIRE
mmc->caps |= MMC_CAP_4_BIT_DATA;
#else
- printk("MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
+ printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
#endif
}
/*
* Get Clock
*/
- mci_clk = clk_get(&pdev->dev, "mci_clk");
- if (IS_ERR(mci_clk)) {
+ host->mci_clk = clk_get(&pdev->dev, "mci_clk");
+ if (IS_ERR(host->mci_clk)) {
printk(KERN_ERR "AT91 MMC: no clock defined.\n");
mmc_free_host(mmc);
+ release_mem_region(res->start, res->end - res->start + 1);
return -ENODEV;
}
- clk_enable(mci_clk); /* Enable the peripheral clock */
+
+ /*
+ * Map I/O region
+ */
+ host->baseaddr = ioremap(res->start, res->end - res->start + 1);
+ if (!host->baseaddr) {
+ clk_put(host->mci_clk);
+ mmc_free_host(mmc);
+ release_mem_region(res->start, res->end - res->start + 1);
+ return -ENOMEM;
+ }
+
+ /*
+ * Reset hardware
+ */
+ clk_enable(host->mci_clk); /* Enable the peripheral clock */
+ at91_mci_disable(host);
+ at91_mci_enable(host);
/*
* Allocate the MCI interrupt
*/
- ret = request_irq(AT91RM9200_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+ host->irq = platform_get_irq(pdev, 0);
+ ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
if (ret) {
- printk(KERN_ERR "Failed to request MCI interrupt\n");
- clk_disable(mci_clk);
- clk_put(mci_clk);
+ printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n");
+ clk_disable(host->mci_clk);
+ clk_put(host->mci_clk);
mmc_free_host(mmc);
+ iounmap(host->baseaddr);
+ release_mem_region(res->start, res->end - res->start + 1);
return ret;
}
@@ -879,10 +899,10 @@ static int at91_mci_probe(struct platform_device *pdev)
ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
0, DRIVER_NAME, host);
if (ret)
- printk(KERN_ERR "couldn't allocate MMC detect irq\n");
+ printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n");
}
- pr_debug(KERN_INFO "Added MCI driver\n");
+ pr_debug("Added MCI driver\n");
return 0;
}
@@ -894,6 +914,7 @@ static int at91_mci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct at91mci_host *host;
+ struct resource *res;
if (!mmc)
return -1;
@@ -905,16 +926,19 @@ static int at91_mci_remove(struct platform_device *pdev)
cancel_delayed_work(&host->mmc->detect);
}
+ at91_mci_disable(host);
mmc_remove_host(mmc);
- at91_mci_disable();
- free_irq(AT91RM9200_ID_MCI, host);
- mmc_free_host(mmc);
+ free_irq(host->irq, host);
- clk_disable(mci_clk); /* Disable the peripheral clock */
- clk_put(mci_clk);
+ clk_disable(host->mci_clk); /* Disable the peripheral clock */
+ clk_put(host->mci_clk);
- platform_set_drvdata(pdev, NULL);
+ iounmap(host->baseaddr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+ mmc_free_host(mmc);
+ platform_set_drvdata(pdev, NULL);
pr_debug("MCI Removed\n");
return 0;
diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c
index a17423a..3e35a43 100644
--- a/drivers/mmc/mmc_queue.c
+++ b/drivers/mmc/mmc_queue.c
@@ -78,8 +78,10 @@ static int mmc_queue_thread(void *d)
spin_unlock_irq(q->queue_lock);
if (!req) {
- if (kthread_should_stop())
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
break;
+ }
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index cd98117..c2d13d7 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1170,8 +1170,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
}
if (pci_resource_len(pdev, first_bar + slot) != 0x100) {
- printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. Aborting.\n");
- return -ENODEV;
+ printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. "
+ "You may experience problems.\n");
}
if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 5db7160..0a7e868 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -459,7 +459,7 @@ add_dataflash(struct spi_device *spi, char *name,
struct mtd_info *device;
struct flash_platform_data *pdata = spi->dev.platform_data;
- priv = (struct dataflash *) kzalloc(sizeof *priv, GFP_KERNEL);
+ priv = kzalloc(sizeof *priv, GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index fa4362f..0f3baa5 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -768,7 +768,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (mtd->type != MTD_NORFLASH)
return;
- part = kcalloc(1, sizeof(struct partition), GFP_KERNEL);
+ part = kzalloc(sizeof(struct partition), GFP_KERNEL);
if (!part)
return;
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 931028f..35ad5cf 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -2131,14 +2131,15 @@ static int rtl8139_poll(struct net_device *dev, int *budget)
}
if (done) {
+ unsigned long flags;
/*
* Order is important since data can get interrupted
* again when we think we are done.
*/
- local_irq_disable();
+ local_irq_save(flags);
RTL_W16_F(IntrMask, rtl8139_intr_mask);
__netif_rx_complete(dev);
- local_irq_enable();
+ local_irq_restore(flags);
}
spin_unlock(&tp->rx_lock);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 9de0eed..8aa8dd0 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2384,6 +2384,14 @@ config CHELSIO_T1_1G
Enables support for Chelsio's gigabit Ethernet PCI cards. If you
are using only 10G cards say 'N' here.
+config CHELSIO_T1_NAPI
+ bool "Use Rx Polling (NAPI)"
+ depends on CHELSIO_T1
+ default y
+ help
+ NAPI is a driver API designed to reduce CPU and interrupt load
+ when the driver is receiving lots of packets from the card.
+
config EHEA
tristate "eHEA Ethernet support"
depends on IBMEBUS
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c
index b98592a..f22e46d 100644
--- a/drivers/net/appletalk/ipddp.c
+++ b/drivers/net/appletalk/ipddp.c
@@ -186,7 +186,7 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
*/
static int ipddp_create(struct ipddp_route *new_rt)
{
- struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL);
+ struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL);
if (rt == NULL)
return -ENOMEM;
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index bae1de1..7845eaf 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -395,7 +395,7 @@ static void *bsd_alloc (unsigned char *options, int opt_len, int decomp)
* Allocate the main control structure for this instance.
*/
maxmaxcode = MAXCODE(bits);
- db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),
+ db = kmalloc(sizeof (struct bsd_db),
GFP_KERNEL);
if (!db)
{
diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c
index de48ead..fd5d821f 100644
--- a/drivers/net/chelsio/cxgb2.c
+++ b/drivers/net/chelsio/cxgb2.c
@@ -220,9 +220,8 @@ static int cxgb_up(struct adapter *adapter)
t1_interrupts_clear(adapter);
- adapter->params.has_msi = !disable_msi && pci_enable_msi(adapter->pdev) == 0;
- err = request_irq(adapter->pdev->irq,
- t1_select_intr_handler(adapter),
+ adapter->params.has_msi = !disable_msi && !pci_enable_msi(adapter->pdev);
+ err = request_irq(adapter->pdev->irq, t1_interrupt,
adapter->params.has_msi ? 0 : IRQF_SHARED,
adapter->name, adapter);
if (err) {
@@ -764,18 +763,7 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
struct adapter *adapter = dev->priv;
- /*
- * If RX coalescing is requested we use NAPI, otherwise interrupts.
- * This choice can be made only when all ports and the TOE are off.
- */
- if (adapter->open_device_map == 0)
- adapter->params.sge.polling = c->use_adaptive_rx_coalesce;
-
- if (adapter->params.sge.polling) {
- adapter->params.sge.rx_coalesce_usecs = 0;
- } else {
- adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
- }
+ adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce;
adapter->params.sge.sample_interval_usecs = c->rate_sample_interval;
t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge);
@@ -944,7 +932,7 @@ static void t1_netpoll(struct net_device *dev)
struct adapter *adapter = dev->priv;
local_irq_save(flags);
- t1_select_intr_handler(adapter)(adapter->pdev->irq, adapter);
+ t1_interrupt(adapter->pdev->irq, adapter);
local_irq_restore(flags);
}
#endif
@@ -1165,7 +1153,10 @@ static int __devinit init_one(struct pci_dev *pdev,
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = t1_netpoll;
#endif
+#ifdef CONFIG_CHELSIO_T1_NAPI
netdev->weight = 64;
+ netdev->poll = t1_poll;
+#endif
SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
}
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
index 0ca8d87..659cb22 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1413,16 +1413,20 @@ static int sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
if (unlikely(adapter->vlan_grp && p->vlan_valid)) {
st->vlan_xtract++;
- if (adapter->params.sge.polling)
+#ifdef CONFIG_CHELSIO_T1_NAPI
vlan_hwaccel_receive_skb(skb, adapter->vlan_grp,
ntohs(p->vlan));
- else
+#else
vlan_hwaccel_rx(skb, adapter->vlan_grp,
ntohs(p->vlan));
- } else if (adapter->params.sge.polling)
+#endif
+ } else {
+#ifdef CONFIG_CHELSIO_T1_NAPI
netif_receive_skb(skb);
- else
+#else
netif_rx(skb);
+#endif
+ }
return 0;
}
@@ -1572,6 +1576,7 @@ static int process_responses(struct adapter *adapter, int budget)
return budget;
}
+#ifdef CONFIG_CHELSIO_T1_NAPI
/*
* A simpler version of process_responses() that handles only pure (i.e.,
* non data-carrying) responses. Such respones are too light-weight to justify
@@ -1619,92 +1624,76 @@ static int process_pure_responses(struct adapter *adapter, struct respQ_e *e)
* or protection from interrupts as data interrupts are off at this point and
* other adapter interrupts do not interfere.
*/
-static int t1_poll(struct net_device *dev, int *budget)
+int t1_poll(struct net_device *dev, int *budget)
{
struct adapter *adapter = dev->priv;
int effective_budget = min(*budget, dev->quota);
-
int work_done = process_responses(adapter, effective_budget);
+
*budget -= work_done;
dev->quota -= work_done;
if (work_done >= effective_budget)
return 1;
+ spin_lock_irq(&adapter->async_lock);
__netif_rx_complete(dev);
-
- /*
- * Because we don't atomically flush the following write it is
- * possible that in very rare cases it can reach the device in a way
- * that races with a new response being written plus an error interrupt
- * causing the NAPI interrupt handler below to return unhandled status
- * to the OS. To protect against this would require flushing the write
- * and doing both the write and the flush with interrupts off. Way too
- * expensive and unjustifiable given the rarity of the race.
- */
writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING);
- return 0;
-}
+ writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
+ adapter->regs + A_PL_ENABLE);
+ spin_unlock_irq(&adapter->async_lock);
-/*
- * Returns true if the device is already scheduled for polling.
- */
-static inline int napi_is_scheduled(struct net_device *dev)
-{
- return test_bit(__LINK_STATE_RX_SCHED, &dev->state);
+ return 0;
}
/*
* NAPI version of the main interrupt handler.
*/
-static irqreturn_t t1_interrupt_napi(int irq, void *data)
+irqreturn_t t1_interrupt(int irq, void *data)
{
- int handled;
struct adapter *adapter = data;
+ struct net_device *dev = adapter->sge->netdev;
struct sge *sge = adapter->sge;
- struct respQ *q = &adapter->sge->respQ;
+ u32 cause;
+ int handled = 0;
- /*
- * Clear the SGE_DATA interrupt first thing. Normally the NAPI
- * handler has control of the response queue and the interrupt handler
- * can look at the queue reliably only once it knows NAPI is off.
- * We can't wait that long to clear the SGE_DATA interrupt because we
- * could race with t1_poll rearming the SGE interrupt, so we need to
- * clear the interrupt speculatively and really early on.
- */
- writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
+ cause = readl(adapter->regs + A_PL_CAUSE);
+ if (cause == 0 || cause == ~0)
+ return IRQ_NONE;
spin_lock(&adapter->async_lock);
- if (!napi_is_scheduled(sge->netdev)) {
+ if (cause & F_PL_INTR_SGE_DATA) {
+ struct respQ *q = &adapter->sge->respQ;
struct respQ_e *e = &q->entries[q->cidx];
- if (e->GenerationBit == q->genbit) {
- if (e->DataValid ||
- process_pure_responses(adapter, e)) {
- if (likely(__netif_rx_schedule_prep(sge->netdev)))
- __netif_rx_schedule(sge->netdev);
- else if (net_ratelimit())
- printk(KERN_INFO
- "NAPI schedule failure!\n");
- } else
- writel(q->cidx, adapter->regs + A_SG_SLEEPING);
-
- handled = 1;
- goto unlock;
- } else
- writel(q->cidx, adapter->regs + A_SG_SLEEPING);
- } else if (readl(adapter->regs + A_PL_CAUSE) & F_PL_INTR_SGE_DATA) {
- printk(KERN_ERR "data interrupt while NAPI running\n");
- }
-
- handled = t1_slow_intr_handler(adapter);
+ handled = 1;
+ writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
+
+ if (e->GenerationBit == q->genbit &&
+ __netif_rx_schedule_prep(dev)) {
+ if (e->DataValid || process_pure_responses(adapter, e)) {
+ /* mask off data IRQ */
+ writel(adapter->slow_intr_mask,
+ adapter->regs + A_PL_ENABLE);
+ __netif_rx_schedule(sge->netdev);
+ goto unlock;
+ }
+ /* no data, no NAPI needed */
+ netif_poll_enable(dev);
+
+ }
+ writel(q->cidx, adapter->regs + A_SG_SLEEPING);
+ } else
+ handled = t1_slow_intr_handler(adapter);
+
if (!handled)
sge->stats.unhandled_irqs++;
- unlock:
+unlock:
spin_unlock(&adapter->async_lock);
return IRQ_RETVAL(handled != 0);
}
+#else
/*
* Main interrupt handler, optimized assuming that we took a 'DATA'
* interrupt.
@@ -1720,7 +1709,7 @@ static irqreturn_t t1_interrupt_napi(int irq, void *data)
* 5. If we took an interrupt, but no valid respQ descriptors was found we
* let the slow_intr_handler run and do error handling.
*/
-static irqreturn_t t1_interrupt(int irq, void *cookie)
+irqreturn_t t1_interrupt(int irq, void *cookie)
{
int work_done;
struct respQ_e *e;
@@ -1752,11 +1741,7 @@ static irqreturn_t t1_interrupt(int irq, void *cookie)
spin_unlock(&adapter->async_lock);
return IRQ_RETVAL(work_done != 0);
}
-
-irq_handler_t t1_select_intr_handler(adapter_t *adapter)
-{
- return adapter->params.sge.polling ? t1_interrupt_napi : t1_interrupt;
-}
+#endif
/*
* Enqueues the sk_buff onto the cmdQ[qid] and has hardware fetch it.
@@ -2033,7 +2018,6 @@ static void sge_tx_reclaim_cb(unsigned long data)
*/
int t1_sge_set_coalesce_params(struct sge *sge, struct sge_params *p)
{
- sge->netdev->poll = t1_poll;
sge->fixed_intrtimer = p->rx_coalesce_usecs *
core_ticks_per_usec(sge->adapter);
writel(sge->fixed_intrtimer, sge->adapter->regs + A_SG_INTRTIMER);
@@ -2234,7 +2218,6 @@ struct sge * __devinit t1_sge_create(struct adapter *adapter,
p->coalesce_enable = 0;
p->sample_interval_usecs = 0;
- p->polling = 0;
return sge;
nomem_port:
diff --git a/drivers/net/chelsio/sge.h b/drivers/net/chelsio/sge.h
index 7ceb011..d132a0ef 100644
--- a/drivers/net/chelsio/sge.h
+++ b/drivers/net/chelsio/sge.h
@@ -76,7 +76,9 @@ struct sge *t1_sge_create(struct adapter *, struct sge_params *);
int t1_sge_configure(struct sge *, struct sge_params *);
int t1_sge_set_coalesce_params(struct sge *, struct sge_params *);
void t1_sge_destroy(struct sge *);
-irq_handler_t t1_select_intr_handler(adapter_t *adapter);
+irqreturn_t t1_interrupt(int irq, void *cookie);
+int t1_poll(struct net_device *, int *);
+
int t1_start_xmit(struct sk_buff *skb, struct net_device *dev);
void t1_set_vlan_accel(struct adapter *adapter, int on_off);
void t1_sge_start(struct sge *);
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index 03bf164..c2ae2a2 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1930,9 +1930,8 @@ static int e100_rx_alloc_list(struct nic *nic)
nic->rx_to_use = nic->rx_to_clean = NULL;
nic->ru_running = RU_UNINITIALIZED;
- if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))
+ if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC)))
return -ENOMEM;
- memset(nic->rxs, 0, sizeof(struct rx) * count);
for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
rx->next = (i + 1 < count) ? rx + 1 : nic->rxs;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 8a83db0..153b6dc 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -1177,7 +1177,7 @@ static void baycom_probe(struct net_device *dev)
dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */
dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */
memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
- memcpy(dev->dev_addr, &ax25_nocall, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, &null_ax25_address, AX25_ADDR_LEN);
dev->tx_queue_len = 16;
/* New style flags */
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 16620bd..11af0ae 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -1603,7 +1603,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
irda_qos_bits_to_value (&self->qos);
/* Allocate twice the size to guarantee alignment */
- self->ringbuf = (void *) kmalloc (OBOE_RING_LEN << 1, GFP_KERNEL);
+ self->ringbuf = kmalloc(OBOE_RING_LEN << 1, GFP_KERNEL);
if (!self->ringbuf)
{
printk (KERN_ERR DRIVER_NAME ": can't allocate DMA buffers\n");
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 6e95645..3ca1082 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -1747,7 +1747,7 @@ static int irda_usb_probe(struct usb_interface *intf,
/* Don't change this buffer size and allocation without doing
* some heavy and complete testing. Don't ask why :-(
* Jean II */
- self->speed_buff = (char *) kmalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL);
+ self->speed_buff = kmalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL);
if (self->speed_buff == NULL)
goto err_out_3;
diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c
index 654a68b..3098960 100644
--- a/drivers/net/irda/irport.c
+++ b/drivers/net/irda/irport.c
@@ -164,7 +164,7 @@ irport_open(int i, unsigned int iobase, unsigned int irq)
/* Allocate memory if needed */
if (self->tx_buff.truesize > 0) {
- self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize,
+ self->tx_buff.head = kmalloc(self->tx_buff.truesize,
GFP_KERNEL);
if (self->tx_buff.head == NULL) {
IRDA_ERROR("%s(), can't allocate memory for "
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
index b833016..177c502 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/lp486e.c
@@ -884,7 +884,7 @@ static int i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
dev->trans_start = jiffies;
- tx_cmd = (struct tx_cmd *) kmalloc ((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
+ tx_cmd = kmalloc((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
if (tx_cmd == NULL) {
printk(KERN_WARNING "%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
lp->stats.tx_dropped++;
@@ -1266,7 +1266,7 @@ static void set_multicast_list(struct net_device *dev) {
if (dev->mc_count > 0) {
struct dev_mc_list *dmi;
char *cp;
- cmd = (struct i596_cmd *)kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, GFP_ATOMIC);
+ cmd = kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, GFP_ATOMIC);
if (cmd == NULL) {
printk (KERN_ERR "%s: set_multicast Memory squeeze.\n", dev->name);
return;
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index bd0ce98..25b559b 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -264,12 +264,12 @@ static void macb_update_stats(struct macb *bp)
WARN_ON((unsigned long)(end - p - 1) != (MACB_TPF - MACB_PFR) / 4);
for(; p < end; p++, reg++)
- *p += readl(reg);
+ *p += __raw_readl(reg);
}
-static void macb_periodic_task(void *arg)
+static void macb_periodic_task(struct work_struct *work)
{
- struct macb *bp = arg;
+ struct macb *bp = container_of(work, struct macb, periodic_task.work);
macb_update_stats(bp);
macb_check_media(bp, 1, 0);
@@ -1088,7 +1088,7 @@ static int __devinit macb_probe(struct platform_device *pdev)
dev->base_addr = regs->start;
- INIT_WORK(&bp->periodic_task, macb_periodic_task, bp);
+ INIT_DELAYED_WORK(&bp->periodic_task, macb_periodic_task);
mutex_init(&bp->mdio_mutex);
init_completion(&bp->mdio_complete);
diff --git a/drivers/net/macb.h b/drivers/net/macb.h
index 8c253db..27bf0ae 100644
--- a/drivers/net/macb.h
+++ b/drivers/net/macb.h
@@ -250,9 +250,9 @@
/* Register access macros */
#define macb_readl(port,reg) \
- readl((port)->regs + MACB_##reg)
+ __raw_readl((port)->regs + MACB_##reg)
#define macb_writel(port,reg,value) \
- writel((value), (port)->regs + MACB_##reg)
+ __raw_writel((value), (port)->regs + MACB_##reg)
struct dma_desc {
u32 addr;
@@ -377,7 +377,7 @@ struct macb {
unsigned int rx_pending, tx_pending;
- struct work_struct periodic_task;
+ struct delayed_work periodic_task;
struct mutex mdio_mutex;
struct completion mdio_complete;
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index 81f127a..94ac168 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -71,7 +71,7 @@
#include "myri10ge_mcp.h"
#include "myri10ge_mcp_gen_header.h"
-#define MYRI10GE_VERSION_STR "1.0.0"
+#define MYRI10GE_VERSION_STR "1.1.0"
MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
MODULE_AUTHOR("Maintainer: help@myri.com");
@@ -92,8 +92,13 @@ MODULE_LICENSE("Dual BSD/GPL");
#define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff)
#define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff
+#define MYRI10GE_ALLOC_ORDER 0
+#define MYRI10GE_ALLOC_SIZE ((1 << MYRI10GE_ALLOC_ORDER) * PAGE_SIZE)
+#define MYRI10GE_MAX_FRAGS_PER_FRAME (MYRI10GE_MAX_ETHER_MTU/MYRI10GE_ALLOC_SIZE + 1)
+
struct myri10ge_rx_buffer_state {
- struct sk_buff *skb;
+ struct page *page;
+ int page_offset;
DECLARE_PCI_UNMAP_ADDR(bus)
DECLARE_PCI_UNMAP_LEN(len)
};
@@ -116,9 +121,14 @@ struct myri10ge_rx_buf {
u8 __iomem *wc_fifo; /* w/c rx dma addr fifo address */
struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */
struct myri10ge_rx_buffer_state *info;
+ struct page *page;
+ dma_addr_t bus;
+ int page_offset;
int cnt;
+ int fill_cnt;
int alloc_fail;
int mask; /* number of rx slots -1 */
+ int watchdog_needed;
};
struct myri10ge_tx_buf {
@@ -150,6 +160,7 @@ struct myri10ge_priv {
struct myri10ge_rx_buf rx_big;
struct myri10ge_rx_done rx_done;
int small_bytes;
+ int big_bytes;
struct net_device *dev;
struct net_device_stats stats;
u8 __iomem *sram;
@@ -238,11 +249,6 @@ module_param(myri10ge_force_firmware, int, S_IRUGO);
MODULE_PARM_DESC(myri10ge_force_firmware,
"Force firmware to assume aligned completions\n");
-static int myri10ge_skb_cross_4k = 0;
-module_param(myri10ge_skb_cross_4k, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(myri10ge_skb_cross_4k,
- "Can a small skb cross a 4KB boundary?\n");
-
static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
module_param(myri10ge_initial_mtu, int, S_IRUGO);
MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU\n");
@@ -266,6 +272,10 @@ static int myri10ge_debug = -1; /* defaults above */
module_param(myri10ge_debug, int, 0);
MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)");
+static int myri10ge_fill_thresh = 256;
+module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed\n");
+
#define MYRI10GE_FW_OFFSET 1024*1024
#define MYRI10GE_HIGHPART_TO_U32(X) \
(sizeof (X) == 8) ? ((u32)((u64)(X) >> 32)) : (0)
@@ -273,9 +283,9 @@ MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)");
#define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8)
-static inline void put_be32(__be32 val, __be32 __iomem *p)
+static inline void put_be32(__be32 val, __be32 __iomem * p)
{
- __raw_writel((__force __u32)val, (__force void __iomem *)p);
+ __raw_writel((__force __u32) val, (__force void __iomem *)p);
}
static int
@@ -804,194 +814,179 @@ myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
mb();
}
-/*
- * Set of routines to get a new receive buffer. Any buffer which
- * crosses a 4KB boundary must start on a 4KB boundary due to PCIe
- * wdma restrictions. We also try to align any smaller allocation to
- * at least a 16 byte boundary for efficiency. We assume the linux
- * memory allocator works by powers of 2, and will not return memory
- * smaller than 2KB which crosses a 4KB boundary. If it does, we fall
- * back to allocating 2x as much space as required.
- *
- * We intend to replace large (>4KB) skb allocations by using
- * pages directly and building a fraglist in the near future.
- */
-
-static inline struct sk_buff *myri10ge_alloc_big(struct net_device *dev,
- int bytes)
-{
- struct sk_buff *skb;
- unsigned long data, roundup;
-
- skb = netdev_alloc_skb(dev, bytes + 4096 + MXGEFW_PAD);
- if (skb == NULL)
- return NULL;
-
- /* Correct skb->truesize so that socket buffer
- * accounting is not confused the rounding we must
- * do to satisfy alignment constraints.
- */
- skb->truesize -= 4096;
-
- data = (unsigned long)(skb->data);
- roundup = (-data) & (4095);
- skb_reserve(skb, roundup);
- return skb;
-}
-
-/* Allocate 2x as much space as required and use whichever portion
- * does not cross a 4KB boundary */
-static inline struct sk_buff *myri10ge_alloc_small_safe(struct net_device *dev,
- unsigned int bytes)
+static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, __wsum hw_csum)
{
- struct sk_buff *skb;
- unsigned long data, boundary;
-
- skb = netdev_alloc_skb(dev, 2 * (bytes + MXGEFW_PAD) - 1);
- if (unlikely(skb == NULL))
- return NULL;
-
- /* Correct skb->truesize so that socket buffer
- * accounting is not confused the rounding we must
- * do to satisfy alignment constraints.
- */
- skb->truesize -= bytes + MXGEFW_PAD;
-
- data = (unsigned long)(skb->data);
- boundary = (data + 4095UL) & ~4095UL;
- if ((boundary - data) >= (bytes + MXGEFW_PAD))
- return skb;
+ struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data);
- skb_reserve(skb, boundary - data);
- return skb;
+ if ((skb->protocol == htons(ETH_P_8021Q)) &&
+ (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) ||
+ vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) {
+ skb->csum = hw_csum;
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
}
-/* Allocate just enough space, and verify that the allocated
- * space does not cross a 4KB boundary */
-static inline struct sk_buff *myri10ge_alloc_small(struct net_device *dev,
- int bytes)
+static inline void
+myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va,
+ struct skb_frag_struct *rx_frags, int len, int hlen)
{
- struct sk_buff *skb;
- unsigned long roundup, data, end;
-
- skb = netdev_alloc_skb(dev, bytes + 16 + MXGEFW_PAD);
- if (unlikely(skb == NULL))
- return NULL;
-
- /* Round allocated buffer to 16 byte boundary */
- data = (unsigned long)(skb->data);
- roundup = (-data) & 15UL;
- skb_reserve(skb, roundup);
- /* Verify that the data buffer does not cross a page boundary */
- data = (unsigned long)(skb->data);
- end = data + bytes + MXGEFW_PAD - 1;
- if (unlikely(((end >> 12) != (data >> 12)) && (data & 4095UL))) {
- printk(KERN_NOTICE
- "myri10ge_alloc_small: small skb crossed 4KB boundary\n");
- myri10ge_skb_cross_4k = 1;
- dev_kfree_skb_any(skb);
- skb = myri10ge_alloc_small_safe(dev, bytes);
- }
- return skb;
+ struct skb_frag_struct *skb_frags;
+
+ skb->len = skb->data_len = len;
+ skb->truesize = len + sizeof(struct sk_buff);
+ /* attach the page(s) */
+
+ skb_frags = skb_shinfo(skb)->frags;
+ while (len > 0) {
+ memcpy(skb_frags, rx_frags, sizeof(*skb_frags));
+ len -= rx_frags->size;
+ skb_frags++;
+ rx_frags++;
+ skb_shinfo(skb)->nr_frags++;
+ }
+
+ /* pskb_may_pull is not available in irq context, but
+ * skb_pull() (for ether_pad and eth_type_trans()) requires
+ * the beginning of the packet in skb_headlen(), move it
+ * manually */
+ memcpy(skb->data, va, hlen);
+ skb_shinfo(skb)->frags[0].page_offset += hlen;
+ skb_shinfo(skb)->frags[0].size -= hlen;
+ skb->data_len -= hlen;
+ skb->tail += hlen;
+ skb_pull(skb, MXGEFW_PAD);
}
-static inline int
-myri10ge_getbuf(struct myri10ge_rx_buf *rx, struct myri10ge_priv *mgp,
- int bytes, int idx)
+static void
+myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
+ int bytes, int watchdog)
{
- struct net_device *dev = mgp->dev;
- struct pci_dev *pdev = mgp->pdev;
- struct sk_buff *skb;
- dma_addr_t bus;
- int len, retval = 0;
+ struct page *page;
+ int idx;
- bytes += VLAN_HLEN; /* account for 802.1q vlan tag */
+ if (unlikely(rx->watchdog_needed && !watchdog))
+ return;
- if ((bytes + MXGEFW_PAD) > (4096 - 16) /* linux overhead */ )
- skb = myri10ge_alloc_big(dev, bytes);
- else if (myri10ge_skb_cross_4k)
- skb = myri10ge_alloc_small_safe(dev, bytes);
- else
- skb = myri10ge_alloc_small(dev, bytes);
+ /* try to refill entire ring */
+ while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) {
+ idx = rx->fill_cnt & rx->mask;
- if (unlikely(skb == NULL)) {
- rx->alloc_fail++;
- retval = -ENOBUFS;
- goto done;
- }
-
- /* set len so that it only covers the area we
- * need mapped for DMA */
- len = bytes + MXGEFW_PAD;
-
- bus = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE);
- rx->info[idx].skb = skb;
- pci_unmap_addr_set(&rx->info[idx], bus, bus);
- pci_unmap_len_set(&rx->info[idx], len, len);
- rx->shadow[idx].addr_low = htonl(MYRI10GE_LOWPART_TO_U32(bus));
- rx->shadow[idx].addr_high = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
-
-done:
- /* copy 8 descriptors (64-bytes) to the mcp at a time */
- if ((idx & 7) == 7) {
- if (rx->wc_fifo == NULL)
- myri10ge_submit_8rx(&rx->lanai[idx - 7],
- &rx->shadow[idx - 7]);
- else {
- mb();
- myri10ge_pio_copy(rx->wc_fifo,
- &rx->shadow[idx - 7], 64);
+ if ((bytes < MYRI10GE_ALLOC_SIZE / 2) &&
+ (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE)) {
+ /* we can use part of previous page */
+ get_page(rx->page);
+ } else {
+ /* we need a new page */
+ page =
+ alloc_pages(GFP_ATOMIC | __GFP_COMP,
+ MYRI10GE_ALLOC_ORDER);
+ if (unlikely(page == NULL)) {
+ if (rx->fill_cnt - rx->cnt < 16)
+ rx->watchdog_needed = 1;
+ return;
+ }
+ rx->page = page;
+ rx->page_offset = 0;
+ rx->bus = pci_map_page(mgp->pdev, page, 0,
+ MYRI10GE_ALLOC_SIZE,
+ PCI_DMA_FROMDEVICE);
+ }
+ rx->info[idx].page = rx->page;
+ rx->info[idx].page_offset = rx->page_offset;
+ /* note that this is the address of the start of the
+ * page */
+ pci_unmap_addr_set(&rx->info[idx], bus, rx->bus);
+ rx->shadow[idx].addr_low =
+ htonl(MYRI10GE_LOWPART_TO_U32(rx->bus) + rx->page_offset);
+ rx->shadow[idx].addr_high =
+ htonl(MYRI10GE_HIGHPART_TO_U32(rx->bus));
+
+ /* start next packet on a cacheline boundary */
+ rx->page_offset += SKB_DATA_ALIGN(bytes);
+ rx->fill_cnt++;
+
+ /* copy 8 descriptors to the firmware at a time */
+ if ((idx & 7) == 7) {
+ if (rx->wc_fifo == NULL)
+ myri10ge_submit_8rx(&rx->lanai[idx - 7],
+ &rx->shadow[idx - 7]);
+ else {
+ mb();
+ myri10ge_pio_copy(rx->wc_fifo,
+ &rx->shadow[idx - 7], 64);
+ }
}
}
- return retval;
}
-static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, __wsum hw_csum)
+static inline void
+myri10ge_unmap_rx_page(struct pci_dev *pdev,
+ struct myri10ge_rx_buffer_state *info, int bytes)
{
- struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data);
-
- if ((skb->protocol == htons(ETH_P_8021Q)) &&
- (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) ||
- vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) {
- skb->csum = hw_csum;
- skb->ip_summed = CHECKSUM_COMPLETE;
+ /* unmap the recvd page if we're the only or last user of it */
+ if (bytes >= MYRI10GE_ALLOC_SIZE / 2 ||
+ (info->page_offset + 2 * bytes) > MYRI10GE_ALLOC_SIZE) {
+ pci_unmap_page(pdev, (pci_unmap_addr(info, bus)
+ & ~(MYRI10GE_ALLOC_SIZE - 1)),
+ MYRI10GE_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
}
}
-static inline unsigned long
+#define MYRI10GE_HLEN 64 /* The number of bytes to copy from a
+ * page into an skb */
+
+static inline int
myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
int bytes, int len, __wsum csum)
{
- dma_addr_t bus;
struct sk_buff *skb;
- int idx, unmap_len;
+ struct skb_frag_struct rx_frags[MYRI10GE_MAX_FRAGS_PER_FRAME];
+ int i, idx, hlen, remainder;
+ struct pci_dev *pdev = mgp->pdev;
+ struct net_device *dev = mgp->dev;
+ u8 *va;
+ len += MXGEFW_PAD;
idx = rx->cnt & rx->mask;
- rx->cnt++;
+ va = page_address(rx->info[idx].page) + rx->info[idx].page_offset;
+ prefetch(va);
+ /* Fill skb_frag_struct(s) with data from our receive */
+ for (i = 0, remainder = len; remainder > 0; i++) {
+ myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
+ rx_frags[i].page = rx->info[idx].page;
+ rx_frags[i].page_offset = rx->info[idx].page_offset;
+ if (remainder < MYRI10GE_ALLOC_SIZE)
+ rx_frags[i].size = remainder;
+ else
+ rx_frags[i].size = MYRI10GE_ALLOC_SIZE;
+ rx->cnt++;
+ idx = rx->cnt & rx->mask;
+ remainder -= MYRI10GE_ALLOC_SIZE;
+ }
+
+ hlen = MYRI10GE_HLEN > len ? len : MYRI10GE_HLEN;
- /* save a pointer to the received skb */
- skb = rx->info[idx].skb;
- bus = pci_unmap_addr(&rx->info[idx], bus);
- unmap_len = pci_unmap_len(&rx->info[idx], len);
+ /* allocate an skb to attach the page(s) to. */
- /* try to replace the received skb */
- if (myri10ge_getbuf(rx, mgp, bytes, idx)) {
- /* drop the frame -- the old skbuf is re-cycled */
- mgp->stats.rx_dropped += 1;
+ skb = netdev_alloc_skb(dev, MYRI10GE_HLEN + 16);
+ if (unlikely(skb == NULL)) {
+ mgp->stats.rx_dropped++;
+ do {
+ i--;
+ put_page(rx_frags[i].page);
+ } while (i != 0);
return 0;
}
- /* unmap the recvd skb */
- pci_unmap_single(mgp->pdev, bus, unmap_len, PCI_DMA_FROMDEVICE);
-
- /* mcp implicitly skips 1st bytes so that packet is properly
- * aligned */
- skb_reserve(skb, MXGEFW_PAD);
-
- /* set the length of the frame */
- skb_put(skb, len);
+ /* Attach the pages to the skb, and trim off any padding */
+ myri10ge_rx_skb_build(skb, va, rx_frags, len, hlen);
+ if (skb_shinfo(skb)->frags[0].size <= 0) {
+ put_page(skb_shinfo(skb)->frags[0].page);
+ skb_shinfo(skb)->nr_frags = 0;
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->dev = dev;
- skb->protocol = eth_type_trans(skb, mgp->dev);
if (mgp->csum_flag) {
if ((skb->protocol == htons(ETH_P_IP)) ||
(skb->protocol == htons(ETH_P_IPV6))) {
@@ -1000,9 +995,8 @@ myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
} else
myri10ge_vlan_ip_csum(skb, csum);
}
-
netif_receive_skb(skb);
- mgp->dev->last_rx = jiffies;
+ dev->last_rx = jiffies;
return 1;
}
@@ -1079,7 +1073,7 @@ static inline void myri10ge_clean_rx_done(struct myri10ge_priv *mgp, int *limit)
length, checksum);
else
rx_ok = myri10ge_rx_done(mgp, &mgp->rx_big,
- mgp->dev->mtu + ETH_HLEN,
+ mgp->big_bytes,
length, checksum);
rx_packets += rx_ok;
rx_bytes += rx_ok * (unsigned long)length;
@@ -1094,6 +1088,14 @@ static inline void myri10ge_clean_rx_done(struct myri10ge_priv *mgp, int *limit)
rx_done->cnt = cnt;
mgp->stats.rx_packets += rx_packets;
mgp->stats.rx_bytes += rx_bytes;
+
+ /* restock receive rings if needed */
+ if (mgp->rx_small.fill_cnt - mgp->rx_small.cnt < myri10ge_fill_thresh)
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_small,
+ mgp->small_bytes + MXGEFW_PAD, 0);
+ if (mgp->rx_big.fill_cnt - mgp->rx_big.cnt < myri10ge_fill_thresh)
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_big, mgp->big_bytes, 0);
+
}
static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
@@ -1484,56 +1486,48 @@ static int myri10ge_allocate_rings(struct net_device *dev)
goto abort_with_rx_small_info;
/* Fill the receive rings */
+ mgp->rx_big.cnt = 0;
+ mgp->rx_small.cnt = 0;
+ mgp->rx_big.fill_cnt = 0;
+ mgp->rx_small.fill_cnt = 0;
+ mgp->rx_small.page_offset = MYRI10GE_ALLOC_SIZE;
+ mgp->rx_big.page_offset = MYRI10GE_ALLOC_SIZE;
+ mgp->rx_small.watchdog_needed = 0;
+ mgp->rx_big.watchdog_needed = 0;
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_small,
+ mgp->small_bytes + MXGEFW_PAD, 0);
- for (i = 0; i <= mgp->rx_small.mask; i++) {
- status = myri10ge_getbuf(&mgp->rx_small, mgp,
- mgp->small_bytes, i);
- if (status) {
- printk(KERN_ERR
- "myri10ge: %s: alloced only %d small bufs\n",
- dev->name, i);
- goto abort_with_rx_small_ring;
- }
+ if (mgp->rx_small.fill_cnt < mgp->rx_small.mask + 1) {
+ printk(KERN_ERR "myri10ge: %s: alloced only %d small bufs\n",
+ dev->name, mgp->rx_small.fill_cnt);
+ goto abort_with_rx_small_ring;
}
- for (i = 0; i <= mgp->rx_big.mask; i++) {
- status =
- myri10ge_getbuf(&mgp->rx_big, mgp, dev->mtu + ETH_HLEN, i);
- if (status) {
- printk(KERN_ERR
- "myri10ge: %s: alloced only %d big bufs\n",
- dev->name, i);
- goto abort_with_rx_big_ring;
- }
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_big, mgp->big_bytes, 0);
+ if (mgp->rx_big.fill_cnt < mgp->rx_big.mask + 1) {
+ printk(KERN_ERR "myri10ge: %s: alloced only %d big bufs\n",
+ dev->name, mgp->rx_big.fill_cnt);
+ goto abort_with_rx_big_ring;
}
return 0;
abort_with_rx_big_ring:
- for (i = 0; i <= mgp->rx_big.mask; i++) {
- if (mgp->rx_big.info[i].skb != NULL)
- dev_kfree_skb_any(mgp->rx_big.info[i].skb);
- if (pci_unmap_len(&mgp->rx_big.info[i], len))
- pci_unmap_single(mgp->pdev,
- pci_unmap_addr(&mgp->rx_big.info[i],
- bus),
- pci_unmap_len(&mgp->rx_big.info[i],
- len),
- PCI_DMA_FROMDEVICE);
+ for (i = mgp->rx_big.cnt; i < mgp->rx_big.fill_cnt; i++) {
+ int idx = i & mgp->rx_big.mask;
+ myri10ge_unmap_rx_page(mgp->pdev, &mgp->rx_big.info[idx],
+ mgp->big_bytes);
+ put_page(mgp->rx_big.info[idx].page);
}
abort_with_rx_small_ring:
- for (i = 0; i <= mgp->rx_small.mask; i++) {
- if (mgp->rx_small.info[i].skb != NULL)
- dev_kfree_skb_any(mgp->rx_small.info[i].skb);
- if (pci_unmap_len(&mgp->rx_small.info[i], len))
- pci_unmap_single(mgp->pdev,
- pci_unmap_addr(&mgp->rx_small.info[i],
- bus),
- pci_unmap_len(&mgp->rx_small.info[i],
- len),
- PCI_DMA_FROMDEVICE);
+ for (i = mgp->rx_small.cnt; i < mgp->rx_small.fill_cnt; i++) {
+ int idx = i & mgp->rx_small.mask;
+ myri10ge_unmap_rx_page(mgp->pdev, &mgp->rx_small.info[idx],
+ mgp->small_bytes + MXGEFW_PAD);
+ put_page(mgp->rx_small.info[idx].page);
}
+
kfree(mgp->rx_big.info);
abort_with_rx_small_info:
@@ -1566,30 +1560,24 @@ static void myri10ge_free_rings(struct net_device *dev)
mgp = netdev_priv(dev);
- for (i = 0; i <= mgp->rx_big.mask; i++) {
- if (mgp->rx_big.info[i].skb != NULL)
- dev_kfree_skb_any(mgp->rx_big.info[i].skb);
- if (pci_unmap_len(&mgp->rx_big.info[i], len))
- pci_unmap_single(mgp->pdev,
- pci_unmap_addr(&mgp->rx_big.info[i],
- bus),
- pci_unmap_len(&mgp->rx_big.info[i],
- len),
- PCI_DMA_FROMDEVICE);
- }
-
- for (i = 0; i <= mgp->rx_small.mask; i++) {
- if (mgp->rx_small.info[i].skb != NULL)
- dev_kfree_skb_any(mgp->rx_small.info[i].skb);
- if (pci_unmap_len(&mgp->rx_small.info[i], len))
- pci_unmap_single(mgp->pdev,
- pci_unmap_addr(&mgp->rx_small.info[i],
- bus),
- pci_unmap_len(&mgp->rx_small.info[i],
- len),
- PCI_DMA_FROMDEVICE);
+ for (i = mgp->rx_big.cnt; i < mgp->rx_big.fill_cnt; i++) {
+ idx = i & mgp->rx_big.mask;
+ if (i == mgp->rx_big.fill_cnt - 1)
+ mgp->rx_big.info[idx].page_offset = MYRI10GE_ALLOC_SIZE;
+ myri10ge_unmap_rx_page(mgp->pdev, &mgp->rx_big.info[idx],
+ mgp->big_bytes);
+ put_page(mgp->rx_big.info[idx].page);
}
+ for (i = mgp->rx_small.cnt; i < mgp->rx_small.fill_cnt; i++) {
+ idx = i & mgp->rx_small.mask;
+ if (i == mgp->rx_small.fill_cnt - 1)
+ mgp->rx_small.info[idx].page_offset =
+ MYRI10GE_ALLOC_SIZE;
+ myri10ge_unmap_rx_page(mgp->pdev, &mgp->rx_small.info[idx],
+ mgp->small_bytes + MXGEFW_PAD);
+ put_page(mgp->rx_small.info[idx].page);
+ }
tx = &mgp->tx;
while (tx->done != tx->req) {
idx = tx->done & tx->mask;
@@ -1657,19 +1645,18 @@ static int myri10ge_open(struct net_device *dev)
*/
if (dev->mtu <= ETH_DATA_LEN)
- mgp->small_bytes = 128; /* enough for a TCP header */
+ /* enough for a TCP header */
+ mgp->small_bytes = (128 > SMP_CACHE_BYTES)
+ ? (128 - MXGEFW_PAD)
+ : (SMP_CACHE_BYTES - MXGEFW_PAD);
else
- mgp->small_bytes = ETH_FRAME_LEN; /* enough for an ETH_DATA_LEN frame */
+ /* enough for a vlan encapsulated ETH_DATA_LEN frame */
+ mgp->small_bytes = VLAN_ETH_FRAME_LEN;
/* Override the small buffer size? */
if (myri10ge_small_bytes > 0)
mgp->small_bytes = myri10ge_small_bytes;
- /* If the user sets an obscenely small MTU, adjust the small
- * bytes down to nearly nothing */
- if (mgp->small_bytes >= (dev->mtu + ETH_HLEN))
- mgp->small_bytes = 64;
-
/* get the lanai pointers to the send and receive rings */
status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET, &cmd, 0);
@@ -1705,17 +1692,23 @@ static int myri10ge_open(struct net_device *dev)
mgp->rx_big.wc_fifo = NULL;
}
- status = myri10ge_allocate_rings(dev);
- if (status != 0)
- goto abort_with_nothing;
-
/* Firmware needs the big buff size as a power of 2. Lie and
* tell him the buffer is larger, because we only use 1
* buffer/pkt, and the mtu will prevent overruns.
*/
- big_pow2 = dev->mtu + ETH_HLEN + MXGEFW_PAD;
- while ((big_pow2 & (big_pow2 - 1)) != 0)
- big_pow2++;
+ big_pow2 = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
+ if (big_pow2 < MYRI10GE_ALLOC_SIZE / 2) {
+ while ((big_pow2 & (big_pow2 - 1)) != 0)
+ big_pow2++;
+ mgp->big_bytes = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
+ } else {
+ big_pow2 = MYRI10GE_ALLOC_SIZE;
+ mgp->big_bytes = big_pow2;
+ }
+
+ status = myri10ge_allocate_rings(dev);
+ if (status != 0)
+ goto abort_with_nothing;
/* now give firmware buffers sizes, and MTU */
cmd.data0 = dev->mtu + ETH_HLEN + VLAN_HLEN;
@@ -2206,7 +2199,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev)
struct myri10ge_cmd cmd;
struct myri10ge_priv *mgp;
struct dev_mc_list *mc_list;
- __be32 data[2] = {0, 0};
+ __be32 data[2] = { 0, 0 };
int err;
mgp = netdev_priv(dev);
@@ -2625,7 +2618,7 @@ static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
static void myri10ge_watchdog(struct work_struct *work)
{
struct myri10ge_priv *mgp =
- container_of(work, struct myri10ge_priv, watchdog_work);
+ container_of(work, struct myri10ge_priv, watchdog_work);
u32 reboot;
int status;
u16 cmd, vendor;
@@ -2698,6 +2691,21 @@ static void myri10ge_watchdog_timer(unsigned long arg)
struct myri10ge_priv *mgp;
mgp = (struct myri10ge_priv *)arg;
+
+ if (mgp->rx_small.watchdog_needed) {
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_small,
+ mgp->small_bytes + MXGEFW_PAD, 1);
+ if (mgp->rx_small.fill_cnt - mgp->rx_small.cnt >=
+ myri10ge_fill_thresh)
+ mgp->rx_small.watchdog_needed = 0;
+ }
+ if (mgp->rx_big.watchdog_needed) {
+ myri10ge_alloc_rx_pages(mgp, &mgp->rx_big, mgp->big_bytes, 1);
+ if (mgp->rx_big.fill_cnt - mgp->rx_big.cnt >=
+ myri10ge_fill_thresh)
+ mgp->rx_big.watchdog_needed = 0;
+ }
+
if (mgp->tx.req != mgp->tx.done &&
mgp->tx.done == mgp->watchdog_tx_done &&
mgp->watchdog_tx_req != mgp->watchdog_tx_done)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index b01fc70..a4d7529 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -50,7 +50,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
struct phy_device *dev;
/* We allocate the device, and initialize the
* default values */
- dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (NULL == dev)
return (struct phy_device*) PTR_ERR((void*)-ENOMEM);
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index f54c552..72c8d66 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -121,7 +121,7 @@ static void *z_comp_alloc(unsigned char *options, int opt_len)
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
return NULL;
- state = (struct ppp_deflate_state *) kmalloc(sizeof(*state),
+ state = kmalloc(sizeof(*state),
GFP_KERNEL);
if (state == NULL)
return NULL;
@@ -341,7 +341,7 @@ static void *z_decomp_alloc(unsigned char *options, int opt_len)
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
return NULL;
- state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL);
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c
index f3655fd..d5bdd25 100644
--- a/drivers/net/ppp_mppe.c
+++ b/drivers/net/ppp_mppe.c
@@ -200,7 +200,7 @@ static void *mppe_alloc(unsigned char *options, int optlen)
|| options[0] != CI_MPPE || options[1] != CILEN_MPPE)
goto out;
- state = (struct ppp_mppe_state *) kmalloc(sizeof(*state), GFP_KERNEL);
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
goto out;
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index b60f045..8a39376 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -749,7 +749,7 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base)
struct skge_element *e;
int i;
- ring->start = kcalloc(sizeof(*e), ring->count, GFP_KERNEL);
+ ring->start = kcalloc(ring->count, sizeof(*e), GFP_KERNEL);
if (!ring->start)
return -ENOMEM;
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 39c2152..a0806d2 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -229,10 +229,10 @@ static int sl_realloc_bufs(struct slip *sl, int mtu)
if (len < 576 * 2)
len = 576 * 2;
- xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
- rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+ xbuff = kmalloc(len + 4, GFP_ATOMIC);
+ rbuff = kmalloc(len + 4, GFP_ATOMIC);
#ifdef SL_INCLUDE_CSLIP
- cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+ cbuff = kmalloc(len + 4, GFP_ATOMIC);
#endif
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 9367c57..d2767e6 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -362,96 +362,6 @@ static inline void LPD7_SMC_outsw (unsigned char* a, int r,
#define SMC_IRQ_FLAGS (0)
-#elif defined(CONFIG_ARCH_VERSATILE)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
-#elif defined(CONFIG_ARCH_VERSATILE)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
-#elif defined(CONFIG_ARCH_VERSATILE)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
-#elif defined(CONFIG_ARCH_VERSATILE)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
-#elif defined(CONFIG_ARCH_VERSATILE)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
#else
#define SMC_CAN_USE_8BIT 1
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 1f05511..8243150 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -194,9 +194,9 @@ static void enqueue(struct list_head *node, struct list_head *lh)
{
unsigned long flags;
- spin_lock_irqsave(ugeth_lock, flags);
+ spin_lock_irqsave(&ugeth_lock, flags);
list_add_tail(node, lh);
- spin_unlock_irqrestore(ugeth_lock, flags);
+ spin_unlock_irqrestore(&ugeth_lock, flags);
}
#endif /* CONFIG_UGETH_FILTERING */
@@ -204,14 +204,14 @@ static struct list_head *dequeue(struct list_head *lh)
{
unsigned long flags;
- spin_lock_irqsave(ugeth_lock, flags);
+ spin_lock_irqsave(&ugeth_lock, flags);
if (!list_empty(lh)) {
struct list_head *node = lh->next;
list_del(node);
- spin_unlock_irqrestore(ugeth_lock, flags);
+ spin_unlock_irqrestore(&ugeth_lock, flags);
return node;
} else {
- spin_unlock_irqrestore(ugeth_lock, flags);
+ spin_unlock_irqrestore(&ugeth_lock, flags);
return NULL;
}
}
@@ -1852,6 +1852,8 @@ static int init_phy(struct net_device *dev)
mii_info->mdio_read = &read_phy_reg;
mii_info->mdio_write = &write_phy_reg;
+ spin_lock_init(&mii_info->mdio_lock);
+
ugeth->mii_info = mii_info;
spin_lock_irq(&ugeth->lock);
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index d5ab9cf..21f76f5 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -382,7 +382,7 @@ config SDLA
# Wan router core.
config WAN_ROUTER_DRIVERS
- bool "WAN router drivers"
+ tristate "WAN router drivers"
depends on WAN && WAN_ROUTER
---help---
Connect LAN to WAN via Linux box.
@@ -393,7 +393,8 @@ config WAN_ROUTER_DRIVERS
<file:Documentation/networking/wan-router.txt>.
Note that the answer to this question won't directly affect the
- kernel: saying N will just cause the configurator to skip all
+ kernel except for how subordinate drivers may be built:
+ saying N will just cause the configurator to skip all
the questions about WAN router drivers.
If unsure, say N.
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index a4f7357..a02c5fb 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -231,7 +231,7 @@ static struct sv11_device *sv11_init(int iobase, int irq)
return NULL;
}
- sv=(struct sv11_device *)kmalloc(sizeof(struct sv11_device), GFP_KERNEL);
+ sv = kmalloc(sizeof(struct sv11_device), GFP_KERNEL);
if(!sv)
goto fail3;
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 36d1c3f..62184de 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -3455,7 +3455,7 @@ cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if ((err = pci_enable_device(pdev)) < 0)
return err;
- card = (pc300_t *) kmalloc(sizeof(pc300_t), GFP_KERNEL);
+ card = kmalloc(sizeof(pc300_t), GFP_KERNEL);
if (card == NULL) {
printk("PC300 found at RAM 0x%016llx, "
"but could not allocate card structure.\n",
diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
index b2a23ae..5873c34 100644
--- a/drivers/net/wan/pc300_tty.c
+++ b/drivers/net/wan/pc300_tty.c
@@ -784,7 +784,7 @@ void cpc_tty_receive(pc300dev_t *pc300dev)
continue;
}
- new = (st_cpc_rx_buf *)kmalloc(rx_len + sizeof(st_cpc_rx_buf), GFP_ATOMIC);
+ new = kmalloc(rx_len + sizeof(st_cpc_rx_buf), GFP_ATOMIC);
if (new == 0) {
cpc_tty_rx_disc_frame(pc300chan);
continue;
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 9c3ccc6..1c9edd9 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -123,8 +123,8 @@ static int x25_asy_change_mtu(struct net_device *dev, int newmtu)
unsigned char *xbuff, *rbuff;
int len = 2* newmtu;
- xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
- rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+ xbuff = kmalloc(len + 4, GFP_ATOMIC);
+ rbuff = kmalloc(len + 4, GFP_ATOMIC);
if (xbuff == NULL || rbuff == NULL)
{
@@ -465,11 +465,11 @@ static int x25_asy_open(struct net_device *dev)
len = dev->mtu * 2;
- sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ sl->rbuff = kmalloc(len + 4, GFP_KERNEL);
if (sl->rbuff == NULL) {
goto norbuff;
}
- sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ sl->xbuff = kmalloc(len + 4, GFP_KERNEL);
if (sl->xbuff == NULL) {
goto noxbuff;
}
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index 974a8e5..efb8cf3 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -1253,7 +1253,7 @@ static char * ap_auth_make_challenge(struct ap_data *ap)
return NULL;
}
- tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
+ tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
if (tmpbuf == NULL) {
PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
return NULL;
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c
index 24fc387..c7678e6 100644
--- a/drivers/net/wireless/hostap/hostap_download.c
+++ b/drivers/net/wireless/hostap/hostap_download.c
@@ -201,7 +201,7 @@ static u8 * prism2_read_pda(struct net_device *dev)
0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
};
- buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
+ buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
if (buf == NULL)
return NULL;
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index a394a23..3079378 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -2252,7 +2252,7 @@ static int hostap_tx_compl_read(local_info_t *local, int error,
if (txdesc->sw_support) {
len = le16_to_cpu(txdesc->data_len);
if (len < PRISM2_DATA_MAXLEN) {
- *payload = (char *) kmalloc(len, GFP_ATOMIC);
+ *payload = kmalloc(len, GFP_ATOMIC);
if (*payload == NULL ||
hfa384x_from_bap(dev, BAP0, *payload, len)) {
PDEBUG(DEBUG_EXTRA, "%s: could not read TX "
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index 3b7b806..cb08bc5 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -3829,7 +3829,7 @@ static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p)
p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer)
return -EINVAL;
- param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL);
+ param = kmalloc(p->length, GFP_KERNEL);
if (param == NULL)
return -ENOMEM;
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index 0796be9..04c19ce 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -250,7 +250,7 @@ u16 hostap_tx_callback_register(local_info_t *local,
unsigned long flags;
struct hostap_tx_callback_info *entry;
- entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry),
+ entry = kmalloc(sizeof(*entry),
GFP_ATOMIC);
if (entry == NULL)
return 0;
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index dd9ba4a..0e94fbb 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -2246,7 +2246,7 @@ static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
if (priv->snapshot[0])
return 1;
for (i = 0; i < 0x30; i++) {
- priv->snapshot[i] = (u8 *) kmalloc(0x1000, GFP_ATOMIC);
+ priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
if (!priv->snapshot[i]) {
IPW_DEBUG_INFO("%s: Error allocating snapshot "
"buffer %d\n", priv->net_dev->name, i);
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index 96606ed..838d510 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -2775,7 +2775,7 @@ prism54_hostapd(struct net_device *ndev, struct iw_point *p)
p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer)
return -EINVAL;
- param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL);
+ param = kmalloc(p->length, GFP_KERNEL);
if (param == NULL)
return -ENOMEM;
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index 233d906c..5eb8163 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -603,7 +603,7 @@ static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char se
if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
return NULL;
- new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
+ new_wavepoint = kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
if(new_wavepoint==NULL)
return NULL;
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 77e11dd..78ea72f 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -101,7 +101,7 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr
/* Allocate a single memory block for values and addresses. */
count16 = 2*count;
- a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
+ a16 = kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
GFP_NOFS);
if (!a16) {
dev_dbg_f(zd_chip_dev(chip),
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 12bab64..6fb3f79 100644
--- a/drivers/parisc/iosapic.c
+++ b/drivers/parisc/iosapic.c
@@ -874,7 +874,7 @@ void *iosapic_register(unsigned long hpa)
return NULL;
}
- isi = (struct iosapic_info *)kzalloc(sizeof(struct iosapic_info), GFP_KERNEL);
+ isi = kzalloc(sizeof(struct iosapic_info), GFP_KERNEL);
if (!isi) {
BUG();
return NULL;
diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig
index c7fa28a..36c6a1b 100644
--- a/drivers/parport/Kconfig
+++ b/drivers/parport/Kconfig
@@ -82,9 +82,6 @@ config PARPORT_PC_PCMCIA
Say Y here if you need PCMCIA support for your PC-style parallel
ports. If unsure, say N.
-config PARPORT_NOT_PC
- bool
-
config PARPORT_IP32
tristate "SGI IP32 builtin port (EXPERIMENTAL)"
depends on SGI_IP32 && PARPORT && EXPERIMENTAL
@@ -158,5 +155,8 @@ config PARPORT_1284
transfer modes. Also say Y if you want device ID information to
appear in /proc/sys/dev/parport/*/autoprobe*. It is safe to say N.
+config PARPORT_NOT_PC
+ bool
+
endmenu
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 6e780db..adce420 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -76,7 +76,8 @@ config HOTPLUG_PCI_IBM
config HOTPLUG_PCI_ACPI
tristate "ACPI PCI Hotplug driver"
- depends on (!ACPI_DOCK && ACPI && HOTPLUG_PCI) || (ACPI_DOCK && HOTPLUG_PCI)
+ depends on HOTPLUG_PCI
+ depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK)
help
Say Y here if you have a system that supports PCI Hotplug using
ACPI.
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
index 298a6cf..ae5e974 100644
--- a/drivers/pci/hotplug/cpqphp_nvram.c
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -520,7 +520,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
return 2;
while (nummem--) {
- mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
break;
@@ -548,7 +548,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
}
while (numpmem--) {
- p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
break;
@@ -576,7 +576,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
}
while (numio--) {
- io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ io_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
break;
@@ -604,7 +604,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
}
while (numbus--) {
- bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
break;
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 6d3f580..25d3aad 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -1320,7 +1320,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
DBG_ENTER_ROUTINE
spin_lock_init(&list_lock);
- php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
+ php_ctlr = kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
if (!php_ctlr) { /* allocate controller state data */
err("%s: HPC controller memory allocation error!\n", __FUNCTION__);
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 55866b6..6f5fabb 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -148,7 +148,7 @@ static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
{
struct aer_rpc *rpc;
- if (!(rpc = (struct aer_rpc *)kmalloc(sizeof(struct aer_rpc),
+ if (!(rpc = kmalloc(sizeof(struct aer_rpc),
GFP_KERNEL)))
return NULL;
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
index 52d4a38..3334f22 100644
--- a/drivers/pcmcia/at91_cf.c
+++ b/drivers/pcmcia/at91_cf.c
@@ -230,7 +230,7 @@ static int __init at91_cf_probe(struct platform_device *pdev)
if (!io)
return -ENODEV;
- cf = kcalloc(1, sizeof *cf, GFP_KERNEL);
+ cf = kzalloc(sizeof *cf, GFP_KERNEL);
if (!cf)
return -ENOMEM;
diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c
index 06bf7f4..e65a6b8 100644
--- a/drivers/pcmcia/omap_cf.c
+++ b/drivers/pcmcia/omap_cf.c
@@ -220,7 +220,7 @@ static int __devinit omap_cf_probe(struct device *dev)
if (irq < 0)
return -EINVAL;
- cf = kcalloc(1, sizeof *cf, GFP_KERNEL);
+ cf = kzalloc(sizeof *cf, GFP_KERNEL);
if (!cf)
return -ENOMEM;
init_timer(&cf->timer);
diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c
index 3ac5b12..a0b1587 100644
--- a/drivers/pnp/isapnp/core.c
+++ b/drivers/pnp/isapnp/core.c
@@ -395,7 +395,7 @@ static void isapnp_parse_id(struct pnp_dev * dev, unsigned short vendor, unsigne
struct pnp_id * id;
if (!dev)
return;
- id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
+ id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!id)
return;
sprintf(id->id, "%c%c%c%x%x%x%x",
@@ -419,7 +419,7 @@ static struct pnp_dev * __init isapnp_parse_device(struct pnp_card *card, int si
struct pnp_dev *dev;
isapnp_peek(tmp, size);
- dev = kcalloc(1, sizeof(struct pnp_dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
if (!dev)
return NULL;
dev->number = number;
@@ -450,7 +450,7 @@ static void __init isapnp_parse_irq_resource(struct pnp_option *option,
unsigned long bits;
isapnp_peek(tmp, size);
- irq = kcalloc(1, sizeof(struct pnp_irq), GFP_KERNEL);
+ irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
if (!irq)
return;
bits = (tmp[1] << 8) | tmp[0];
@@ -474,7 +474,7 @@ static void __init isapnp_parse_dma_resource(struct pnp_option *option,
struct pnp_dma *dma;
isapnp_peek(tmp, size);
- dma = kcalloc(1, sizeof(struct pnp_dma), GFP_KERNEL);
+ dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
if (!dma)
return;
dma->map = tmp[0];
@@ -494,7 +494,7 @@ static void __init isapnp_parse_port_resource(struct pnp_option *option,
struct pnp_port *port;
isapnp_peek(tmp, size);
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = (tmp[2] << 8) | tmp[1];
@@ -517,7 +517,7 @@ static void __init isapnp_parse_fixed_port_resource(struct pnp_option *option,
struct pnp_port *port;
isapnp_peek(tmp, size);
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = port->max = (tmp[1] << 8) | tmp[0];
@@ -539,7 +539,7 @@ static void __init isapnp_parse_mem_resource(struct pnp_option *option,
struct pnp_mem *mem;
isapnp_peek(tmp, size);
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = ((tmp[2] << 8) | tmp[1]) << 8;
@@ -562,7 +562,7 @@ static void __init isapnp_parse_mem32_resource(struct pnp_option *option,
struct pnp_mem *mem;
isapnp_peek(tmp, size);
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1];
@@ -584,7 +584,7 @@ static void __init isapnp_parse_fixed_mem32_resource(struct pnp_option *option,
struct pnp_mem *mem;
isapnp_peek(tmp, size);
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = mem->max = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1];
@@ -829,7 +829,7 @@ static unsigned char __init isapnp_checksum(unsigned char *data)
static void isapnp_parse_card_id(struct pnp_card * card, unsigned short vendor, unsigned short device)
{
- struct pnp_id * id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
+ struct pnp_id * id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!id)
return;
sprintf(id->id, "%c%c%c%x%x%x%x",
@@ -865,7 +865,7 @@ static int __init isapnp_build_device_list(void)
header[4], header[5], header[6], header[7], header[8]);
printk(KERN_DEBUG "checksum = 0x%x\n", checksum);
#endif
- if ((card = kcalloc(1, sizeof(struct pnp_card), GFP_KERNEL)) == NULL)
+ if ((card = kzalloc(sizeof(struct pnp_card), GFP_KERNEL)) == NULL)
continue;
card->number = csn;
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 6cf34a6..62eda5d 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -139,7 +139,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
return 0;
pnp_dbg("ACPI device : hid %s", acpi_device_hid(device));
- dev = kcalloc(1, sizeof(struct pnp_dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
if (!dev) {
pnp_err("Out of memory");
return -ENOMEM;
@@ -169,7 +169,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
dev->number = num;
/* set the initial values for the PnP device */
- dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
+ dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!dev_id)
goto err;
pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id);
@@ -201,7 +201,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
for (i = 0; i < cid_list->count; i++) {
if (!ispnpidacpi(cid_list->id[i].value))
continue;
- dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
+ dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!dev_id)
continue;
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index 379048f..7a53554 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/drivers/pnp/pnpacpi/rsparser.c
@@ -298,7 +298,7 @@ static void pnpacpi_parse_dma_option(struct pnp_option *option, struct acpi_reso
if (p->channel_count == 0)
return;
- dma = kcalloc(1, sizeof(struct pnp_dma), GFP_KERNEL);
+ dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
if (!dma)
return;
@@ -354,7 +354,7 @@ static void pnpacpi_parse_irq_option(struct pnp_option *option,
if (p->interrupt_count == 0)
return;
- irq = kcalloc(1, sizeof(struct pnp_irq), GFP_KERNEL);
+ irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
if (!irq)
return;
@@ -375,7 +375,7 @@ static void pnpacpi_parse_ext_irq_option(struct pnp_option *option,
if (p->interrupt_count == 0)
return;
- irq = kcalloc(1, sizeof(struct pnp_irq), GFP_KERNEL);
+ irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
if (!irq)
return;
@@ -396,7 +396,7 @@ pnpacpi_parse_port_option(struct pnp_option *option,
if (io->address_length == 0)
return;
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = io->minimum;
@@ -417,7 +417,7 @@ pnpacpi_parse_fixed_port_option(struct pnp_option *option,
if (io->address_length == 0)
return;
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = port->max = io->address;
@@ -436,7 +436,7 @@ pnpacpi_parse_mem24_option(struct pnp_option *option,
if (p->address_length == 0)
return;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = p->minimum;
@@ -459,7 +459,7 @@ pnpacpi_parse_mem32_option(struct pnp_option *option,
if (p->address_length == 0)
return;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = p->minimum;
@@ -482,7 +482,7 @@ pnpacpi_parse_fixed_mem32_option(struct pnp_option *option,
if (p->address_length == 0)
return;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = mem->max = p->address;
@@ -514,7 +514,7 @@ pnpacpi_parse_address_option(struct pnp_option *option, struct acpi_resource *r)
return;
if (p->resource_type == ACPI_MEMORY_RANGE) {
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = mem->max = p->minimum;
@@ -524,7 +524,7 @@ pnpacpi_parse_address_option(struct pnp_option *option, struct acpi_resource *r)
ACPI_READ_WRITE_MEMORY) ? IORESOURCE_MEM_WRITEABLE : 0;
pnp_register_mem_resource(option, mem);
} else if (p->resource_type == ACPI_IO_RANGE) {
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = port->max = p->minimum;
@@ -721,7 +721,7 @@ int pnpacpi_build_resource_template(acpi_handle handle,
if (!res_cnt)
return -EINVAL;
buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1) + 1;
- buffer->pointer = kcalloc(1, buffer->length - 1, GFP_KERNEL);
+ buffer->pointer = kzalloc(buffer->length - 1, GFP_KERNEL);
if (!buffer->pointer)
return -ENOMEM;
pnp_dbg("Res cnt %d", res_cnt);
diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
index 33adeba..95738db 100644
--- a/drivers/pnp/pnpbios/core.c
+++ b/drivers/pnp/pnpbios/core.c
@@ -109,10 +109,10 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
if (!current->fs->root) {
return -EAGAIN;
}
- if (!(envp = (char **) kcalloc (20, sizeof (char *), GFP_KERNEL))) {
+ if (!(envp = kcalloc(20, sizeof (char *), GFP_KERNEL))) {
return -ENOMEM;
}
- if (!(buf = kcalloc (1, 256, GFP_KERNEL))) {
+ if (!(buf = kzalloc(256, GFP_KERNEL))) {
kfree (envp);
return -ENOMEM;
}
@@ -220,7 +220,7 @@ static int pnpbios_get_resources(struct pnp_dev * dev, struct pnp_resource_table
if(!pnpbios_is_dynamic(dev))
return -EPERM;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return -1;
if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) {
@@ -243,7 +243,7 @@ static int pnpbios_set_resources(struct pnp_dev * dev, struct pnp_resource_table
if (!pnpbios_is_dynamic(dev))
return -EPERM;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return -1;
if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) {
@@ -294,7 +294,7 @@ static int pnpbios_disable_resources(struct pnp_dev *dev)
if(dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev))
return -EPERM;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return -ENOMEM;
@@ -336,7 +336,7 @@ static int insert_device(struct pnp_dev *dev, struct pnp_bios_node * node)
}
/* set the initial values for the PnP device */
- dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
+ dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!dev_id)
return -1;
pnpid32_to_pnpid(node->eisa_id,id);
@@ -374,7 +374,7 @@ static void __init build_devlist(void)
struct pnp_bios_node *node;
struct pnp_dev *dev;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return;
@@ -391,7 +391,7 @@ static void __init build_devlist(void)
break;
}
nodes_got++;
- dev = kcalloc(1, sizeof (struct pnp_dev), GFP_KERNEL);
+ dev = kzalloc(sizeof (struct pnp_dev), GFP_KERNEL);
if (!dev)
break;
if(insert_device(dev,node)<0)
diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c
index 5a3dfc9..8027073 100644
--- a/drivers/pnp/pnpbios/proc.c
+++ b/drivers/pnp/pnpbios/proc.c
@@ -87,7 +87,7 @@ static int proc_read_escd(char *buf, char **start, off_t pos,
return -EFBIG;
}
- tmpbuf = kcalloc(1, escd.escd_size, GFP_KERNEL);
+ tmpbuf = kzalloc(escd.escd_size, GFP_KERNEL);
if (!tmpbuf) return -ENOMEM;
if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) {
@@ -133,7 +133,7 @@ static int proc_read_devices(char *buf, char **start, off_t pos,
if (pos >= 0xff)
return 0;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node) return -ENOMEM;
for (nodenum=pos; nodenum<0xff; ) {
@@ -168,7 +168,7 @@ static int proc_read_node(char *buf, char **start, off_t pos,
u8 nodenum = (long)data;
int len;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node) return -ENOMEM;
if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
kfree(node);
@@ -188,7 +188,7 @@ static int proc_write_node(struct file *file, const char __user *buf,
u8 nodenum = (long)data;
int ret = count;
- node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
+ node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return -ENOMEM;
if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c
index ef508a4..95b7968 100644
--- a/drivers/pnp/pnpbios/rsparser.c
+++ b/drivers/pnp/pnpbios/rsparser.c
@@ -248,7 +248,7 @@ static void
pnpbios_parse_mem_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_mem * mem;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = ((p[5] << 8) | p[4]) << 8;
@@ -264,7 +264,7 @@ static void
pnpbios_parse_mem32_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_mem * mem;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
@@ -280,7 +280,7 @@ static void
pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_mem * mem;
- mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
if (!mem)
return;
mem->min = mem->max = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
@@ -297,7 +297,7 @@ pnpbios_parse_irq_option(unsigned char *p, int size, struct pnp_option *option)
struct pnp_irq * irq;
unsigned long bits;
- irq = kcalloc(1, sizeof(struct pnp_irq), GFP_KERNEL);
+ irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
if (!irq)
return;
bits = (p[2] << 8) | p[1];
@@ -314,7 +314,7 @@ static void
pnpbios_parse_dma_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_dma * dma;
- dma = kcalloc(1, sizeof(struct pnp_dma), GFP_KERNEL);
+ dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
if (!dma)
return;
dma->map = p[1];
@@ -327,7 +327,7 @@ static void
pnpbios_parse_port_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_port * port;
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = (p[3] << 8) | p[2];
@@ -343,7 +343,7 @@ static void
pnpbios_parse_fixed_port_option(unsigned char *p, int size, struct pnp_option *option)
{
struct pnp_port * port;
- port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
if (!port)
return;
port->min = port->max = (p[2] << 8) | p[1];
@@ -527,7 +527,7 @@ pnpbios_parse_compatible_ids(unsigned char *p, unsigned char *end, struct pnp_de
case SMALL_TAG_COMPATDEVID: /* compatible ID */
if (len != 4)
goto len_err;
- dev_id = kcalloc(1, sizeof (struct pnp_id), GFP_KERNEL);
+ dev_id = kzalloc(sizeof (struct pnp_id), GFP_KERNEL);
if (!dev_id)
return NULL;
memset(dev_id, 0, sizeof(struct pnp_id));
diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile
index b52d547..8433eb7 100644
--- a/drivers/ps3/Makefile
+++ b/drivers/ps3/Makefile
@@ -1 +1,2 @@
obj-y += system-bus.o
+obj-$(CONFIG_PS3_VUART) += vuart.o
diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c
new file mode 100644
index 0000000..6974f65
--- /dev/null
+++ b/drivers/ps3/vuart.c
@@ -0,0 +1,965 @@
+/*
+ * PS3 virtual uart
+ *
+ * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright 2006 Sony Corp.
+ *
+ * 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 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <asm/ps3.h>
+
+#include <asm/lv1call.h>
+#include <asm/bitops.h>
+
+#include "vuart.h"
+
+MODULE_AUTHOR("Sony Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ps3 vuart");
+
+/**
+ * vuart - An inter-partition data link service.
+ * port 0: PS3 AV Settings.
+ * port 2: PS3 System Manager.
+ *
+ * The vuart provides a bi-directional byte stream data link between logical
+ * partitions. Its primary role is as a communications link between the guest
+ * OS and the system policy module. The current HV does not support any
+ * connections other than those listed.
+ */
+
+enum {PORT_COUNT = 3,};
+
+enum vuart_param {
+ PARAM_TX_TRIGGER = 0,
+ PARAM_RX_TRIGGER = 1,
+ PARAM_INTERRUPT_MASK = 2,
+ PARAM_RX_BUF_SIZE = 3, /* read only */
+ PARAM_RX_BYTES = 4, /* read only */
+ PARAM_TX_BUF_SIZE = 5, /* read only */
+ PARAM_TX_BYTES = 6, /* read only */
+ PARAM_INTERRUPT_STATUS = 7, /* read only */
+};
+
+enum vuart_interrupt_bit {
+ INTERRUPT_BIT_TX = 0,
+ INTERRUPT_BIT_RX = 1,
+ INTERRUPT_BIT_DISCONNECT = 2,
+};
+
+enum vuart_interrupt_mask {
+ INTERRUPT_MASK_TX = 1,
+ INTERRUPT_MASK_RX = 2,
+ INTERRUPT_MASK_DISCONNECT = 4,
+};
+
+/**
+ * struct ports_bmp - bitmap indicating ports needing service.
+ *
+ * A 256 bit read only bitmap indicating ports needing service. Do not write
+ * to these bits. Must not cross a page boundary.
+ */
+
+struct ports_bmp {
+ u64 status;
+ u64 unused[3];
+} __attribute__ ((aligned (32)));
+
+/* redefine dev_dbg to do a syntax check */
+
+#if !defined(DEBUG)
+#undef dev_dbg
+static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg(
+ const struct device *_dev, const char *fmt, ...) {return 0;}
+#endif
+
+#define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__)
+static void __attribute__ ((unused)) _dump_ports_bmp(
+ const struct ports_bmp* bmp, const char* func, int line)
+{
+ pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status);
+}
+
+static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id,
+ unsigned int *port_number)
+{
+ switch(match_id) {
+ case PS3_MATCH_ID_AV_SETTINGS:
+ *port_number = 0;
+ return 0;
+ case PS3_MATCH_ID_SYSTEM_MANAGER:
+ *port_number = 2;
+ return 0;
+ default:
+ WARN_ON(1);
+ *port_number = UINT_MAX;
+ return -EINVAL;
+ };
+}
+
+#define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__)
+static void __attribute__ ((unused)) _dump_port_params(unsigned int port_number,
+ const char* func, int line)
+{
+#if defined(DEBUG)
+ static const char *strings[] = {
+ "tx_trigger ",
+ "rx_trigger ",
+ "interrupt_mask ",
+ "rx_buf_size ",
+ "rx_bytes ",
+ "tx_buf_size ",
+ "tx_bytes ",
+ "interrupt_status",
+ };
+ int result;
+ unsigned int i;
+ u64 value;
+
+ for (i = 0; i < ARRAY_SIZE(strings); i++) {
+ result = lv1_get_virtual_uart_param(port_number, i, &value);
+
+ if (result) {
+ pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line,
+ port_number, strings[i], ps3_result(result));
+ continue;
+ }
+ pr_debug("%s:%d: port_%u: %s = %lxh\n",
+ func, line, port_number, strings[i], value);
+ }
+#endif
+}
+
+struct vuart_triggers {
+ unsigned long rx;
+ unsigned long tx;
+};
+
+int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev,
+ struct vuart_triggers *trig)
+{
+ int result;
+ unsigned long size;
+ unsigned long val;
+
+ result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_TX_TRIGGER, &trig->tx);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_RX_BUF_SIZE, &size);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_RX_TRIGGER, &val);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ trig->rx = size - val;
+
+ dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__,
+ trig->tx, trig->rx);
+
+ return result;
+}
+
+int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx,
+ unsigned int rx)
+{
+ int result;
+ unsigned long size;
+
+ result = lv1_set_virtual_uart_param(dev->port_number,
+ PARAM_TX_TRIGGER, tx);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_RX_BUF_SIZE, &size);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ result = lv1_set_virtual_uart_param(dev->port_number,
+ PARAM_RX_TRIGGER, size - rx);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__,
+ tx, rx);
+
+ return result;
+}
+
+static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev,
+ unsigned long *bytes_waiting)
+{
+ int result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_RX_BYTES, bytes_waiting);
+
+ if (result)
+ dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__,
+ *bytes_waiting);
+ return result;
+}
+
+static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev,
+ unsigned long mask)
+{
+ int result;
+
+ dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask);
+
+ dev->interrupt_mask = mask;
+
+ result = lv1_set_virtual_uart_param(dev->port_number,
+ PARAM_INTERRUPT_MASK, dev->interrupt_mask);
+
+ if (result)
+ dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ return result;
+}
+
+static int ps3_vuart_get_interrupt_mask(struct ps3_vuart_port_device *dev,
+ unsigned long *status)
+{
+ int result = lv1_get_virtual_uart_param(dev->port_number,
+ PARAM_INTERRUPT_STATUS, status);
+
+ if (result)
+ dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n",
+ __func__, __LINE__, dev->interrupt_mask, *status,
+ dev->interrupt_mask & *status);
+
+ return result;
+}
+
+int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_TX) ? 0
+ : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ | INTERRUPT_MASK_TX);
+}
+
+int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_RX) ? 0
+ : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ | INTERRUPT_MASK_RX);
+}
+
+int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0
+ : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ | INTERRUPT_MASK_DISCONNECT);
+}
+
+int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_TX)
+ ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ & ~INTERRUPT_MASK_TX) : 0;
+}
+
+int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_RX)
+ ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ & ~INTERRUPT_MASK_RX) : 0;
+}
+
+int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev)
+{
+ return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT)
+ ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask
+ & ~INTERRUPT_MASK_DISCONNECT) : 0;
+}
+
+/**
+ * ps3_vuart_raw_write - Low level write helper.
+ *
+ * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write.
+ */
+
+static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev,
+ const void* buf, unsigned int bytes, unsigned long *bytes_written)
+{
+ int result;
+
+ dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes);
+
+ result = lv1_write_virtual_uart(dev->port_number,
+ ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: "
+ "%s\n", __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ dev->stats.bytes_written += *bytes_written;
+
+ dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__,
+ __LINE__, *bytes_written, bytes, dev->stats.bytes_written);
+
+ return result;
+}
+
+/**
+ * ps3_vuart_raw_read - Low level read helper.
+ *
+ * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read.
+ */
+
+static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf,
+ unsigned int bytes, unsigned long *bytes_read)
+{
+ int result;
+
+ dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes);
+
+ result = lv1_read_virtual_uart(dev->port_number,
+ ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ return result;
+ }
+
+ dev->stats.bytes_read += *bytes_read;
+
+ dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__,
+ *bytes_read, bytes, dev->stats.bytes_read);
+
+ return result;
+}
+
+/**
+ * struct list_buffer - An element for a port device fifo buffer list.
+ */
+
+struct list_buffer {
+ struct list_head link;
+ const unsigned char *head;
+ const unsigned char *tail;
+ unsigned long dbg_number;
+ unsigned char data[];
+};
+
+/**
+ * ps3_vuart_write - the entry point for writing data to a port
+ *
+ * If the port is idle on entry as much of the incoming data is written to
+ * the port as the port will accept. Otherwise a list buffer is created
+ * and any remaning incoming data is copied to that buffer. The buffer is
+ * then enqueued for transmision via the transmit interrupt.
+ */
+
+int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
+ unsigned int bytes)
+{
+ static unsigned long dbg_number;
+ int result;
+ unsigned long flags;
+ struct list_buffer *lb;
+
+ dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
+ bytes, bytes);
+
+ spin_lock_irqsave(&dev->tx_list.lock, flags);
+
+ if (list_empty(&dev->tx_list.head)) {
+ unsigned long bytes_written;
+
+ result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written);
+
+ spin_unlock_irqrestore(&dev->tx_list.lock, flags);
+
+ if (result) {
+ dev_dbg(&dev->core,
+ "%s:%d: ps3_vuart_raw_write failed\n",
+ __func__, __LINE__);
+ return result;
+ }
+
+ if (bytes_written == bytes) {
+ dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n",
+ __func__, __LINE__, bytes);
+ return 0;
+ }
+
+ bytes -= bytes_written;
+ buf += bytes_written;
+ } else
+ spin_unlock_irqrestore(&dev->tx_list.lock, flags);
+
+ lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL);
+
+ if (!lb) {
+ return -ENOMEM;
+ }
+
+ memcpy(lb->data, buf, bytes);
+ lb->head = lb->data;
+ lb->tail = lb->data + bytes;
+ lb->dbg_number = ++dbg_number;
+
+ spin_lock_irqsave(&dev->tx_list.lock, flags);
+ list_add_tail(&lb->link, &dev->tx_list.head);
+ ps3_vuart_enable_interrupt_tx(dev);
+ spin_unlock_irqrestore(&dev->tx_list.lock, flags);
+
+ dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n",
+ __func__, __LINE__, lb->dbg_number, bytes);
+
+ return 0;
+}
+
+/**
+ * ps3_vuart_read - the entry point for reading data from a port
+ *
+ * If enough bytes to satisfy the request are held in the buffer list those
+ * bytes are dequeued and copied to the caller's buffer. Emptied list buffers
+ * are retiered. If the request cannot be statified by bytes held in the list
+ * buffers -EAGAIN is returned.
+ */
+
+int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
+ unsigned int bytes)
+{
+ unsigned long flags;
+ struct list_buffer *lb, *n;
+ unsigned long bytes_read;
+
+ dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
+ bytes, bytes);
+
+ spin_lock_irqsave(&dev->rx_list.lock, flags);
+
+ if (dev->rx_list.bytes_held < bytes) {
+ spin_unlock_irqrestore(&dev->rx_list.lock, flags);
+ dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n",
+ __func__, __LINE__, bytes - dev->rx_list.bytes_held);
+ return -EAGAIN;
+ }
+
+ list_for_each_entry_safe(lb, n, &dev->rx_list.head, link) {
+ bytes_read = min((unsigned int)(lb->tail - lb->head), bytes);
+
+ memcpy(buf, lb->head, bytes_read);
+ buf += bytes_read;
+ bytes -= bytes_read;
+ dev->rx_list.bytes_held -= bytes_read;
+
+ if (bytes_read < lb->tail - lb->head) {
+ lb->head += bytes_read;
+ spin_unlock_irqrestore(&dev->rx_list.lock, flags);
+
+ dev_dbg(&dev->core,
+ "%s:%d: dequeued buf_%lu, %lxh bytes\n",
+ __func__, __LINE__, lb->dbg_number, bytes_read);
+ return 0;
+ }
+
+ dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__,
+ lb->dbg_number);
+
+ list_del(&lb->link);
+ kfree(lb);
+ }
+ spin_unlock_irqrestore(&dev->rx_list.lock, flags);
+
+ dev_dbg(&dev->core, "%s:%d: dequeued buf_%lu, %xh bytes\n",
+ __func__, __LINE__, lb->dbg_number, bytes);
+
+ return 0;
+}
+
+/**
+ * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
+ *
+ * Services the transmit interrupt for the port. Writes as much data from the
+ * buffer list as the port will accept. Retires any emptied list buffers and
+ * adjusts the final list buffer state for a partial write.
+ */
+
+static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev)
+{
+ int result = 0;
+ unsigned long flags;
+ struct list_buffer *lb, *n;
+ unsigned long bytes_total = 0;
+
+ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
+
+ spin_lock_irqsave(&dev->tx_list.lock, flags);
+
+ list_for_each_entry_safe(lb, n, &dev->tx_list.head, link) {
+
+ unsigned long bytes_written;
+
+ result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head,
+ &bytes_written);
+
+ if (result) {
+ dev_dbg(&dev->core,
+ "%s:%d: ps3_vuart_raw_write failed\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ bytes_total += bytes_written;
+
+ if (bytes_written < lb->tail - lb->head) {
+ lb->head += bytes_written;
+ dev_dbg(&dev->core,
+ "%s:%d cleared buf_%lu, %lxh bytes\n",
+ __func__, __LINE__, lb->dbg_number,
+ bytes_written);
+ goto port_full;
+ }
+
+ dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__,
+ lb->dbg_number);
+
+ list_del(&lb->link);
+ kfree(lb);
+ }
+
+ ps3_vuart_disable_interrupt_tx(dev);
+port_full:
+ spin_unlock_irqrestore(&dev->tx_list.lock, flags);
+ dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n",
+ __func__, __LINE__, bytes_total);
+ return result;
+}
+
+/**
+ * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler
+ *
+ * Services the receive interrupt for the port. Creates a list buffer and
+ * copies all waiting port data to that buffer and enqueues the buffer in the
+ * buffer list. Buffer list data is dequeued via ps3_vuart_read.
+ */
+
+static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev)
+{
+ static unsigned long dbg_number;
+ int result = 0;
+ unsigned long flags;
+ struct list_buffer *lb;
+ unsigned long bytes;
+
+ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
+
+ result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes);
+
+ if (result)
+ return -EIO;
+
+ BUG_ON(!bytes);
+
+ /* add some extra space for recently arrived data */
+
+ bytes += 128;
+
+ lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC);
+
+ if (!lb)
+ return -ENOMEM;
+
+ ps3_vuart_raw_read(dev, lb->data, bytes, &bytes);
+
+ lb->head = lb->data;
+ lb->tail = lb->data + bytes;
+ lb->dbg_number = ++dbg_number;
+
+ spin_lock_irqsave(&dev->rx_list.lock, flags);
+ list_add_tail(&lb->link, &dev->rx_list.head);
+ dev->rx_list.bytes_held += bytes;
+ spin_unlock_irqrestore(&dev->rx_list.lock, flags);
+
+ dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %lxh bytes\n",
+ __func__, __LINE__, lb->dbg_number, bytes);
+
+ return 0;
+}
+
+static int ps3_vuart_handle_interrupt_disconnect(
+ struct ps3_vuart_port_device *dev)
+{
+ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
+ BUG_ON("no support");
+ return -1;
+}
+
+/**
+ * ps3_vuart_handle_port_interrupt - second stage interrupt handler
+ *
+ * Services any pending interrupt types for the port. Passes control to the
+ * third stage type specific interrupt handler. Returns control to the first
+ * stage handler after one iteration.
+ */
+
+static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev)
+{
+ int result;
+ unsigned long status;
+
+ result = ps3_vuart_get_interrupt_mask(dev, &status);
+
+ if (result)
+ return result;
+
+ dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__,
+ status);
+
+ if (status & INTERRUPT_MASK_DISCONNECT) {
+ dev->stats.disconnect_interrupts++;
+ result = ps3_vuart_handle_interrupt_disconnect(dev);
+ if (result)
+ ps3_vuart_disable_interrupt_disconnect(dev);
+ }
+
+ if (status & INTERRUPT_MASK_TX) {
+ dev->stats.tx_interrupts++;
+ result = ps3_vuart_handle_interrupt_tx(dev);
+ if (result)
+ ps3_vuart_disable_interrupt_tx(dev);
+ }
+
+ if (status & INTERRUPT_MASK_RX) {
+ dev->stats.rx_interrupts++;
+ result = ps3_vuart_handle_interrupt_rx(dev);
+ if (result)
+ ps3_vuart_disable_interrupt_rx(dev);
+ }
+
+ return 0;
+}
+
+struct vuart_private {
+ unsigned int in_use;
+ unsigned int virq;
+ struct ps3_vuart_port_device *devices[PORT_COUNT];
+ const struct ports_bmp bmp;
+};
+
+/**
+ * ps3_vuart_irq_handler - first stage interrupt handler
+ *
+ * Loops finding any interrupting port and its associated instance data.
+ * Passes control to the second stage port specific interrupt handler. Loops
+ * until all outstanding interrupts are serviced.
+ */
+
+static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private)
+{
+ struct vuart_private *private;
+
+ BUG_ON(!_private);
+ private = (struct vuart_private *)_private;
+
+ while (1) {
+ unsigned int port;
+
+ dump_ports_bmp(&private->bmp);
+
+ port = (BITS_PER_LONG - 1) - __ilog2(private->bmp.status);
+
+ if (port == BITS_PER_LONG)
+ break;
+
+ BUG_ON(port >= PORT_COUNT);
+ BUG_ON(!private->devices[port]);
+
+ ps3_vuart_handle_port_interrupt(private->devices[port]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv)
+{
+ int result;
+ struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv);
+ struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
+
+ result = dev->match_id == drv->match_id;
+
+ dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__,
+ __LINE__, dev->match_id, dev->core.bus_id, drv->match_id,
+ drv->core.name, (result ? "match" : "miss"));
+
+ return result;
+}
+
+static struct vuart_private vuart_private;
+
+static int ps3_vuart_probe(struct device *_dev)
+{
+ int result;
+ unsigned long tmp;
+ struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
+ struct ps3_vuart_port_driver *drv =
+ to_ps3_vuart_port_driver(_dev->driver);
+
+ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
+
+ BUG_ON(!drv);
+
+ result = ps3_vuart_match_id_to_port(dev->match_id, &dev->port_number);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n",
+ __func__, __LINE__, dev->match_id);
+ result = -EINVAL;
+ goto fail_match;
+ }
+
+ if (vuart_private.devices[dev->port_number]) {
+ dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__,
+ __LINE__, dev->port_number);
+ result = -EBUSY;
+ goto fail_match;
+ }
+
+ vuart_private.devices[dev->port_number] = dev;
+
+ INIT_LIST_HEAD(&dev->tx_list.head);
+ spin_lock_init(&dev->tx_list.lock);
+ INIT_LIST_HEAD(&dev->rx_list.head);
+ spin_lock_init(&dev->rx_list.lock);
+
+ vuart_private.in_use++;
+ if (vuart_private.in_use == 1) {
+ result = ps3_alloc_vuart_irq((void*)&vuart_private.bmp.status,
+ &vuart_private.virq);
+
+ if (result) {
+ dev_dbg(&dev->core,
+ "%s:%d: ps3_alloc_vuart_irq failed (%d)\n",
+ __func__, __LINE__, result);
+ result = -EPERM;
+ goto fail_alloc_irq;
+ }
+
+ result = request_irq(vuart_private.virq, ps3_vuart_irq_handler,
+ IRQF_DISABLED, "vuart", &vuart_private);
+
+ if (result) {
+ dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n",
+ __func__, __LINE__, result);
+ goto fail_request_irq;
+ }
+ }
+
+ ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX);
+
+ /* clear stale pending interrupts */
+ ps3_vuart_get_interrupt_mask(dev, &tmp);
+
+ ps3_vuart_set_triggers(dev, 1, 1);
+
+ if (drv->probe)
+ result = drv->probe(dev);
+ else {
+ result = 0;
+ dev_info(&dev->core, "%s:%d: no probe method\n", __func__,
+ __LINE__);
+ }
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: drv->probe failed\n",
+ __func__, __LINE__);
+ goto fail_probe;
+ }
+
+ return result;
+
+fail_probe:
+fail_request_irq:
+ vuart_private.in_use--;
+ if (!vuart_private.in_use) {
+ ps3_free_vuart_irq(vuart_private.virq);
+ vuart_private.virq = NO_IRQ;
+ }
+fail_alloc_irq:
+fail_match:
+ dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__);
+ return result;
+}
+
+static int ps3_vuart_remove(struct device *_dev)
+{
+ struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
+ struct ps3_vuart_port_driver *drv =
+ to_ps3_vuart_port_driver(_dev->driver);
+
+ dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__,
+ dev->core.bus_id);
+
+ BUG_ON(vuart_private.in_use < 1);
+
+ if (drv->remove)
+ drv->remove(dev);
+ else
+ dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__,
+ __LINE__, dev->core.bus_id);
+
+ vuart_private.in_use--;
+
+ if (!vuart_private.in_use) {
+ free_irq(vuart_private.virq, &vuart_private);
+ ps3_free_vuart_irq(vuart_private.virq);
+ vuart_private.virq = NO_IRQ;
+ }
+ return 0;
+}
+
+/**
+ * ps3_vuart - The vuart instance.
+ *
+ * The vuart is managed as a bus that port devices connect to.
+ */
+
+struct bus_type ps3_vuart = {
+ .name = "ps3_vuart",
+ .match = ps3_vuart_match,
+ .probe = ps3_vuart_probe,
+ .remove = ps3_vuart_remove,
+};
+
+int __init ps3_vuart_init(void)
+{
+ int result;
+
+ pr_debug("%s:%d:\n", __func__, __LINE__);
+ result = bus_register(&ps3_vuart);
+ BUG_ON(result);
+ return result;
+}
+
+void __exit ps3_vuart_exit(void)
+{
+ pr_debug("%s:%d:\n", __func__, __LINE__);
+ bus_unregister(&ps3_vuart);
+}
+
+core_initcall(ps3_vuart_init);
+module_exit(ps3_vuart_exit);
+
+/**
+ * ps3_vuart_port_release_device - Remove a vuart port device.
+ */
+
+static void ps3_vuart_port_release_device(struct device *_dev)
+{
+ struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev);
+#if defined(DEBUG)
+ memset(dev, 0xad, sizeof(struct ps3_vuart_port_device));
+#endif
+ kfree(dev);
+}
+
+/**
+ * ps3_vuart_port_device_register - Add a vuart port device.
+ */
+
+int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev)
+{
+ int result;
+ static unsigned int dev_count = 1;
+
+ dev->core.parent = NULL;
+ dev->core.bus = &ps3_vuart;
+ dev->core.release = ps3_vuart_port_release_device;
+
+ snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x",
+ dev_count++);
+
+ dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__);
+
+ result = device_register(&dev->core);
+
+ return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register);
+
+/**
+ * ps3_vuart_port_driver_register - Add a vuart port device driver.
+ */
+
+int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv)
+{
+ int result;
+
+ pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name);
+ drv->core.bus = &ps3_vuart;
+ result = driver_register(&drv->core);
+ return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register);
+
+/**
+ * ps3_vuart_port_driver_unregister - Remove a vuart port device driver.
+ */
+
+void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv)
+{
+ driver_unregister(&drv->core);
+}
+
+EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister);
diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h
new file mode 100644
index 0000000..28fd89f
--- /dev/null
+++ b/drivers/ps3/vuart.h
@@ -0,0 +1,94 @@
+/*
+ * PS3 virtual uart
+ *
+ * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright 2006 Sony Corp.
+ *
+ * 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 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
+ */
+
+#if !defined(_PS3_VUART_H)
+#define _PS3_VUART_H
+
+struct ps3_vuart_stats {
+ unsigned long bytes_written;
+ unsigned long bytes_read;
+ unsigned long tx_interrupts;
+ unsigned long rx_interrupts;
+ unsigned long disconnect_interrupts;
+};
+
+/**
+ * struct ps3_vuart_port_device - a device on a vuart port
+ */
+
+struct ps3_vuart_port_device {
+ enum ps3_match_id match_id;
+ struct device core;
+
+ /* private driver variables */
+ unsigned int port_number;
+ unsigned long interrupt_mask;
+ struct {
+ spinlock_t lock;
+ struct list_head head;
+ } tx_list;
+ struct {
+ unsigned long bytes_held;
+ spinlock_t lock;
+ struct list_head head;
+ } rx_list;
+ struct ps3_vuart_stats stats;
+};
+
+/**
+ * struct ps3_vuart_port_driver - a driver for a device on a vuart port
+ */
+
+struct ps3_vuart_port_driver {
+ enum ps3_match_id match_id;
+ struct device_driver core;
+ int (*probe)(struct ps3_vuart_port_device *);
+ int (*remove)(struct ps3_vuart_port_device *);
+ int (*tx_event)(struct ps3_vuart_port_device *dev);
+ int (*rx_event)(struct ps3_vuart_port_device *dev);
+ int (*disconnect_event)(struct ps3_vuart_port_device *dev);
+ /* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */
+ /* int (*resume)(struct ps3_vuart_port_device *); */
+};
+
+int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev);
+int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv);
+void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv);
+int ps3_vuart_write(struct ps3_vuart_port_device *dev,
+ const void* buf, unsigned int bytes);
+int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
+ unsigned int bytes);
+static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver(
+ struct device_driver *_drv)
+{
+ return container_of(_drv, struct ps3_vuart_port_driver, core);
+}
+static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device(
+ struct device *_dev)
+{
+ return container_of(_dev, struct ps3_vuart_port_device, core);
+}
+
+int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
+ unsigned int bytes);
+int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
+ unsigned int bytes);
+
+#endif
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2a63ab2..09660e2 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -288,7 +288,7 @@ config RTC_DRV_PL031
To compile this driver as a module, choose M here: the
module will be called rtc-pl031.
-config RTC_DRV_AT91
+config RTC_DRV_AT91RM9200
tristate "AT91RM9200"
depends on RTC_CLASS && ARCH_AT91RM9200
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index bd4c45d..e6beeda 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -35,5 +35,5 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
-obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o
+obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91rm9200.c
index 5c8addc..4f654c9 100644
--- a/drivers/rtc/rtc-at91.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -137,6 +137,9 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = at91_alarm_year - 1900;
+ alrm->enabled = (at91_sys_read(AT91_RTC_IMR) & AT91_RTC_ALARM)
+ ? 1 : 0;
+
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
@@ -223,8 +226,6 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned long imr = at91_sys_read(AT91_RTC_IMR);
- seq_printf(seq, "alarm_IRQ\t: %s\n",
- (imr & AT91_RTC_ALARM) ? "yes" : "no");
seq_printf(seq, "update_IRQ\t: %s\n",
(imr & AT91_RTC_ACKUPD) ? "yes" : "no");
seq_printf(seq, "periodic_IRQ\t: %s\n",
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 828b329..94d3df6 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -435,7 +435,7 @@ static int rtc_dev_add_device(struct class_device *class_dev,
goto err_cdev_del;
}
- dev_info(class_dev->dev, "rtc intf: dev (%d:%d)\n",
+ dev_dbg(class_dev->dev, "rtc intf: dev (%d:%d)\n",
MAJOR(rtc->rtc_dev->devt),
MINOR(rtc->rtc_dev->devt));
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index dfef163..205fa28 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -199,7 +199,7 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
struct i2c_client *client;
struct rtc_device *rtc;
- dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+ dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index ba795a4..7bbc26a 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -117,4 +117,85 @@ int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
}
EXPORT_SYMBOL(rtc_tm_to_time);
+
+/* Merge the valid (i.e. non-negative) fields of alarm into the current
+ * time. If the valid alarm fields are earlier than the equivalent
+ * fields in the time, carry one into the least significant invalid
+ * field, so that the alarm expiry is in the future. It assumes that the
+ * least significant invalid field is more significant than the most
+ * significant valid field, and that the seconds field is valid.
+ *
+ * This is used by alarms that take relative (rather than absolute)
+ * times, and/or have a simple binary second counter instead of
+ * day/hour/minute/sec registers.
+ */
+void rtc_merge_alarm(struct rtc_time *now, struct rtc_time *alarm)
+{
+ int *alarmp = &alarm->tm_sec;
+ int *timep = &now->tm_sec;
+ int carry_into, i;
+
+ /* Ignore everything past the 6th element (tm_year). */
+ for (i = 5; i > 0; i--) {
+ if (alarmp[i] < 0)
+ alarmp[i] = timep[i];
+ else
+ break;
+ }
+
+ /* No carry needed if all fields are valid. */
+ if (i == 5)
+ return;
+
+ for (carry_into = i + 1; i >= 0; i--) {
+ if (alarmp[i] < timep[i])
+ break;
+
+ if (alarmp[i] > timep[i])
+ return;
+ }
+
+ switch (carry_into) {
+ case 1:
+ alarm->tm_min++;
+
+ if (alarm->tm_min < 60)
+ return;
+
+ alarm->tm_min = 0;
+ /* fall-through */
+
+ case 2:
+ alarm->tm_hour++;
+
+ if (alarm->tm_hour < 60)
+ return;
+
+ alarm->tm_hour = 0;
+ /* fall-through */
+
+ case 3:
+ alarm->tm_mday++;
+
+ if (alarm->tm_mday <= rtc_days_in_month[alarm->tm_mon])
+ return;
+
+ alarm->tm_mday = 1;
+ /* fall-through */
+
+ case 4:
+ alarm->tm_mon++;
+
+ if (alarm->tm_mon <= 12)
+ return;
+
+ alarm->tm_mon = 1;
+ /* fall-through */
+
+ case 5:
+ alarm->tm_year++;
+ }
+}
+EXPORT_SYMBOL(rtc_merge_alarm);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index eac5fb1..d59880d 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -279,9 +279,8 @@ static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
local_irq_enable();
bcd2tm(&alm->time);
- alm->pending = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG)
+ alm->enabled = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG)
& OMAP_RTC_INTERRUPTS_IT_ALARM);
- alm->enabled = alm->pending && device_may_wakeup(dev);
return 0;
}
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index a760cf6..4b72b8e 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -192,7 +192,7 @@ static int pcf8563_validate_client(struct i2c_client *client)
xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (xfer != ARRAY_SIZE(msgs)) {
- dev_err(&client->adapter->dev,
+ dev_err(&client->dev,
"%s: could not read register 0x%02X\n",
__FUNCTION__, pattern[i].reg);
@@ -203,7 +203,7 @@ static int pcf8563_validate_client(struct i2c_client *client)
if (value > pattern[i].max ||
value < pattern[i].min) {
- dev_dbg(&client->adapter->dev,
+ dev_dbg(&client->dev,
"%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, "
"max=%d, value=%d, raw=0x%02X\n",
__FUNCTION__, i, pattern[i].reg, pattern[i].mask,
@@ -253,7 +253,7 @@ static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
int err = 0;
- dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+ dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
index d51d8f2..c272afd 100644
--- a/drivers/rtc/rtc-proc.c
+++ b/drivers/rtc/rtc-proc.c
@@ -65,7 +65,7 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)
seq_printf(seq, "%02d\n", alrm.time.tm_mday);
else
seq_printf(seq, "**\n");
- seq_printf(seq, "alrm_wakeup\t: %s\n",
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
alrm.enabled ? "yes" : "no");
seq_printf(seq, "alrm_pending\t: %s\n",
alrm.pending ? "yes" : "no");
@@ -120,7 +120,7 @@ static int rtc_proc_add_device(struct class_device *class_dev,
ent->owner = rtc->owner;
ent->data = class_dev;
- dev_info(class_dev->dev, "rtc intf: proc\n");
+ dev_dbg(class_dev->dev, "rtc intf: proc\n");
}
else
rtc_dev = NULL;
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index e2c7698f..1460f6b 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -200,7 +200,7 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
struct i2c_client *client;
struct rs5c372 *rs5c372;
- dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+ dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index e301dea..f406a2b 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -191,6 +191,8 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
alm_en = readb(base + S3C2410_RTCALM);
+ alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
+
pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
alm_en,
alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
@@ -331,12 +333,8 @@ static int s3c_rtc_ioctl(struct device *dev,
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
- unsigned int rtcalm = readb(s3c_rtc_base + S3C2410_RTCALM);
unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
- seq_printf(seq, "alarm_IRQ\t: %s\n",
- (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
-
seq_printf(seq, "periodic_IRQ\t: %s\n",
(ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index bd4d7d1..9c8ead4 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -289,9 +289,7 @@ static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
{
- seq_printf(seq, "trim/divider\t: 0x%08lx\n", RTTR);
- seq_printf(seq, "alarm_IRQ\t: %s\n",
- (RTSR & RTSR_ALE) ? "yes" : "no" );
+ seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR);
seq_printf(seq, "update_IRQ\t: %s\n",
(RTSR & RTSR_HZE) ? "yes" : "no");
seq_printf(seq, "periodic_IRQ\t: %s\n",
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 143302a..72ba1a7 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -2,6 +2,7 @@
* SuperH On-Chip RTC Support
*
* Copyright (C) 2006 Paul Mundt
+ * Copyright (C) 2006 Jamie Lenehan
*
* Based on the old arch/sh/kernel/cpu/rtc.c by:
*
@@ -21,7 +22,10 @@
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
-#include <asm/io.h>
+#include <linux/io.h>
+
+#define DRV_NAME "sh-rtc"
+#define DRV_VERSION "0.1.2"
#ifdef CONFIG_CPU_SH3
#define rtc_reg_size sizeof(u16)
@@ -33,22 +37,26 @@
#define RTC_REG(r) ((r) * rtc_reg_size)
-#define R64CNT RTC_REG(0)
-#define RSECCNT RTC_REG(1)
-#define RMINCNT RTC_REG(2)
-#define RHRCNT RTC_REG(3)
-#define RWKCNT RTC_REG(4)
-#define RDAYCNT RTC_REG(5)
-#define RMONCNT RTC_REG(6)
-#define RYRCNT RTC_REG(7)
-#define RSECAR RTC_REG(8)
-#define RMINAR RTC_REG(9)
-#define RHRAR RTC_REG(10)
-#define RWKAR RTC_REG(11)
-#define RDAYAR RTC_REG(12)
-#define RMONAR RTC_REG(13)
-#define RCR1 RTC_REG(14)
-#define RCR2 RTC_REG(15)
+#define R64CNT RTC_REG(0)
+
+#define RSECCNT RTC_REG(1) /* RTC sec */
+#define RMINCNT RTC_REG(2) /* RTC min */
+#define RHRCNT RTC_REG(3) /* RTC hour */
+#define RWKCNT RTC_REG(4) /* RTC week */
+#define RDAYCNT RTC_REG(5) /* RTC day */
+#define RMONCNT RTC_REG(6) /* RTC month */
+#define RYRCNT RTC_REG(7) /* RTC year */
+#define RSECAR RTC_REG(8) /* ALARM sec */
+#define RMINAR RTC_REG(9) /* ALARM min */
+#define RHRAR RTC_REG(10) /* ALARM hour */
+#define RWKAR RTC_REG(11) /* ALARM week */
+#define RDAYAR RTC_REG(12) /* ALARM day */
+#define RMONAR RTC_REG(13) /* ALARM month */
+#define RCR1 RTC_REG(14) /* Control */
+#define RCR2 RTC_REG(15) /* Control */
+
+/* ALARM Bits - or with BCD encoded value */
+#define AR_ENB 0x80 /* Enable for alarm cmp */
/* RCR1 Bits */
#define RCR1_CF 0x80 /* Carry Flag */
@@ -71,22 +79,28 @@ struct sh_rtc {
unsigned int alarm_irq, periodic_irq, carry_irq;
struct rtc_device *rtc_dev;
spinlock_t lock;
+ int rearm_aie;
};
-static irqreturn_t sh_rtc_interrupt(int irq, void *id)
+static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
{
- struct platform_device *pdev = id;
+ struct platform_device *pdev = to_platform_device(dev_id);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int tmp, events = 0;
spin_lock(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF;
- if (tmp & RCR1_AF)
- events |= RTC_AF | RTC_IRQF;
-
- tmp &= ~(RCR1_CF | RCR1_AF);
+ if (rtc->rearm_aie) {
+ if (tmp & RCR1_AF)
+ tmp &= ~RCR1_AF; /* try to clear AF again */
+ else {
+ tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */
+ rtc->rearm_aie = 0;
+ }
+ }
writeb(tmp, rtc->regbase + RCR1);
@@ -97,9 +111,45 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *id)
return IRQ_HANDLED;
}
-static irqreturn_t sh_rtc_periodic(int irq, void *id)
+static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
+{
+ struct platform_device *pdev = to_platform_device(dev_id);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp, events = 0;
+
+ spin_lock(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ /*
+ * If AF is set then the alarm has triggered. If we clear AF while
+ * the alarm time still matches the RTC time then AF will
+ * immediately be set again, and if AIE is enabled then the alarm
+ * interrupt will immediately be retrigger. So we clear AIE here
+ * and use rtc->rearm_aie so that the carry interrupt will keep
+ * trying to clear AF and once it stays cleared it'll re-enable
+ * AIE.
+ */
+ if (tmp & RCR1_AF) {
+ events |= RTC_AF | RTC_IRQF;
+
+ tmp &= ~(RCR1_AF|RCR1_AIE);
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ rtc->rearm_aie = 1;
+
+ rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
+ }
+
+ spin_unlock(&rtc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
{
- struct sh_rtc *rtc = dev_get_drvdata(id);
+ struct platform_device *pdev = to_platform_device(dev_id);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
spin_lock(&rtc->lock);
@@ -139,10 +189,11 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
tmp = readb(rtc->regbase + RCR1);
- if (enable)
- tmp |= RCR1_AIE;
- else
+ if (!enable) {
tmp &= ~RCR1_AIE;
+ rtc->rearm_aie = 0;
+ } else if (rtc->rearm_aie == 0)
+ tmp |= RCR1_AIE;
writeb(tmp, rtc->regbase + RCR1);
@@ -177,7 +228,7 @@ static int sh_rtc_open(struct device *dev)
goto err_bad_carry;
}
- ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, IRQF_DISABLED,
+ ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED,
"sh-rtc alarm", dev);
if (unlikely(ret)) {
dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n",
@@ -200,6 +251,7 @@ static void sh_rtc_release(struct device *dev)
struct sh_rtc *rtc = dev_get_drvdata(dev);
sh_rtc_setpie(dev, 0);
+ sh_rtc_setaie(dev, 0);
free_irq(rtc->periodic_irq, dev);
free_irq(rtc->carry_irq, dev);
@@ -267,7 +319,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT));
tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT));
tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT));
- tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT));
+ tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)) - 1;
#if defined(CONFIG_CPU_SH4)
yr = readw(rtc->regbase + RYRCNT);
@@ -295,7 +347,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
- tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+ tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
if (rtc_valid_tm(tm) < 0)
dev_err(dev, "invalid date\n");
@@ -322,7 +374,7 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT);
writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT);
writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT);
- writeb(BIN2BCD(tm->tm_mon), rtc->regbase + RMONCNT);
+ writeb(BIN2BCD(tm->tm_mon + 1), rtc->regbase + RMONCNT);
#ifdef CONFIG_CPU_SH3
year = tm->tm_year % 100;
@@ -344,12 +396,136 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
return 0;
}
+static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
+{
+ unsigned int byte;
+ int value = 0xff; /* return 0xff for ignored values */
+
+ byte = readb(rtc->regbase + reg_off);
+ if (byte & AR_ENB) {
+ byte &= ~AR_ENB; /* strip the enable bit */
+ value = BCD2BIN(byte);
+ }
+
+ return value;
+}
+
+static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ struct rtc_time* tm = &wkalrm->time;
+
+ spin_lock_irq(&rtc->lock);
+
+ tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR);
+ tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR);
+ tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR);
+ tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR);
+ tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR);
+ tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR);
+ if (tm->tm_mon > 0)
+ tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+ tm->tm_year = 0xffff;
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc,
+ int value, int reg_off)
+{
+ /* < 0 for a value that is ignored */
+ if (value < 0)
+ writeb(0, rtc->regbase + reg_off);
+ else
+ writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off);
+}
+
+static int sh_rtc_check_alarm(struct rtc_time* tm)
+{
+ /*
+ * The original rtc says anything > 0xc0 is "don't care" or "match
+ * all" - most users use 0xff but rtc-dev uses -1 for the same thing.
+ * The original rtc doesn't support years - some things use -1 and
+ * some 0xffff. We use -1 to make out tests easier.
+ */
+ if (tm->tm_year == 0xffff)
+ tm->tm_year = -1;
+ if (tm->tm_mon >= 0xff)
+ tm->tm_mon = -1;
+ if (tm->tm_mday >= 0xff)
+ tm->tm_mday = -1;
+ if (tm->tm_wday >= 0xff)
+ tm->tm_wday = -1;
+ if (tm->tm_hour >= 0xff)
+ tm->tm_hour = -1;
+ if (tm->tm_min >= 0xff)
+ tm->tm_min = -1;
+ if (tm->tm_sec >= 0xff)
+ tm->tm_sec = -1;
+
+ if (tm->tm_year > 9999 ||
+ tm->tm_mon >= 12 ||
+ tm->tm_mday == 0 || tm->tm_mday >= 32 ||
+ tm->tm_wday >= 7 ||
+ tm->tm_hour >= 24 ||
+ tm->tm_min >= 60 ||
+ tm->tm_sec >= 60)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int rcr1;
+ struct rtc_time *tm = &wkalrm->time;
+ int mon, err;
+
+ err = sh_rtc_check_alarm(tm);
+ if (unlikely(err < 0))
+ return err;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* disable alarm interrupt and clear flag */
+ rcr1 = readb(rtc->regbase + RCR1);
+ rcr1 &= ~RCR1_AF;
+ writeb(rcr1 & ~RCR1_AIE, rtc->regbase + RCR1);
+
+ rtc->rearm_aie = 0;
+
+ /* set alarm time */
+ sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR);
+ mon = tm->tm_mon;
+ if (mon >= 0)
+ mon += 1;
+ sh_rtc_write_alarm_value(rtc, mon, RMONAR);
+
+ /* Restore interrupt activation status */
+ writeb(rcr1, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
static struct rtc_class_ops sh_rtc_ops = {
.open = sh_rtc_open,
.release = sh_rtc_release,
.ioctl = sh_rtc_ioctl,
.read_time = sh_rtc_read_time,
.set_time = sh_rtc_set_time,
+ .read_alarm = sh_rtc_read_alarm,
+ .set_alarm = sh_rtc_set_alarm,
.proc = sh_rtc_proc,
};
@@ -442,7 +618,7 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev)
}
static struct platform_driver sh_rtc_platform_driver = {
.driver = {
- .name = "sh-rtc",
+ .name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = sh_rtc_probe,
@@ -463,5 +639,6 @@ module_init(sh_rtc_init);
module_exit(sh_rtc_exit);
MODULE_DESCRIPTION("SuperH on-chip RTC driver");
-MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 625637b8..9418a59 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -83,7 +83,7 @@ static int __devinit rtc_sysfs_add_device(struct class_device *class_dev,
{
int err;
- dev_info(class_dev->dev, "rtc intf: sysfs\n");
+ dev_dbg(class_dev->dev, "rtc intf: sysfs\n");
err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group);
if (err)
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 9a67487..019ae25 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -372,7 +372,7 @@ static int x1205_validate_client(struct i2c_client *client)
};
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
- dev_err(&client->adapter->dev,
+ dev_err(&client->dev,
"%s: could not read register %x\n",
__FUNCTION__, probe_zero_pattern[i]);
@@ -380,7 +380,7 @@ static int x1205_validate_client(struct i2c_client *client)
}
if ((buf & probe_zero_pattern[i+1]) != 0) {
- dev_err(&client->adapter->dev,
+ dev_err(&client->dev,
"%s: register=%02x, zero pattern=%d, value=%x\n",
__FUNCTION__, probe_zero_pattern[i], i, buf);
@@ -400,7 +400,7 @@ static int x1205_validate_client(struct i2c_client *client)
};
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
- dev_err(&client->adapter->dev,
+ dev_err(&client->dev,
"%s: could not read register %x\n",
__FUNCTION__, probe_limits_pattern[i].reg);
@@ -411,7 +411,7 @@ static int x1205_validate_client(struct i2c_client *client)
if (value > probe_limits_pattern[i].max ||
value < probe_limits_pattern[i].min) {
- dev_dbg(&client->adapter->dev,
+ dev_dbg(&client->dev,
"%s: register=%x, lim pattern=%d, value=%d\n",
__FUNCTION__, probe_limits_pattern[i].reg,
i, value);
@@ -506,7 +506,7 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
struct i2c_client *client;
struct rtc_device *rtc;
- dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+ dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index c9321b9..25b5d7a 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -688,7 +688,7 @@ raw3215_probe (struct ccw_device *cdev)
raw->cdev = cdev;
raw->inbuf = (char *) raw + sizeof(struct raw3215_info);
memset(raw, 0, sizeof(struct raw3215_info));
- raw->buffer = (char *) kmalloc(RAW3215_BUFFER_SIZE,
+ raw->buffer = kmalloc(RAW3215_BUFFER_SIZE,
GFP_KERNEL|GFP_DMA);
if (raw->buffer == NULL) {
spin_lock(&raw3215_device_lock);
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index e3491a5..3e86fd1 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -377,7 +377,7 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
if (!(key_map = kbd->key_maps[tmp.kb_table])) {
int j;
- key_map = (ushort *) kmalloc(sizeof(plain_map),
+ key_map = kmalloc(sizeof(plain_map),
GFP_KERNEL);
if (!key_map)
return -ENOMEM;
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 732dfbd..f7c10d9 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -127,7 +127,7 @@ cpi_prepare_req(void)
struct cpi_sccb *sccb;
struct cpi_evbuf *evb;
- req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
+ req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
if (req == NULL)
return ERR_PTR(-ENOMEM);
sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA);
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index a62b000..5bb13a9 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -295,7 +295,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
struct completion work;
int rc;
- ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
@@ -337,7 +337,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
struct completion work;
int rc;
- ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index b6a4ecd..32e3701 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -279,7 +279,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
struct completion work;
int rc;
- ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
@@ -321,7 +321,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
struct completion work;
int rc;
- ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 2da8b93..b7153c1 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -717,7 +717,7 @@ long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, struct ica_xcRB *xcRB)
};
int rc;
- ap_msg.message = (void *) kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
+ ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 3257c22..03cc263 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -1646,7 +1646,7 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
return -1;
}
memset(ch, 0, sizeof (struct channel));
- if ((ch->ccw = (struct ccw1 *) kmalloc(8*sizeof(struct ccw1),
+ if ((ch->ccw = kmalloc(8*sizeof(struct ccw1),
GFP_KERNEL | GFP_DMA)) == NULL) {
kfree(ch);
ctc_pr_warn("ctc: Out of memory in add_channel\n");
@@ -1693,7 +1693,7 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
return -1;
}
fsm_newstate(ch->fsm, CH_STATE_IDLE);
- if ((ch->irb = (struct irb *) kmalloc(sizeof (struct irb),
+ if ((ch->irb = kmalloc(sizeof (struct irb),
GFP_KERNEL)) == NULL) {
ctc_pr_warn("ctc: Out of memory in add_channel\n");
kfree_fsm(ch->fsm);
@@ -2535,7 +2535,7 @@ ctc_print_statistics(struct ctc_priv *priv)
DBF_TEXT(trace, 4, __FUNCTION__);
if (!priv)
return;
- sbuf = (char *)kmalloc(2048, GFP_KERNEL);
+ sbuf = kmalloc(2048, GFP_KERNEL);
if (sbuf == NULL)
return;
p = sbuf;
diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
index 1476ce2..229aeb5 100644
--- a/drivers/s390/net/iucv.c
+++ b/drivers/s390/net/iucv.c
@@ -772,7 +772,7 @@ iucv_register_program (__u8 pgmname[16],
}
/* Allocate handler entry */
- new_handler = (handler *)kmalloc(sizeof(handler), GFP_ATOMIC);
+ new_handler = kmalloc(sizeof(handler), GFP_ATOMIC);
if (new_handler == NULL) {
printk(KERN_WARNING "%s: storage allocation for new handler "
"failed.\n", __FUNCTION__);
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 5d39b2d..85093b7 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -237,7 +237,7 @@ zfcp_device_setup(char *devstr)
return 0;
len = strlen(devstr) + 1;
- str = (char *) kmalloc(len, GFP_KERNEL);
+ str = kmalloc(len, GFP_KERNEL);
if (!str)
goto err_out;
memcpy(str, devstr, len);
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c
index 2722af5..386e7de 100644
--- a/drivers/sbus/char/vfc_dev.c
+++ b/drivers/sbus/char/vfc_dev.c
@@ -659,7 +659,7 @@ static int vfc_probe(void)
if (!cards)
return -ENODEV;
- vfc_dev_lst = (struct vfc_dev **)kmalloc(sizeof(struct vfc_dev *) *
+ vfc_dev_lst = kmalloc(sizeof(struct vfc_dev *) *
(cards+1),
GFP_KERNEL);
if (vfc_dev_lst == NULL)
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index ac108f9..426cd6f 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -288,7 +288,7 @@ int aac_get_containers(struct aac_dev *dev)
if (maximum_num_containers < MAXIMUM_NUM_CONTAINERS)
maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
- fsa_dev_ptr = (struct fsa_dev_info *) kmalloc(
+ fsa_dev_ptr = kmalloc(
sizeof(*fsa_dev_ptr) * maximum_num_containers, GFP_KERNEL);
if (!fsa_dev_ptr) {
aac_fib_free(fibptr);
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index d5cf8b9..6d305b2 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -386,7 +386,7 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
* Ok now init the communication subsystem
*/
- dev->queues = (struct aac_queue_block *) kmalloc(sizeof(struct aac_queue_block), GFP_KERNEL);
+ dev->queues = kmalloc(sizeof(struct aac_queue_block), GFP_KERNEL);
if (dev->queues == NULL) {
printk(KERN_ERR "Error could not allocate comm region.\n");
return NULL;
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index d7a61a6..1d239f6 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -699,7 +699,7 @@ static int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
#endif
int i;
ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather */
- SCpnt->host_scribble = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ SCpnt->host_scribble = kmalloc(512, GFP_KERNEL | GFP_DMA);
sgpnt = (struct scatterlist *) SCpnt->request_buffer;
cptr = (struct chain *) SCpnt->host_scribble;
if (cptr == NULL) {
diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c
index 46eed10..7d1fec6 100644
--- a/drivers/scsi/aic7xxx_old.c
+++ b/drivers/scsi/aic7xxx_old.c
@@ -2565,7 +2565,7 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p)
}
}
scb_count = min( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
- scb_ap = (struct aic7xxx_scb *)kmalloc(sizeof (struct aic7xxx_scb) * scb_count
+ scb_ap = kmalloc(sizeof (struct aic7xxx_scb) * scb_count
+ sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC);
if (scb_ap == NULL)
return(0);
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index e95b367..a965ed3 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -4319,7 +4319,7 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
dprintkdbg(DBG_1, "Allocate %i pages for SG tables\n", pages);
while (pages--) {
- ptr = (struct SGentry *)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ ptr = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ptr) {
adapter_sg_tables_free(acb);
return 1;
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index 60b1b43..365db53 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -297,7 +297,7 @@ static void adpt_inquiry(adpt_hba* pHba)
s32 rcode;
memset(msg, 0, sizeof(msg));
- buf = (u8*)kmalloc(80,GFP_KERNEL|ADDR32);
+ buf = kmalloc(80,GFP_KERNEL|ADDR32);
if(!buf){
printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
return;
@@ -1311,7 +1311,7 @@ static s32 adpt_i2o_reset_hba(adpt_hba* pHba)
schedule_timeout_uninterruptible(1);
} while (m == EMPTY_QUEUE);
- status = (u8*)kmalloc(4, GFP_KERNEL|ADDR32);
+ status = kmalloc(4, GFP_KERNEL|ADDR32);
if(status == NULL) {
adpt_send_nop(pHba, m);
printk(KERN_ERR"IOP reset failed - no free memory.\n");
@@ -1444,7 +1444,7 @@ static int adpt_i2o_parse_lct(adpt_hba* pHba)
}
continue;
}
- d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+ d = kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
if(d==NULL)
{
printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name);
@@ -2425,7 +2425,7 @@ static s32 adpt_i2o_reparse_lct(adpt_hba* pHba)
pDev = pDev->next_lun;
}
if(!pDev ) { // Something new add it
- d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+ d = kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
if(d==NULL)
{
printk(KERN_CRIT "Out of memory for I2O device data.\n");
@@ -2728,7 +2728,7 @@ static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba)
kfree(pHba->reply_pool);
- pHba->reply_pool = (u32*)kmalloc(pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, GFP_KERNEL|ADDR32);
+ pHba->reply_pool = kmalloc(pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, GFP_KERNEL|ADDR32);
if(!pHba->reply_pool){
printk(KERN_ERR"%s: Could not allocate reply pool\n",pHba->name);
return -1;
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index f160357..d561663 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -2828,7 +2828,7 @@ static int i91u_detect(struct scsi_host_template * tpnt)
for (; tul_num_scb >= MAX_TARGETS + 3; tul_num_scb--) {
i = tul_num_ch * tul_num_scb * sizeof(SCB);
- if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
+ if ((tul_scb = kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
break;
}
if (tul_scb == NULL) {
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
index 824fe08..7d23110 100644
--- a/drivers/scsi/osst.c
+++ b/drivers/scsi/osst.c
@@ -5777,7 +5777,7 @@ static int osst_probe(struct device *dev)
dev_num = i;
/* allocate a struct osst_tape for this device */
- tpnt = (struct osst_tape *)kmalloc(sizeof(struct osst_tape), GFP_ATOMIC);
+ tpnt = kmalloc(sizeof(struct osst_tape), GFP_ATOMIC);
if (tpnt == NULL) {
write_unlock(&os_scsi_tapes_lock);
printk(KERN_ERR "osst :E: Can't allocate device descriptor, device not attached.\n");
diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c
index aa60a5f..3b2e1a5 100644
--- a/drivers/scsi/pluto.c
+++ b/drivers/scsi/pluto.c
@@ -117,7 +117,7 @@ int __init pluto_detect(struct scsi_host_template *tpnt)
#endif
return 0;
}
- fcs = (struct ctrl_inquiry *) kmalloc (sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA);
+ fcs = kmalloc(sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA);
if (!fcs) {
printk ("PLUTO: Not enough memory to probe\n");
return 0;
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index d1268cb..0578ba4 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -546,7 +546,7 @@ int sr_is_xa(Scsi_CD *cd)
if (!xa_test)
return 0;
- raw_sector = (unsigned char *) kmalloc(2048, GFP_KERNEL | SR_GFP_DMA(cd));
+ raw_sector = kmalloc(2048, GFP_KERNEL | SR_GFP_DMA(cd));
if (!raw_sector)
return -ENOMEM;
if (0 == sr_read_sector(cd, cd->ms_offset + 16,
diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c
index a3e9d0f..4eb3da9 100644
--- a/drivers/scsi/sr_vendor.c
+++ b/drivers/scsi/sr_vendor.c
@@ -117,7 +117,7 @@ int sr_set_blocklength(Scsi_CD *cd, int blocklength)
density = (blocklength > 2048) ? 0x81 : 0x83;
#endif
- buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
if (!buffer)
return -ENOMEM;
@@ -164,7 +164,7 @@ int sr_cd_check(struct cdrom_device_info *cdi)
if (cd->cdi.mask & CDC_MULTI_SESSION)
return 0;
- buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
if (!buffer)
return -ENOMEM;
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index 940fa1e..21cd4c7 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -5545,7 +5545,7 @@ int sym_hcb_attach(struct Scsi_Host *shost, struct sym_fw *fw, struct sym_nvram
/*
* Allocate the array of lists of CCBs hashed by DSA.
*/
- np->ccbh = kcalloc(sizeof(struct sym_ccb **), CCB_HASH_SIZE, GFP_KERNEL);
+ np->ccbh = kcalloc(CCB_HASH_SIZE, sizeof(struct sym_ccb **), GFP_KERNEL);
if (!np->ccbh)
goto attach_failed;
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index fc12d5d..2978c09 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -151,32 +151,6 @@ config SERIAL_8250_MANY_PORTS
say N here to save some memory. You can also say Y if you have an
"intelligent" multiport card such as Cyclades, Digiboards, etc.
-config SERIAL_8250_SHARE_IRQ
- bool "Support for sharing serial interrupts"
- depends on SERIAL_8250_EXTENDED
- help
- Some serial boards have hardware support which allows multiple dumb
- serial ports on the same board to share a single IRQ. To enable
- support for this in the serial driver, say Y here.
-
-config SERIAL_8250_DETECT_IRQ
- bool "Autodetect IRQ on standard ports (unsafe)"
- depends on SERIAL_8250_EXTENDED
- help
- Say Y here if you want the kernel to try to guess which IRQ
- to use for your serial port.
-
- This is considered unsafe; it is far better to configure the IRQ in
- a boot script using the setserial command.
-
- If unsure, say N.
-
-config SERIAL_8250_RSA
- bool "Support RSA serial ports"
- depends on SERIAL_8250_EXTENDED
- help
- ::: To be written :::
-
#
# Multi-port serial cards
#
@@ -199,7 +173,6 @@ config SERIAL_8250_ACCENT
To compile this driver as a module, choose M here: the module
will be called 8250_accent.
-
config SERIAL_8250_BOCA
tristate "Support Boca cards"
depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
@@ -230,6 +203,32 @@ config SERIAL_8250_HUB6
To compile this driver as a module, choose M here: the module
will be called 8250_hub6.
+config SERIAL_8250_SHARE_IRQ
+ bool "Support for sharing serial interrupts"
+ depends on SERIAL_8250_EXTENDED
+ help
+ Some serial boards have hardware support which allows multiple dumb
+ serial ports on the same board to share a single IRQ. To enable
+ support for this in the serial driver, say Y here.
+
+config SERIAL_8250_DETECT_IRQ
+ bool "Autodetect IRQ on standard ports (unsafe)"
+ depends on SERIAL_8250_EXTENDED
+ help
+ Say Y here if you want the kernel to try to guess which IRQ
+ to use for your serial port.
+
+ This is considered unsafe; it is far better to configure the IRQ in
+ a boot script using the setserial command.
+
+ If unsure, say N.
+
+config SERIAL_8250_RSA
+ bool "Support RSA serial ports"
+ depends on SERIAL_8250_EXTENDED
+ help
+ ::: To be written :::
+
config SERIAL_8250_MCA
tristate "Support 8250-type ports on MCA buses"
depends on SERIAL_8250 != n && MCA
@@ -664,7 +663,7 @@ config V850E_UART
config V850E_UARTB
bool
- depends V850E_UART && V850E_ME2
+ depends on V850E_UART && V850E_ME2
default y
config V850E_UART_CONSOLE
@@ -910,7 +909,7 @@ config SERIAL_M32R_PLDSIO
config SERIAL_TXX9
bool "TMPTX39XX/49XX SIO support"
- depends HAS_TXX9_SERIAL
+ depends on HAS_TXX9_SERIAL
select SERIAL_CORE
default y
diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c
index 7d623003..71e6a24 100644
--- a/drivers/serial/icom.c
+++ b/drivers/serial/icom.c
@@ -1510,7 +1510,7 @@ static int __devinit icom_probe(struct pci_dev *dev,
}
if ( (retval = pci_request_regions(dev, "icom"))) {
- dev_err(&dev->dev, "pci_request_region FAILED\n");
+ dev_err(&dev->dev, "pci_request_regions FAILED\n");
pci_disable_device(dev);
return retval;
}
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 9031b57..c53b696 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -319,6 +319,28 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
sci_out(port, SCFCR, fcr_val);
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
+static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+{
+ unsigned int fcr_val = 0;
+
+ if (cflag & CRTSCTS) {
+ fcr_val |= SCFCR_MCE;
+
+ ctrl_outw(0x0000, PORT_PSCR);
+ } else {
+ unsigned short data;
+
+ data = ctrl_inw(PORT_PSCR);
+ data &= 0x033f;
+ data |= 0x0400;
+ ctrl_outw(data, PORT_PSCR);
+
+ ctrl_outw(ctrl_inw(SCSPTR0) & 0x17, SCSPTR0);
+ }
+
+ sci_out(port, SCFCR, fcr_val);
+}
#else
/* For SH7750 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index e4557cc..77f7d63 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -90,6 +90,13 @@
# define SCSPTR3 0xffe30010 /* 16 bit SCIF */
# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */
# define SCIF_ONLY
+#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
+# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */
+# define SCSPTR0 SCPDR0
+# define SCIF_ORER 0x0001 /* overrun error bit */
+# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+# define SCIF_ONLY
+# define PORT_PSCR 0xA405011E
#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* overrun error bit */
@@ -495,6 +502,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xfe620000)
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7300)
static inline int sci_rxd_in(struct uart_port *port)
@@ -521,6 +529,13 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
return 1;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == 0xffe00000)
+ return ctrl_inb(SCPDR0) & 0x0001 ? 1 : 0; /* SCIF0 */
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
static inline int sci_rxd_in(struct uart_port *port)
{
@@ -550,6 +565,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xff925000)
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
static inline int sci_rxd_in(struct uart_port *port)
@@ -558,6 +574,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xffe10000)
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7206)
static inline int sci_rxd_in(struct uart_port *port)
@@ -570,6 +587,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xfffe9800)
return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
static inline int sci_rxd_in(struct uart_port *port)
@@ -580,6 +598,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xf8420000)
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
}
#endif
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 494d9b8..6ed3f1d 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -49,6 +49,14 @@ MODULE_LICENSE("GPL");
#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07)==0)
+/* for testing SSCR1 changes that require SSP restart, basically
+ * everything except the service and interrupt enables */
+#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_EBCEI | SSCR1_SCFR \
+ | SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \
+ | SSCR1_RWOT | SSCR1_TRAIL | SSCR1_PINTE \
+ | SSCR1_STRF | SSCR1_EFWR |SSCR1_RFT \
+ | SSCR1_TFT | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
+
#define DEFINE_SSP_REG(reg, off) \
static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \
static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }
@@ -123,8 +131,8 @@ struct driver_data {
u8 n_bytes;
u32 dma_width;
int cs_change;
- void (*write)(struct driver_data *drv_data);
- void (*read)(struct driver_data *drv_data);
+ int (*write)(struct driver_data *drv_data);
+ int (*read)(struct driver_data *drv_data);
irqreturn_t (*transfer_handler)(struct driver_data *drv_data);
void (*cs_control)(u32 command);
};
@@ -132,7 +140,6 @@ struct driver_data {
struct chip_data {
u32 cr0;
u32 cr1;
- u32 to;
u32 psp;
u32 timeout;
u8 n_bytes;
@@ -143,8 +150,8 @@ struct chip_data {
u8 enable_dma;
u8 bits_per_word;
u32 speed_hz;
- void (*write)(struct driver_data *drv_data);
- void (*read)(struct driver_data *drv_data);
+ int (*write)(struct driver_data *drv_data);
+ int (*read)(struct driver_data *drv_data);
void (*cs_control)(u32 command);
};
@@ -166,114 +173,118 @@ static int flush(struct driver_data *drv_data)
return limit;
}
-static void restore_state(struct driver_data *drv_data)
-{
- void *reg = drv_data->ioaddr;
-
- /* Clear status and disable clock */
- write_SSSR(drv_data->clear_sr, reg);
- write_SSCR0(drv_data->cur_chip->cr0 & ~SSCR0_SSE, reg);
-
- /* Load the registers */
- write_SSCR1(drv_data->cur_chip->cr1, reg);
- write_SSCR0(drv_data->cur_chip->cr0, reg);
- if (drv_data->ssp_type != PXA25x_SSP) {
- write_SSTO(0, reg);
- write_SSPSP(drv_data->cur_chip->psp, reg);
- }
-}
-
static void null_cs_control(u32 command)
{
}
-static void null_writer(struct driver_data *drv_data)
+static int null_writer(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
u8 n_bytes = drv_data->n_bytes;
- while ((read_SSSR(reg) & SSSR_TNF)
- && (drv_data->tx < drv_data->tx_end)) {
- write_SSDR(0, reg);
- drv_data->tx += n_bytes;
- }
+ if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)
+ || (drv_data->tx == drv_data->tx_end))
+ return 0;
+
+ write_SSDR(0, reg);
+ drv_data->tx += n_bytes;
+
+ return 1;
}
-static void null_reader(struct driver_data *drv_data)
+static int null_reader(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
u8 n_bytes = drv_data->n_bytes;
while ((read_SSSR(reg) & SSSR_RNE)
- && (drv_data->rx < drv_data->rx_end)) {
+ && (drv_data->rx < drv_data->rx_end)) {
read_SSDR(reg);
drv_data->rx += n_bytes;
}
+
+ return drv_data->rx == drv_data->rx_end;
}
-static void u8_writer(struct driver_data *drv_data)
+static int u8_writer(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
- while ((read_SSSR(reg) & SSSR_TNF)
- && (drv_data->tx < drv_data->tx_end)) {
- write_SSDR(*(u8 *)(drv_data->tx), reg);
- ++drv_data->tx;
- }
+ if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)
+ || (drv_data->tx == drv_data->tx_end))
+ return 0;
+
+ write_SSDR(*(u8 *)(drv_data->tx), reg);
+ ++drv_data->tx;
+
+ return 1;
}
-static void u8_reader(struct driver_data *drv_data)
+static int u8_reader(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
while ((read_SSSR(reg) & SSSR_RNE)
- && (drv_data->rx < drv_data->rx_end)) {
+ && (drv_data->rx < drv_data->rx_end)) {
*(u8 *)(drv_data->rx) = read_SSDR(reg);
++drv_data->rx;
}
+
+ return drv_data->rx == drv_data->rx_end;
}
-static void u16_writer(struct driver_data *drv_data)
+static int u16_writer(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
- while ((read_SSSR(reg) & SSSR_TNF)
- && (drv_data->tx < drv_data->tx_end)) {
- write_SSDR(*(u16 *)(drv_data->tx), reg);
- drv_data->tx += 2;
- }
+ if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)
+ || (drv_data->tx == drv_data->tx_end))
+ return 0;
+
+ write_SSDR(*(u16 *)(drv_data->tx), reg);
+ drv_data->tx += 2;
+
+ return 1;
}
-static void u16_reader(struct driver_data *drv_data)
+static int u16_reader(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
while ((read_SSSR(reg) & SSSR_RNE)
- && (drv_data->rx < drv_data->rx_end)) {
+ && (drv_data->rx < drv_data->rx_end)) {
*(u16 *)(drv_data->rx) = read_SSDR(reg);
drv_data->rx += 2;
}
+
+ return drv_data->rx == drv_data->rx_end;
}
-static void u32_writer(struct driver_data *drv_data)
+
+static int u32_writer(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
- while ((read_SSSR(reg) & SSSR_TNF)
- && (drv_data->tx < drv_data->tx_end)) {
- write_SSDR(*(u32 *)(drv_data->tx), reg);
- drv_data->tx += 4;
- }
+ if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)
+ || (drv_data->tx == drv_data->tx_end))
+ return 0;
+
+ write_SSDR(*(u32 *)(drv_data->tx), reg);
+ drv_data->tx += 4;
+
+ return 1;
}
-static void u32_reader(struct driver_data *drv_data)
+static int u32_reader(struct driver_data *drv_data)
{
void *reg = drv_data->ioaddr;
while ((read_SSSR(reg) & SSSR_RNE)
- && (drv_data->rx < drv_data->rx_end)) {
+ && (drv_data->rx < drv_data->rx_end)) {
*(u32 *)(drv_data->rx) = read_SSDR(reg);
drv_data->rx += 4;
}
+
+ return drv_data->rx == drv_data->rx_end;
}
static void *next_transfer(struct driver_data *drv_data)
@@ -409,166 +420,134 @@ static int wait_dma_channel_stop(int channel)
return limit;
}
-static void dma_handler(int channel, void *data)
+void dma_error_stop(struct driver_data *drv_data, const char *msg)
{
- struct driver_data *drv_data = data;
- struct spi_message *msg = drv_data->cur_msg;
void *reg = drv_data->ioaddr;
- u32 irq_status = DCSR(channel) & DMA_INT_MASK;
- u32 trailing_sssr = 0;
- if (irq_status & DCSR_BUSERR) {
+ /* Stop and reset */
+ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
+ DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
+ write_SSSR(drv_data->clear_sr, reg);
+ write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
+ if (drv_data->ssp_type != PXA25x_SSP)
+ write_SSTO(0, reg);
+ flush(drv_data);
+ write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
- /* Disable interrupts, clear status and reset DMA */
- write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
- write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
- if (drv_data->ssp_type != PXA25x_SSP)
- write_SSTO(0, reg);
- write_SSSR(drv_data->clear_sr, reg);
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
+ unmap_dma_buffers(drv_data);
- if (flush(drv_data) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_handler: flush fail\n");
+ dev_err(&drv_data->pdev->dev, "%s\n", msg);
- unmap_dma_buffers(drv_data);
+ drv_data->cur_msg->state = ERROR_STATE;
+ tasklet_schedule(&drv_data->pump_transfers);
+}
+
+static void dma_transfer_complete(struct driver_data *drv_data)
+{
+ void *reg = drv_data->ioaddr;
+ struct spi_message *msg = drv_data->cur_msg;
+
+ /* Clear and disable interrupts on SSP and DMA channels*/
+ write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
+ write_SSSR(drv_data->clear_sr, reg);
+ DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
+ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
+
+ if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
+ dev_err(&drv_data->pdev->dev,
+ "dma_handler: dma rx channel stop failed\n");
+
+ if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
+ dev_err(&drv_data->pdev->dev,
+ "dma_transfer: ssp rx stall failed\n");
+
+ unmap_dma_buffers(drv_data);
+
+ /* update the buffer pointer for the amount completed in dma */
+ drv_data->rx += drv_data->len -
+ (DCMD(drv_data->rx_channel) & DCMD_LENGTH);
+
+ /* read trailing data from fifo, it does not matter how many
+ * bytes are in the fifo just read until buffer is full
+ * or fifo is empty, which ever occurs first */
+ drv_data->read(drv_data);
+
+ /* return count of what was actually read */
+ msg->actual_length += drv_data->len -
+ (drv_data->rx_end - drv_data->rx);
+
+ /* Release chip select if requested, transfer delays are
+ * handled in pump_transfers */
+ if (drv_data->cs_change)
+ drv_data->cs_control(PXA2XX_CS_DEASSERT);
+
+ /* Move to next transfer */
+ msg->state = next_transfer(drv_data);
+
+ /* Schedule transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
+}
+
+static void dma_handler(int channel, void *data)
+{
+ struct driver_data *drv_data = data;
+ u32 irq_status = DCSR(channel) & DMA_INT_MASK;
+
+ if (irq_status & DCSR_BUSERR) {
if (channel == drv_data->tx_channel)
- dev_err(&drv_data->pdev->dev,
- "dma_handler: bad bus address on "
- "tx channel %d, source %x target = %x\n",
- channel, DSADR(channel), DTADR(channel));
+ dma_error_stop(drv_data,
+ "dma_handler: "
+ "bad bus address on tx channel");
else
- dev_err(&drv_data->pdev->dev,
- "dma_handler: bad bus address on "
- "rx channel %d, source %x target = %x\n",
- channel, DSADR(channel), DTADR(channel));
-
- msg->state = ERROR_STATE;
- tasklet_schedule(&drv_data->pump_transfers);
+ dma_error_stop(drv_data,
+ "dma_handler: "
+ "bad bus address on rx channel");
+ return;
}
/* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
- if ((drv_data->ssp_type == PXA25x_SSP)
- && (channel == drv_data->tx_channel)
- && (irq_status & DCSR_ENDINTR)) {
+ if ((channel == drv_data->tx_channel)
+ && (irq_status & DCSR_ENDINTR)
+ && (drv_data->ssp_type == PXA25x_SSP)) {
/* Wait for rx to stall */
if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
dev_err(&drv_data->pdev->dev,
"dma_handler: ssp rx stall failed\n");
- /* Clear and disable interrupts on SSP and DMA channels*/
- write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
- write_SSSR(drv_data->clear_sr, reg);
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_handler: dma rx channel stop failed\n");
-
- unmap_dma_buffers(drv_data);
-
- /* Read trailing bytes */
- /* Calculate number of trailing bytes, read them */
- trailing_sssr = read_SSSR(reg);
- if ((trailing_sssr & 0xf008) != 0xf000) {
- drv_data->rx = drv_data->rx_end -
- (((trailing_sssr >> 12) & 0x0f) + 1);
- drv_data->read(drv_data);
- }
- msg->actual_length += drv_data->len;
-
- /* Release chip select if requested, transfer delays are
- * handled in pump_transfers */
- if (drv_data->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
-
- /* Move to next transfer */
- msg->state = next_transfer(drv_data);
-
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
+ /* finish this transfer, start the next */
+ dma_transfer_complete(drv_data);
}
}
static irqreturn_t dma_transfer(struct driver_data *drv_data)
{
u32 irq_status;
- u32 trailing_sssr = 0;
- struct spi_message *msg = drv_data->cur_msg;
void *reg = drv_data->ioaddr;
irq_status = read_SSSR(reg) & drv_data->mask_sr;
if (irq_status & SSSR_ROR) {
- /* Clear and disable interrupts on SSP and DMA channels*/
- write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
- write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
- if (drv_data->ssp_type != PXA25x_SSP)
- write_SSTO(0, reg);
- write_SSSR(drv_data->clear_sr, reg);
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- unmap_dma_buffers(drv_data);
-
- if (flush(drv_data) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_transfer: flush fail\n");
-
- dev_warn(&drv_data->pdev->dev, "dma_transfer: fifo overun\n");
-
- drv_data->cur_msg->state = ERROR_STATE;
- tasklet_schedule(&drv_data->pump_transfers);
-
+ dma_error_stop(drv_data, "dma_transfer: fifo overrun");
return IRQ_HANDLED;
}
/* Check for false positive timeout */
- if ((irq_status & SSSR_TINT) && DCSR(drv_data->tx_channel) & DCSR_RUN) {
+ if ((irq_status & SSSR_TINT)
+ && (DCSR(drv_data->tx_channel) & DCSR_RUN)) {
write_SSSR(SSSR_TINT, reg);
return IRQ_HANDLED;
}
if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
- /* Clear and disable interrupts on SSP and DMA channels*/
- write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
+ /* Clear and disable timeout interrupt, do the rest in
+ * dma_transfer_complete */
if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg);
- write_SSSR(drv_data->clear_sr, reg);
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_transfer: dma rx channel stop failed\n");
-
- if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_transfer: ssp rx stall failed\n");
-
- unmap_dma_buffers(drv_data);
-
- /* Calculate number of trailing bytes, read them */
- trailing_sssr = read_SSSR(reg);
- if ((trailing_sssr & 0xf008) != 0xf000) {
- drv_data->rx = drv_data->rx_end -
- (((trailing_sssr >> 12) & 0x0f) + 1);
- drv_data->read(drv_data);
- }
- msg->actual_length += drv_data->len;
-
- /* Release chip select if requested, transfer delays are
- * handled in pump_transfers */
- if (drv_data->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
-
- /* Move to next transfer */
- msg->state = next_transfer(drv_data);
-
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
+ /* finish this transfer, start the next */
+ dma_transfer_complete(drv_data);
return IRQ_HANDLED;
}
@@ -577,89 +556,103 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
return IRQ_NONE;
}
-static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
+static void int_error_stop(struct driver_data *drv_data, const char* msg)
{
- struct spi_message *msg = drv_data->cur_msg;
void *reg = drv_data->ioaddr;
- unsigned long limit = loops_per_jiffy << 1;
- u32 irq_status;
- u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ?
- drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;
-
- while ((irq_status = read_SSSR(reg) & irq_mask)) {
-
- if (irq_status & SSSR_ROR) {
- /* Clear and disable interrupts */
- write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
- write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
- if (drv_data->ssp_type != PXA25x_SSP)
- write_SSTO(0, reg);
- write_SSSR(drv_data->clear_sr, reg);
+ /* Stop and reset SSP */
+ write_SSSR(drv_data->clear_sr, reg);
+ write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
+ if (drv_data->ssp_type != PXA25x_SSP)
+ write_SSTO(0, reg);
+ flush(drv_data);
+ write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
- if (flush(drv_data) == 0)
- dev_err(&drv_data->pdev->dev,
- "interrupt_transfer: flush fail\n");
+ dev_err(&drv_data->pdev->dev, "%s\n", msg);
- /* Stop the SSP */
+ drv_data->cur_msg->state = ERROR_STATE;
+ tasklet_schedule(&drv_data->pump_transfers);
+}
- dev_warn(&drv_data->pdev->dev,
- "interrupt_transfer: fifo overun\n");
+static void int_transfer_complete(struct driver_data *drv_data)
+{
+ void *reg = drv_data->ioaddr;
- msg->state = ERROR_STATE;
- tasklet_schedule(&drv_data->pump_transfers);
+ /* Stop SSP */
+ write_SSSR(drv_data->clear_sr, reg);
+ write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
+ if (drv_data->ssp_type != PXA25x_SSP)
+ write_SSTO(0, reg);
- return IRQ_HANDLED;
- }
+ /* Update total byte transfered return count actual bytes read */
+ drv_data->cur_msg->actual_length += drv_data->len -
+ (drv_data->rx_end - drv_data->rx);
- /* Look for false positive timeout */
- if ((irq_status & SSSR_TINT)
- && (drv_data->rx < drv_data->rx_end))
- write_SSSR(SSSR_TINT, reg);
+ /* Release chip select if requested, transfer delays are
+ * handled in pump_transfers */
+ if (drv_data->cs_change)
+ drv_data->cs_control(PXA2XX_CS_DEASSERT);
- /* Pump data */
- drv_data->read(drv_data);
- drv_data->write(drv_data);
+ /* Move to next transfer */
+ drv_data->cur_msg->state = next_transfer(drv_data);
- if (drv_data->tx == drv_data->tx_end) {
- /* Disable tx interrupt */
- write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
- irq_mask = drv_data->mask_sr & ~SSSR_TFS;
+ /* Schedule transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
+}
- /* PXA25x_SSP has no timeout, read trailing bytes */
- if (drv_data->ssp_type == PXA25x_SSP) {
- while ((read_SSSR(reg) & SSSR_BSY) && limit--)
- drv_data->read(drv_data);
+static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
+{
+ void *reg = drv_data->ioaddr;
- if (limit == 0)
- dev_err(&drv_data->pdev->dev,
- "interrupt_transfer: "
- "trailing byte read failed\n");
- }
- }
+ u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ?
+ drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;
- if ((irq_status & SSSR_TINT)
- || (drv_data->rx == drv_data->rx_end)) {
+ u32 irq_status = read_SSSR(reg) & irq_mask;
- /* Clear timeout */
- write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
- if (drv_data->ssp_type != PXA25x_SSP)
- write_SSTO(0, reg);
- write_SSSR(drv_data->clear_sr, reg);
+ if (irq_status & SSSR_ROR) {
+ int_error_stop(drv_data, "interrupt_transfer: fifo overrun");
+ return IRQ_HANDLED;
+ }
- /* Update total byte transfered */
- msg->actual_length += drv_data->len;
+ if (irq_status & SSSR_TINT) {
+ write_SSSR(SSSR_TINT, reg);
+ if (drv_data->read(drv_data)) {
+ int_transfer_complete(drv_data);
+ return IRQ_HANDLED;
+ }
+ }
- /* Release chip select if requested, transfer delays are
- * handled in pump_transfers */
- if (drv_data->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
+ /* Drain rx fifo, Fill tx fifo and prevent overruns */
+ do {
+ if (drv_data->read(drv_data)) {
+ int_transfer_complete(drv_data);
+ return IRQ_HANDLED;
+ }
+ } while (drv_data->write(drv_data));
- /* Move to next transfer */
- msg->state = next_transfer(drv_data);
+ if (drv_data->read(drv_data)) {
+ int_transfer_complete(drv_data);
+ return IRQ_HANDLED;
+ }
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
+ if (drv_data->tx == drv_data->tx_end) {
+ write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
+ /* PXA25x_SSP has no timeout, read trailing bytes */
+ if (drv_data->ssp_type == PXA25x_SSP) {
+ if (!wait_ssp_rx_stall(reg))
+ {
+ int_error_stop(drv_data, "interrupt_transfer: "
+ "rx stall failed");
+ return IRQ_HANDLED;
+ }
+ if (!drv_data->read(drv_data))
+ {
+ int_error_stop(drv_data,
+ "interrupt_transfer: "
+ "trailing byte read failed");
+ return IRQ_HANDLED;
+ }
+ int_transfer_complete(drv_data);
}
}
@@ -681,7 +674,7 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
write_SSSR(drv_data->clear_sr, reg);
dev_err(&drv_data->pdev->dev, "bad message state "
- "in interrupt handler");
+ "in interrupt handler\n");
/* Never fail */
return IRQ_HANDLED;
@@ -690,6 +683,102 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
return drv_data->transfer_handler(drv_data);
}
+int set_dma_burst_and_threshold(struct chip_data *chip, struct spi_device *spi,
+ u8 bits_per_word, u32 *burst_code,
+ u32 *threshold)
+{
+ struct pxa2xx_spi_chip *chip_info =
+ (struct pxa2xx_spi_chip *)spi->controller_data;
+ int bytes_per_word;
+ int burst_bytes;
+ int thresh_words;
+ int req_burst_size;
+ int retval = 0;
+
+ /* Set the threshold (in registers) to equal the same amount of data
+ * as represented by burst size (in bytes). The computation below
+ * is (burst_size rounded up to nearest 8 byte, word or long word)
+ * divided by (bytes/register); the tx threshold is the inverse of
+ * the rx, so that there will always be enough data in the rx fifo
+ * to satisfy a burst, and there will always be enough space in the
+ * tx fifo to accept a burst (a tx burst will overwrite the fifo if
+ * there is not enough space), there must always remain enough empty
+ * space in the rx fifo for any data loaded to the tx fifo.
+ * Whenever burst_size (in bytes) equals bits/word, the fifo threshold
+ * will be 8, or half the fifo;
+ * The threshold can only be set to 2, 4 or 8, but not 16, because
+ * to burst 16 to the tx fifo, the fifo would have to be empty;
+ * however, the minimum fifo trigger level is 1, and the tx will
+ * request service when the fifo is at this level, with only 15 spaces.
+ */
+
+ /* find bytes/word */
+ if (bits_per_word <= 8)
+ bytes_per_word = 1;
+ else if (bits_per_word <= 16)
+ bytes_per_word = 2;
+ else
+ bytes_per_word = 4;
+
+ /* use struct pxa2xx_spi_chip->dma_burst_size if available */
+ if (chip_info)
+ req_burst_size = chip_info->dma_burst_size;
+ else {
+ switch (chip->dma_burst_size) {
+ default:
+ /* if the default burst size is not set,
+ * do it now */
+ chip->dma_burst_size = DCMD_BURST8;
+ case DCMD_BURST8:
+ req_burst_size = 8;
+ break;
+ case DCMD_BURST16:
+ req_burst_size = 16;
+ break;
+ case DCMD_BURST32:
+ req_burst_size = 32;
+ break;
+ }
+ }
+ if (req_burst_size <= 8) {
+ *burst_code = DCMD_BURST8;
+ burst_bytes = 8;
+ } else if (req_burst_size <= 16) {
+ if (bytes_per_word == 1) {
+ /* don't burst more than 1/2 the fifo */
+ *burst_code = DCMD_BURST8;
+ burst_bytes = 8;
+ retval = 1;
+ } else {
+ *burst_code = DCMD_BURST16;
+ burst_bytes = 16;
+ }
+ } else {
+ if (bytes_per_word == 1) {
+ /* don't burst more than 1/2 the fifo */
+ *burst_code = DCMD_BURST8;
+ burst_bytes = 8;
+ retval = 1;
+ } else if (bytes_per_word == 2) {
+ /* don't burst more than 1/2 the fifo */
+ *burst_code = DCMD_BURST16;
+ burst_bytes = 16;
+ retval = 1;
+ } else {
+ *burst_code = DCMD_BURST32;
+ burst_bytes = 32;
+ }
+ }
+
+ thresh_words = burst_bytes / bytes_per_word;
+
+ /* thresh_words will be between 2 and 8 */
+ *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT)
+ | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT);
+
+ return retval;
+}
+
static void pump_transfers(unsigned long data)
{
struct driver_data *drv_data = (struct driver_data *)data;
@@ -702,6 +791,9 @@ static void pump_transfers(unsigned long data)
u8 bits = 0;
u32 speed = 0;
u32 cr0;
+ u32 cr1;
+ u32 dma_thresh = drv_data->cur_chip->dma_threshold;
+ u32 dma_burst = drv_data->cur_chip->dma_burst_size;
/* Get current state information */
message = drv_data->cur_msg;
@@ -731,6 +823,16 @@ static void pump_transfers(unsigned long data)
udelay(previous->delay_usecs);
}
+ /* Check transfer length */
+ if (transfer->len > 8191)
+ {
+ dev_warn(&drv_data->pdev->dev, "pump_transfers: transfer "
+ "length greater than 8191\n");
+ message->status = -EINVAL;
+ giveback(drv_data);
+ return;
+ }
+
/* Setup the transfer state based on the type of transfer */
if (flush(drv_data) == 0) {
dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
@@ -747,17 +849,15 @@ static void pump_transfers(unsigned long data)
drv_data->rx_end = drv_data->rx + transfer->len;
drv_data->rx_dma = transfer->rx_dma;
drv_data->tx_dma = transfer->tx_dma;
- drv_data->len = transfer->len;
+ drv_data->len = transfer->len & DCMD_LENGTH;
drv_data->write = drv_data->tx ? chip->write : null_writer;
drv_data->read = drv_data->rx ? chip->read : null_reader;
drv_data->cs_change = transfer->cs_change;
/* Change speed and bit per word on a per transfer */
+ cr0 = chip->cr0;
if (transfer->speed_hz || transfer->bits_per_word) {
- /* Disable clock */
- write_SSCR0(chip->cr0 & ~SSCR0_SSE, reg);
- cr0 = chip->cr0;
bits = chip->bits_per_word;
speed = chip->speed_hz;
@@ -796,15 +896,24 @@ static void pump_transfers(unsigned long data)
drv_data->write = drv_data->write != null_writer ?
u32_writer : null_writer;
}
+ /* if bits/word is changed in dma mode, then must check the
+ * thresholds and burst also */
+ if (chip->enable_dma) {
+ if (set_dma_burst_and_threshold(chip, message->spi,
+ bits, &dma_burst,
+ &dma_thresh))
+ if (printk_ratelimit())
+ dev_warn(&message->spi->dev,
+ "pump_transfer: "
+ "DMA burst size reduced to "
+ "match bits_per_word\n");
+ }
cr0 = clk_div
| SSCR0_Motorola
| SSCR0_DataSize(bits > 16 ? bits - 16 : bits)
| SSCR0_SSE
| (bits > 16 ? SSCR0_EDSS : 0);
-
- /* Start it back up */
- write_SSCR0(cr0, reg);
}
message->state = RUNNING_STATE;
@@ -823,13 +932,13 @@ static void pump_transfers(unsigned long data)
/* No target address increment */
DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
| drv_data->dma_width
- | chip->dma_burst_size
+ | dma_burst
| drv_data->len;
else
DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
| DCMD_FLOWSRC
| drv_data->dma_width
- | chip->dma_burst_size
+ | dma_burst
| drv_data->len;
/* Setup tx DMA Channel */
@@ -840,13 +949,13 @@ static void pump_transfers(unsigned long data)
/* No source address increment */
DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
| drv_data->dma_width
- | chip->dma_burst_size
+ | dma_burst
| drv_data->len;
else
DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
| DCMD_FLOWTRG
| drv_data->dma_width
- | chip->dma_burst_size
+ | dma_burst
| drv_data->len;
/* Enable dma end irqs on SSP to detect end of transfer */
@@ -856,16 +965,11 @@ static void pump_transfers(unsigned long data)
/* Fix me, need to handle cs polarity */
drv_data->cs_control(PXA2XX_CS_ASSERT);
- /* Go baby, go */
+ /* Clear status and start DMA engine */
+ cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1;
write_SSSR(drv_data->clear_sr, reg);
DCSR(drv_data->rx_channel) |= DCSR_RUN;
DCSR(drv_data->tx_channel) |= DCSR_RUN;
- if (drv_data->ssp_type != PXA25x_SSP)
- write_SSTO(chip->timeout, reg);
- write_SSCR1(chip->cr1
- | chip->dma_threshold
- | drv_data->dma_cr1,
- reg);
} else {
/* Ensure we have the correct interrupt handler */
drv_data->transfer_handler = interrupt_transfer;
@@ -873,14 +977,25 @@ static void pump_transfers(unsigned long data)
/* Fix me, need to handle cs polarity */
drv_data->cs_control(PXA2XX_CS_ASSERT);
- /* Go baby, go */
+ /* Clear status */
+ cr1 = chip->cr1 | chip->threshold | drv_data->int_cr1;
write_SSSR(drv_data->clear_sr, reg);
+ }
+
+ /* see if we need to reload the config registers */
+ if ((read_SSCR0(reg) != cr0)
+ || (read_SSCR1(reg) & SSCR1_CHANGE_MASK) !=
+ (cr1 & SSCR1_CHANGE_MASK)) {
+
+ write_SSCR0(cr0 & ~SSCR0_SSE, reg);
if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(chip->timeout, reg);
- write_SSCR1(chip->cr1
- | chip->threshold
- | drv_data->int_cr1,
- reg);
+ write_SSCR1(cr1, reg);
+ write_SSCR0(cr0, reg);
+ } else {
+ if (drv_data->ssp_type != PXA25x_SSP)
+ write_SSTO(chip->timeout, reg);
+ write_SSCR1(cr1, reg);
}
}
@@ -915,9 +1030,9 @@ static void pump_messages(struct work_struct *work)
struct spi_transfer,
transfer_list);
- /* Setup the SSP using the per chip configuration */
+ /* prepare to setup the SSP, in pump_transfers, using the per
+ * chip configuration */
drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
- restore_state(drv_data);
/* Mark as busy and launch transfers */
tasklet_schedule(&drv_data->pump_transfers);
@@ -963,63 +1078,77 @@ static int setup(struct spi_device *spi)
spi->bits_per_word = 8;
if (drv_data->ssp_type != PXA25x_SSP
- && (spi->bits_per_word < 4 || spi->bits_per_word > 32))
+ && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) {
+ dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d "
+ "b/w not 4-32 for type non-PXA25x_SSP\n",
+ drv_data->ssp_type, spi->bits_per_word);
return -EINVAL;
- else if (spi->bits_per_word < 4 || spi->bits_per_word > 16)
+ }
+ else if (drv_data->ssp_type == PXA25x_SSP
+ && (spi->bits_per_word < 4
+ || spi->bits_per_word > 16)) {
+ dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d "
+ "b/w not 4-16 for type PXA25x_SSP\n",
+ drv_data->ssp_type, spi->bits_per_word);
return -EINVAL;
+ }
- /* Only alloc (or use chip_info) on first setup */
+ /* Only alloc on first setup */
chip = spi_get_ctldata(spi);
- if (chip == NULL) {
+ if (!chip) {
chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
- if (!chip)
+ if (!chip) {
+ dev_err(&spi->dev,
+ "failed setup: can't allocate chip data\n");
return -ENOMEM;
+ }
chip->cs_control = null_cs_control;
chip->enable_dma = 0;
- chip->timeout = SSP_TIMEOUT(1000);
+ chip->timeout = 1000;
chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1);
chip->dma_burst_size = drv_data->master_info->enable_dma ?
DCMD_BURST8 : 0;
-
- chip_info = spi->controller_data;
}
+ /* protocol drivers may change the chip settings, so...
+ * if chip_info exists, use it */
+ chip_info = spi->controller_data;
+
/* chip_info isn't always needed */
+ chip->cr1 = 0;
if (chip_info) {
if (chip_info->cs_control)
chip->cs_control = chip_info->cs_control;
- chip->timeout = SSP_TIMEOUT(chip_info->timeout_microsecs);
+ chip->timeout = chip_info->timeout;
- chip->threshold = SSCR1_RxTresh(chip_info->rx_threshold)
- | SSCR1_TxTresh(chip_info->tx_threshold);
+ chip->threshold = (SSCR1_RxTresh(chip_info->rx_threshold) &
+ SSCR1_RFT) |
+ (SSCR1_TxTresh(chip_info->tx_threshold) &
+ SSCR1_TFT);
chip->enable_dma = chip_info->dma_burst_size != 0
&& drv_data->master_info->enable_dma;
chip->dma_threshold = 0;
- if (chip->enable_dma) {
- if (chip_info->dma_burst_size <= 8) {
- chip->dma_threshold = SSCR1_RxTresh(8)
- | SSCR1_TxTresh(8);
- chip->dma_burst_size = DCMD_BURST8;
- } else if (chip_info->dma_burst_size <= 16) {
- chip->dma_threshold = SSCR1_RxTresh(16)
- | SSCR1_TxTresh(16);
- chip->dma_burst_size = DCMD_BURST16;
- } else {
- chip->dma_threshold = SSCR1_RxTresh(32)
- | SSCR1_TxTresh(32);
- chip->dma_burst_size = DCMD_BURST32;
- }
- }
-
-
if (chip_info->enable_loopback)
chip->cr1 = SSCR1_LBM;
}
+ /* set dma burst and threshold outside of chip_info path so that if
+ * chip_info goes away after setting chip->enable_dma, the
+ * burst and threshold can still respond to changes in bits_per_word */
+ if (chip->enable_dma) {
+ /* set up legal burst and threshold for dma */
+ if (set_dma_burst_and_threshold(chip, spi, spi->bits_per_word,
+ &chip->dma_burst_size,
+ &chip->dma_threshold)) {
+ dev_warn(&spi->dev, "in setup: DMA burst size reduced "
+ "to match bits_per_word\n");
+ }
+ }
+
if (drv_data->ioaddr == SSP1_VIRT)
clk_div = SSP1_SerClkDiv(spi->max_speed_hz);
else if (drv_data->ioaddr == SSP2_VIRT)
@@ -1027,7 +1156,11 @@ static int setup(struct spi_device *spi)
else if (drv_data->ioaddr == SSP3_VIRT)
clk_div = SSP3_SerClkDiv(spi->max_speed_hz);
else
+ {
+ dev_err(&spi->dev, "failed setup: unknown IO address=0x%p\n",
+ drv_data->ioaddr);
return -ENODEV;
+ }
chip->speed_hz = spi->max_speed_hz;
chip->cr0 = clk_div
@@ -1071,7 +1204,6 @@ static int setup(struct spi_device *spi)
chip->write = u32_writer;
} else {
dev_err(&spi->dev, "invalid wordsize\n");
- kfree(chip);
return -ENODEV;
}
chip->bits_per_word = spi->bits_per_word;
@@ -1162,6 +1294,12 @@ static int destroy_queue(struct driver_data *drv_data)
int status;
status = stop_queue(drv_data);
+ /* we are unloading the module or failing to load (only two calls
+ * to this routine), and neither call can handle a return value.
+ * However, destroy_workqueue calls flush_workqueue, and that will
+ * block until all work is done. If the reason that stop_queue
+ * timed out is that the work will never finish, then it does no
+ * good to call destroy_workqueue, so return anyway. */
if (status != 0)
return status;
@@ -1360,7 +1498,16 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
/* Remove the queue */
status = destroy_queue(drv_data);
if (status != 0)
- return status;
+ /* the kernel does not check the return status of this
+ * this routine (mod->exit, within the kernel). Therefore
+ * nothing is gained by returning from here, the module is
+ * going away regardless, and we should not leave any more
+ * resources allocated than necessary. We cannot free the
+ * message memory in drv_data->queue, but we can release the
+ * resources below. I think the kernel should honor -EBUSY
+ * returns but... */
+ dev_err(&pdev->dev, "pxa2xx_spi_remove: workqueue will not "
+ "complete, message memory not freed\n");
/* Disable the SSP at the peripheral and SOC level */
write_SSCR0(0, drv_data->ioaddr);
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 72f3db9..3e0abbb 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -598,7 +598,7 @@ at91_ep_alloc_request(struct usb_ep *_ep, unsigned int gfp_flags)
{
struct at91_request *req;
- req = kcalloc(1, sizeof (struct at91_request), gfp_flags);
+ req = kzalloc(sizeof (struct at91_request), gfp_flags);
if (!req)
return NULL;
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 5516c59..2d12bf9 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -2195,7 +2195,7 @@ static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
if (size == 0)
return NULL;
- gb = (struct gs_buf *)kmalloc(sizeof(struct gs_buf), kmalloc_flags);
+ gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags);
if (gb == NULL)
return NULL;
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
index 9325e46..282d82e 100644
--- a/drivers/usb/host/hc_crisv10.c
+++ b/drivers/usb/host/hc_crisv10.c
@@ -365,7 +365,7 @@ static inline struct urb *urb_list_first(int epid)
/* Adds an urb_entry last in the list for this epid. */
static inline void urb_list_add(struct urb *urb, int epid)
{
- urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+ urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
assert(urb_entry);
urb_entry->urb = urb;
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index c703f73..6c7f3ef 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -766,7 +766,7 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned
bep->bufp = kmalloc (bufsize, GFP_KERNEL);
if (!bep->bufp)
goto bl_fail;
- bep->dr = (struct usb_ctrlrequest *) kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL);
+ bep->dr = kmalloc(sizeof (struct usb_ctrlrequest), GFP_KERNEL);
if (!bep->dr)
goto bl_fail;
bep->urbp = usb_alloc_urb (0, GFP_KERNEL);
@@ -1969,7 +1969,7 @@ static int auerswald_probe (struct usb_interface *intf,
info("device is a %s", cp->dev_desc);
/* get the maximum allowed control transfer length */
- pbuf = (__le16 *) kmalloc (2, GFP_KERNEL); /* use an allocated buffer because of urb target */
+ pbuf = kmalloc(2, GFP_KERNEL); /* use an allocated buffer because of urb target */
if (!pbuf) {
err( "out of memory");
goto pfail;
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 7e8a0ac..7025025 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -705,7 +705,7 @@ static int uss720_probe(struct usb_interface *intf,
/*
* Allocate parport interface
*/
- if (!(priv = kcalloc(sizeof(struct parport_uss720_private), 1, GFP_KERNEL))) {
+ if (!(priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) {
usb_put_dev(usbdev);
return -ENOMEM;
}
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index 99f26b3..ea5f44d 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -469,7 +469,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
struct rndis_halt *halt;
/* try to clear any rndis state/activity (no i/o from stack!) */
- halt = kcalloc(1, sizeof *halt, GFP_KERNEL);
+ halt = kzalloc(sizeof *halt, GFP_KERNEL);
if (halt) {
halt->msg_type = RNDIS_MSG_HALT;
halt->msg_len = ccpu2(sizeof *halt);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index a1fdb85..45cdf9b 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -1493,7 +1493,7 @@ static struct cypress_buf *cypress_buf_alloc(unsigned int size)
if (size == 0)
return NULL;
- cb = (struct cypress_buf *)kmalloc(sizeof(struct cypress_buf), GFP_KERNEL);
+ cb = kmalloc(sizeof(struct cypress_buf), GFP_KERNEL);
if (cb == NULL)
return NULL;
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 9d9ea87..efd9ce3 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1681,7 +1681,7 @@ dbg( "digi_startup: TOP" );
for( i=0; i<serial->type->num_ports+1; i++ ) {
/* allocate port private structure */
- priv = (struct digi_port *)kmalloc( sizeof(struct digi_port),
+ priv = kmalloc( sizeof(struct digi_port),
GFP_KERNEL );
if( priv == (struct digi_port *)0 ) {
while( --i >= 0 )
@@ -1714,7 +1714,7 @@ dbg( "digi_startup: TOP" );
}
/* allocate serial private structure */
- serial_priv = (struct digi_serial *)kmalloc( sizeof(struct digi_serial),
+ serial_priv = kmalloc( sizeof(struct digi_serial),
GFP_KERNEL );
if( serial_priv == (struct digi_serial *)0 ) {
for( i=0; i<serial->type->num_ports+1; i++ )
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 2da2684..980285c 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2811,7 +2811,7 @@ static struct edge_buf *edge_buf_alloc(unsigned int size)
if (size == 0)
return NULL;
- eb = (struct edge_buf *)kmalloc(sizeof(struct edge_buf), GFP_KERNEL);
+ eb = kmalloc(sizeof(struct edge_buf), GFP_KERNEL);
if (eb == NULL)
return NULL;
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index d72cf8b..42f757a 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -595,7 +595,7 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
bytes_in = 0;
bytes_out = 0;
- priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
+ priv = kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
if (priv == NULL) {
err("%s - Out of memory", __FUNCTION__);
return -ENOMEM;
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index e284d6c..62bea0c 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -269,7 +269,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp)
}
// allocate memory for write_urb transfer buffer
- port->write_urb->transfer_buffer = (unsigned char *) kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
+ port->write_urb->transfer_buffer = kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
if (! port->write_urb->transfer_buffer) {
kfree(transfer_buffer);
usb_free_urb(port->write_urb);
@@ -696,7 +696,7 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file *file,
return 0;
case TCFLSH: // 0x540B
- transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+ transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
if (! transfer_buffer) {
return -ENOBUFS;
}
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index d124d78..5dc2ac9 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -159,7 +159,7 @@ static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
if (size == 0)
return NULL;
- pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+ pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
if (pb == NULL)
return NULL;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index f42eb9e..8318900 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -1710,7 +1710,7 @@ static struct circ_buf *ti_buf_alloc(void)
{
struct circ_buf *cb;
- cb = (struct circ_buf *)kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+ cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
if (cb == NULL)
return NULL;
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index dc45e58..5483d85 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -416,7 +416,7 @@ static int whiteheat_attach (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
- info = (struct whiteheat_private *)kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
+ info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
if (info == NULL) {
err("%s: Out of memory for port structures\n", serial->type->description);
goto no_private;
@@ -487,7 +487,7 @@ static int whiteheat_attach (struct usb_serial *serial)
usb_set_serial_port_data(port, info);
}
- command_info = (struct whiteheat_command_private *)kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL);
+ command_info = kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL);
if (command_info == NULL) {
err("%s: Out of memory for port structures\n", serial->type->description);
goto no_command_private;
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index fb8baca..e3528ec 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -646,7 +646,7 @@ sddr09_read_sg_test_only(struct us_data *us) {
return result;
}
- buf = (unsigned char *) kmalloc(bulklen, GFP_NOIO);
+ buf = kmalloc(bulklen, GFP_NOIO);
if (!buf)
return -ENOMEM;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ab1daec..4e83f01 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1615,6 +1615,16 @@ config FB_PNX4008_DUM_RGB
---help---
Say Y here to enable support for PNX4008 RGB Framebuffer
+config FB_IBM_GXT4500
+ tristate "Framebuffer support for IBM GXT4500P adaptor"
+ depends on PPC
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Say Y here to enable support for the IBM GXT4500P display
+ adaptor, found on some IBM System P (pSeries) machines.
+
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index a6980e9..309a26d 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_FB_IMX) += imxfb.o
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
+obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_VESA) += vesafb.o
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 6761b68..6c9dc2e 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -447,7 +447,7 @@ static int clcdfb_probe(struct amba_device *dev, void *id)
goto out;
}
- fb = (struct clcd_fb *) kmalloc(sizeof(struct clcd_fb), GFP_KERNEL);
+ fb = kmalloc(sizeof(struct clcd_fb), GFP_KERNEL);
if (!fb) {
printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n");
ret = -ENOMEM;
diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c
index 88a4784..1a849b8 100644
--- a/drivers/video/amifb.c
+++ b/drivers/video/amifb.c
@@ -2906,14 +2906,6 @@ static int ami_decode_var(struct fb_var_screeninfo *var,
par->crsr.spot_x = par->crsr.spot_y = 0;
par->crsr.height = par->crsr.width = 0;
-#if 0 /* fbmon not done. uncomment for 2.5.x -brad */
- if (!fbmon_valid_timings(pixclock[clk_shift], htotal, vtotal,
- &fb_info)) {
- DPRINTK("mode doesn't fit for monitor\n");
- return -EINVAL;
- }
-#endif
-
return 0;
}
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 176f9b8..09684d7 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -1488,10 +1488,6 @@ static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
else
info->var.accel_flags = 0;
-#if 0 /* fbmon is not done. uncomment for 2.5.x -brad */
- if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
- return -EINVAL;
-#endif
aty_crtc_to_var(&crtc, var);
var->pixclock = par->pll_ops->pll_to_var(info, &pll);
return 0;
diff --git a/drivers/video/aty/radeon_i2c.c b/drivers/video/aty/radeon_i2c.c
index 869725a..e7c5b21 100644
--- a/drivers/video/aty/radeon_i2c.c
+++ b/drivers/video/aty/radeon_i2c.c
@@ -120,19 +120,19 @@ void radeon_create_i2c_busses(struct radeonfb_info *rinfo)
void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
{
if (rinfo->i2c[0].rinfo)
- i2c_bit_del_bus(&rinfo->i2c[0].adapter);
+ i2c_del_adapter(&rinfo->i2c[0].adapter);
rinfo->i2c[0].rinfo = NULL;
if (rinfo->i2c[1].rinfo)
- i2c_bit_del_bus(&rinfo->i2c[1].adapter);
+ i2c_del_adapter(&rinfo->i2c[1].adapter);
rinfo->i2c[1].rinfo = NULL;
if (rinfo->i2c[2].rinfo)
- i2c_bit_del_bus(&rinfo->i2c[2].adapter);
+ i2c_del_adapter(&rinfo->i2c[2].adapter);
rinfo->i2c[2].rinfo = NULL;
if (rinfo->i2c[3].rinfo)
- i2c_bit_del_bus(&rinfo->i2c[3].adapter);
+ i2c_del_adapter(&rinfo->i2c[3].adapter);
rinfo->i2c[3].rinfo = NULL;
}
diff --git a/drivers/video/gxt4500.c b/drivers/video/gxt4500.c
new file mode 100644
index 0000000..3adf6ab
--- /dev/null
+++ b/drivers/video/gxt4500.c
@@ -0,0 +1,741 @@
+/*
+ * Frame buffer device for IBM GXT4500P display adaptor
+ *
+ * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@samba.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/delay.h>
+
+#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c
+
+/* GXT4500P registers */
+
+/* Registers in PCI config space */
+#define CFG_ENDIAN0 0x40
+
+/* Misc control/status registers */
+#define STATUS 0x1000
+#define CTRL_REG0 0x1004
+#define CR0_HALT_DMA 0x4
+#define CR0_RASTER_RESET 0x8
+#define CR0_GEOM_RESET 0x10
+#define CR0_MEM_CTRLER_RESET 0x20
+
+/* Framebuffer control registers */
+#define FB_AB_CTRL 0x1100
+#define FB_CD_CTRL 0x1104
+#define FB_WID_CTRL 0x1108
+#define FB_Z_CTRL 0x110c
+#define FB_VGA_CTRL 0x1110
+#define REFRESH_AB_CTRL 0x1114
+#define REFRESH_CD_CTRL 0x1118
+#define FB_OVL_CTRL 0x111c
+#define FB_CTRL_TYPE 0x80000000
+#define FB_CTRL_WIDTH_MASK 0x007f0000
+#define FB_CTRL_WIDTH_SHIFT 16
+#define FB_CTRL_START_SEG_MASK 0x00003fff
+
+#define REFRESH_START 0x1098
+#define REFRESH_SIZE 0x109c
+
+/* "Direct" framebuffer access registers */
+#define DFA_FB_A 0x11e0
+#define DFA_FB_B 0x11e4
+#define DFA_FB_C 0x11e8
+#define DFA_FB_D 0x11ec
+#define DFA_FB_ENABLE 0x80000000
+#define DFA_FB_BASE_MASK 0x03f00000
+#define DFA_FB_STRIDE_1k 0x00000000
+#define DFA_FB_STRIDE_2k 0x00000010
+#define DFA_FB_STRIDE_4k 0x00000020
+#define DFA_PIX_8BIT 0x00000000
+#define DFA_PIX_16BIT_565 0x00000001
+#define DFA_PIX_16BIT_1555 0x00000002
+#define DFA_PIX_24BIT 0x00000004
+#define DFA_PIX_32BIT 0x00000005
+
+/* maps DFA_PIX_* to pixel size in bytes */
+static const unsigned char pixsize[] = {
+ 1, 2, 2, 2, 4, 4
+};
+
+/* Display timing generator registers */
+#define DTG_CONTROL 0x1900
+#define DTG_CTL_SCREEN_REFRESH 2
+#define DTG_CTL_ENABLE 1
+#define DTG_HORIZ_EXTENT 0x1904
+#define DTG_HORIZ_DISPLAY 0x1908
+#define DTG_HSYNC_START 0x190c
+#define DTG_HSYNC_END 0x1910
+#define DTG_HSYNC_END_COMP 0x1914
+#define DTG_VERT_EXTENT 0x1918
+#define DTG_VERT_DISPLAY 0x191c
+#define DTG_VSYNC_START 0x1920
+#define DTG_VSYNC_END 0x1924
+#define DTG_VERT_SHORT 0x1928
+
+/* PLL/RAMDAC registers */
+#define DISP_CTL 0x402c
+#define DISP_CTL_OFF 2
+#define SYNC_CTL 0x4034
+#define SYNC_CTL_SYNC_ON_RGB 1
+#define SYNC_CTL_SYNC_OFF 2
+#define SYNC_CTL_HSYNC_INV 8
+#define SYNC_CTL_VSYNC_INV 0x10
+#define SYNC_CTL_HSYNC_OFF 0x20
+#define SYNC_CTL_VSYNC_OFF 0x40
+
+#define PLL_M 0x4040
+#define PLL_N 0x4044
+#define PLL_POSTDIV 0x4048
+
+/* Hardware cursor */
+#define CURSOR_X 0x4078
+#define CURSOR_Y 0x407c
+#define CURSOR_HOTSPOT 0x4080
+#define CURSOR_MODE 0x4084
+#define CURSOR_MODE_OFF 0
+#define CURSOR_MODE_4BPP 1
+#define CURSOR_PIXMAP 0x5000
+#define CURSOR_CMAP 0x7400
+
+/* Window attribute table */
+#define WAT_FMT 0x4100
+#define WAT_FMT_24BIT 0
+#define WAT_FMT_16BIT_565 1
+#define WAT_FMT_16BIT_1555 2
+#define WAT_FMT_32BIT 3 /* 0 vs. 3 is a guess */
+#define WAT_FMT_8BIT_332 9
+#define WAT_FMT_8BIT 0xa
+#define WAT_FMT_NO_CMAP 4 /* ORd in to other values */
+#define WAT_CMAP_OFFSET 0x4104 /* 4-bit value gets << 6 */
+#define WAT_CTRL 0x4108
+#define WAT_CTRL_SEL_B 1 /* select B buffer if 1 */
+#define WAT_CTRL_NO_INC 2
+#define WAT_GAMMA_CTRL 0x410c
+#define WAT_GAMMA_DISABLE 1 /* disables gamma cmap */
+#define WAT_OVL_CTRL 0x430c /* controls overlay */
+
+/* Indexed by DFA_PIX_* values */
+static const unsigned char watfmt[] = {
+ WAT_FMT_8BIT, WAT_FMT_16BIT_565, WAT_FMT_16BIT_1555, 0,
+ WAT_FMT_24BIT, WAT_FMT_32BIT
+};
+
+/* Colormap array; 1k entries of 4 bytes each */
+#define CMAP 0x6000
+
+#define readreg(par, reg) readl((par)->regs + (reg))
+#define writereg(par, reg, val) writel((val), (par)->regs + (reg))
+
+struct gxt4500_par {
+ void __iomem *regs;
+
+ int pixfmt; /* pixel format, see DFA_PIX_* values */
+
+ /* PLL parameters */
+ int pll_m; /* ref clock divisor */
+ int pll_n; /* VCO divisor */
+ int pll_pd1; /* first post-divisor */
+ int pll_pd2; /* second post-divisor */
+
+ u32 pseudo_palette[16]; /* used in color blits */
+};
+
+/* mode requested by user */
+static char *mode_option;
+
+/* default mode: 1280x1024 @ 60 Hz, 8 bpp */
+static const struct fb_videomode defaultmode __devinitdata = {
+ .refresh = 60,
+ .xres = 1280,
+ .yres = 1024,
+ .pixclock = 9295,
+ .left_margin = 248,
+ .right_margin = 48,
+ .upper_margin = 38,
+ .lower_margin = 1,
+ .hsync_len = 112,
+ .vsync_len = 3,
+ .vmode = FB_VMODE_NONINTERLACED
+};
+
+/*
+ * The refclk and VCO dividers appear to use a linear feedback shift
+ * register, which gets reloaded when it reaches a terminal value, at
+ * which point the divider output is toggled. Thus one can obtain
+ * whatever divisor is required by putting the appropriate value into
+ * the reload register. For a divisor of N, one puts the value from
+ * the LFSR sequence that comes N-1 places before the terminal value
+ * into the reload register.
+ */
+
+static const unsigned char mdivtab[] = {
+/* 1 */ 0x3f, 0x00, 0x20, 0x10, 0x28, 0x14, 0x2a, 0x15, 0x0a,
+/* 10 */ 0x25, 0x32, 0x19, 0x0c, 0x26, 0x13, 0x09, 0x04, 0x22, 0x11,
+/* 20 */ 0x08, 0x24, 0x12, 0x29, 0x34, 0x1a, 0x2d, 0x36, 0x1b, 0x0d,
+/* 30 */ 0x06, 0x23, 0x31, 0x38, 0x1c, 0x2e, 0x17, 0x0b, 0x05, 0x02,
+/* 40 */ 0x21, 0x30, 0x18, 0x2c, 0x16, 0x2b, 0x35, 0x3a, 0x1d, 0x0e,
+/* 50 */ 0x27, 0x33, 0x39, 0x3c, 0x1e, 0x2f, 0x37, 0x3b, 0x3d, 0x3e,
+/* 60 */ 0x1f, 0x0f, 0x07, 0x03, 0x01,
+};
+
+static const unsigned char ndivtab[] = {
+/* 2 */ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x78, 0xbc, 0x5e,
+/* 10 */ 0x2f, 0x17, 0x0b, 0x85, 0xc2, 0xe1, 0x70, 0x38, 0x9c, 0x4e,
+/* 20 */ 0xa7, 0xd3, 0xe9, 0xf4, 0xfa, 0xfd, 0xfe, 0x7f, 0xbf, 0xdf,
+/* 30 */ 0xef, 0x77, 0x3b, 0x1d, 0x8e, 0xc7, 0xe3, 0x71, 0xb8, 0xdc,
+/* 40 */ 0x6e, 0xb7, 0x5b, 0x2d, 0x16, 0x8b, 0xc5, 0xe2, 0xf1, 0xf8,
+/* 50 */ 0xfc, 0x7e, 0x3f, 0x9f, 0xcf, 0x67, 0xb3, 0xd9, 0x6c, 0xb6,
+/* 60 */ 0xdb, 0x6d, 0x36, 0x9b, 0x4d, 0x26, 0x13, 0x89, 0xc4, 0x62,
+/* 70 */ 0xb1, 0xd8, 0xec, 0xf6, 0xfb, 0x7d, 0xbe, 0x5f, 0xaf, 0x57,
+/* 80 */ 0x2b, 0x95, 0x4a, 0x25, 0x92, 0x49, 0xa4, 0x52, 0x29, 0x94,
+/* 90 */ 0xca, 0x65, 0xb2, 0x59, 0x2c, 0x96, 0xcb, 0xe5, 0xf2, 0x79,
+/* 100 */ 0x3c, 0x1e, 0x0f, 0x07, 0x83, 0x41, 0x20, 0x90, 0x48, 0x24,
+/* 110 */ 0x12, 0x09, 0x84, 0x42, 0xa1, 0x50, 0x28, 0x14, 0x8a, 0x45,
+/* 120 */ 0xa2, 0xd1, 0xe8, 0x74, 0xba, 0xdd, 0xee, 0xf7, 0x7b, 0x3d,
+/* 130 */ 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e,
+/* 140 */ 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98,
+/* 150 */ 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2,
+/* 160 */ 0x69, 0xb4, 0xda, 0xed, 0x76, 0xbb, 0x5d, 0xae, 0xd7, 0x6b,
+/* 170 */ 0xb5, 0x5a, 0xad, 0x56, 0xab, 0xd5, 0x6a, 0x35, 0x1a, 0x8d,
+/* 180 */ 0x46, 0x23, 0x11, 0x88, 0x44, 0x22, 0x91, 0xc8, 0x64, 0x32,
+/* 190 */ 0x19, 0x0c, 0x86, 0x43, 0x21, 0x10, 0x08, 0x04, 0x02, 0x81,
+/* 200 */ 0x40, 0xa0, 0xd0, 0x68, 0x34, 0x9a, 0xcd, 0x66, 0x33, 0x99,
+/* 210 */ 0x4c, 0xa6, 0x53, 0xa9, 0xd4, 0xea, 0x75, 0x3a, 0x9d, 0xce,
+/* 220 */ 0xe7, 0xf3, 0xf9, 0x7c, 0x3e, 0x1f, 0x8f, 0x47, 0xa3, 0x51,
+/* 230 */ 0xa8, 0x54, 0xaa, 0x55, 0x2a, 0x15, 0x0a, 0x05, 0x82, 0xc1,
+/* 240 */ 0x60, 0xb0, 0x58, 0xac, 0xd6, 0xeb, 0xf5, 0x7a, 0xbd, 0xde,
+/* 250 */ 0x6f, 0x37, 0x1b, 0x0d, 0x06, 0x03, 0x01,
+};
+
+#define REF_PERIOD_PS 9259 /* period of reference clock in ps */
+
+static int calc_pll(int period_ps, struct gxt4500_par *par)
+{
+ int m, n, pdiv1, pdiv2, postdiv;
+ int pll_period, best_error, t;
+
+ /* only deal with range 1MHz - 400MHz */
+ if (period_ps < 2500 || period_ps > 1000000)
+ return -1;
+
+ best_error = 1000000;
+ for (pdiv1 = 1; pdiv1 <= 8; ++pdiv1) {
+ for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) {
+ postdiv = pdiv1 * pdiv2;
+ pll_period = (period_ps + postdiv - 1) / postdiv;
+ /* keep pll in range 500..1250 MHz */
+ if (pll_period < 800 || pll_period > 2000)
+ continue;
+ for (m = 3; m <= 40; ++m) {
+ n = REF_PERIOD_PS * m * postdiv / period_ps;
+ if (n < 5 || n > 256)
+ continue;
+ t = REF_PERIOD_PS * m * postdiv / n;
+ t -= period_ps;
+ if (t >= 0 && t < best_error) {
+ par->pll_m = m;
+ par->pll_n = n;
+ par->pll_pd1 = pdiv1;
+ par->pll_pd2 = pdiv2;
+ best_error = t;
+ }
+ }
+ }
+ }
+ if (best_error == 1000000)
+ return -1;
+ return 0;
+}
+
+static int calc_pixclock(struct gxt4500_par *par)
+{
+ return REF_PERIOD_PS * par->pll_m * par->pll_pd1 * par->pll_pd2
+ / par->pll_n;
+}
+
+static int gxt4500_var_to_par(struct fb_var_screeninfo *var,
+ struct gxt4500_par *par)
+{
+ if (var->xres + var->xoffset > var->xres_virtual ||
+ var->yres + var->yoffset > var->yres_virtual ||
+ var->xres_virtual > 4096)
+ return -EINVAL;
+ if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+
+ if (calc_pll(var->pixclock, par) < 0)
+ return -EINVAL;
+
+ switch (var->bits_per_pixel) {
+ case 32:
+ if (var->transp.length)
+ par->pixfmt = DFA_PIX_32BIT;
+ else
+ par->pixfmt = DFA_PIX_24BIT;
+ break;
+ case 24:
+ par->pixfmt = DFA_PIX_24BIT;
+ break;
+ case 16:
+ if (var->green.length == 5)
+ par->pixfmt = DFA_PIX_16BIT_1555;
+ else
+ par->pixfmt = DFA_PIX_16BIT_565;
+ break;
+ case 8:
+ par->pixfmt = DFA_PIX_8BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct fb_bitfield eightbits = {0, 8};
+static const struct fb_bitfield nobits = {0, 0};
+
+static void gxt4500_unpack_pixfmt(struct fb_var_screeninfo *var,
+ int pixfmt)
+{
+ var->bits_per_pixel = pixsize[pixfmt] * 8;
+ var->red = eightbits;
+ var->green = eightbits;
+ var->blue = eightbits;
+ var->transp = nobits;
+
+ switch (pixfmt) {
+ case DFA_PIX_16BIT_565:
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ break;
+ case DFA_PIX_16BIT_1555:
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ break;
+ case DFA_PIX_32BIT:
+ var->transp.length = 8;
+ break;
+ }
+ if (pixfmt != DFA_PIX_8BIT) {
+ var->green.offset = var->red.length;
+ var->blue.offset = var->green.offset + var->green.length;
+ if (var->transp.length)
+ var->transp.offset =
+ var->blue.offset + var->blue.length;
+ }
+}
+
+static int gxt4500_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct gxt4500_par par;
+ int err;
+
+ par = *(struct gxt4500_par *)info->par;
+ err = gxt4500_var_to_par(var, &par);
+ if (!err) {
+ var->pixclock = calc_pixclock(&par);
+ gxt4500_unpack_pixfmt(var, par.pixfmt);
+ }
+ return err;
+}
+
+static int gxt4500_set_par(struct fb_info *info)
+{
+ struct gxt4500_par *par = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int err;
+ u32 ctrlreg;
+ unsigned int dfa_ctl, pixfmt, stride;
+ unsigned int wid_tiles, i;
+ unsigned int prefetch_pix, htot;
+ struct gxt4500_par save_par;
+
+ save_par = *par;
+ err = gxt4500_var_to_par(var, par);
+ if (err) {
+ *par = save_par;
+ return err;
+ }
+
+ /* turn off DTG for now */
+ ctrlreg = readreg(par, DTG_CONTROL);
+ ctrlreg &= ~(DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH);
+ writereg(par, DTG_CONTROL, ctrlreg);
+
+ /* set PLL registers */
+ writereg(par, PLL_M, mdivtab[par->pll_m - 1]);
+ writereg(par, PLL_N, ndivtab[par->pll_n - 2]);
+ writereg(par, PLL_POSTDIV,
+ ((8 - par->pll_pd1) << 3) | (8 - par->pll_pd2));
+ msleep(20);
+
+ /* turn off hardware cursor */
+ writereg(par, CURSOR_MODE, CURSOR_MODE_OFF);
+
+ /* reset raster engine */
+ writereg(par, CTRL_REG0, CR0_RASTER_RESET | (CR0_RASTER_RESET << 16));
+ udelay(10);
+ writereg(par, CTRL_REG0, CR0_RASTER_RESET << 16);
+
+ /* set display timing generator registers */
+ htot = var->xres + var->left_margin + var->right_margin +
+ var->hsync_len;
+ writereg(par, DTG_HORIZ_EXTENT, htot - 1);
+ writereg(par, DTG_HORIZ_DISPLAY, var->xres - 1);
+ writereg(par, DTG_HSYNC_START, var->xres + var->right_margin - 1);
+ writereg(par, DTG_HSYNC_END,
+ var->xres + var->right_margin + var->hsync_len - 1);
+ writereg(par, DTG_HSYNC_END_COMP,
+ var->xres + var->right_margin + var->hsync_len - 1);
+ writereg(par, DTG_VERT_EXTENT,
+ var->yres + var->upper_margin + var->lower_margin +
+ var->vsync_len - 1);
+ writereg(par, DTG_VERT_DISPLAY, var->yres - 1);
+ writereg(par, DTG_VSYNC_START, var->yres + var->lower_margin - 1);
+ writereg(par, DTG_VSYNC_END,
+ var->yres + var->lower_margin + var->vsync_len - 1);
+ prefetch_pix = 3300000 / var->pixclock;
+ if (prefetch_pix >= htot)
+ prefetch_pix = htot - 1;
+ writereg(par, DTG_VERT_SHORT, htot - prefetch_pix - 1);
+ ctrlreg |= DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH;
+ writereg(par, DTG_CONTROL, ctrlreg);
+
+ /* calculate stride in DFA aperture */
+ if (var->xres_virtual > 2048) {
+ stride = 4096;
+ dfa_ctl = DFA_FB_STRIDE_4k;
+ } else if (var->xres_virtual > 1024) {
+ stride = 2048;
+ dfa_ctl = DFA_FB_STRIDE_2k;
+ } else {
+ stride = 1024;
+ dfa_ctl = DFA_FB_STRIDE_1k;
+ }
+
+ /* Set up framebuffer definition */
+ wid_tiles = (var->xres_virtual + 63) >> 6;
+
+ /* XXX add proper FB allocation here someday */
+ writereg(par, FB_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
+ writereg(par, REFRESH_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
+ writereg(par, FB_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
+ writereg(par, REFRESH_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
+ writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset);
+ writereg(par, REFRESH_SIZE, (var->xres << 16) | var->yres);
+
+ /* Set up framebuffer access by CPU */
+
+ pixfmt = par->pixfmt;
+ dfa_ctl |= DFA_FB_ENABLE | pixfmt;
+ writereg(par, DFA_FB_A, dfa_ctl);
+
+ /*
+ * Set up window attribute table.
+ * We set all WAT entries the same so it doesn't matter what the
+ * window ID (WID) plane contains.
+ */
+ for (i = 0; i < 32; ++i) {
+ writereg(par, WAT_FMT + (i << 4), watfmt[pixfmt]);
+ writereg(par, WAT_CMAP_OFFSET + (i << 4), 0);
+ writereg(par, WAT_CTRL + (i << 4), 0);
+ writereg(par, WAT_GAMMA_CTRL + (i << 4), WAT_GAMMA_DISABLE);
+ }
+
+ /* Set sync polarity etc. */
+ ctrlreg = readreg(par, SYNC_CTL) &
+ ~(SYNC_CTL_SYNC_ON_RGB | SYNC_CTL_HSYNC_INV |
+ SYNC_CTL_VSYNC_INV);
+ if (var->sync & FB_SYNC_ON_GREEN)
+ ctrlreg |= SYNC_CTL_SYNC_ON_RGB;
+ if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
+ ctrlreg |= SYNC_CTL_HSYNC_INV;
+ if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
+ ctrlreg |= SYNC_CTL_VSYNC_INV;
+ writereg(par, SYNC_CTL, ctrlreg);
+
+ info->fix.line_length = stride * pixsize[pixfmt];
+ info->fix.visual = (pixfmt == DFA_PIX_8BIT)? FB_VISUAL_PSEUDOCOLOR:
+ FB_VISUAL_DIRECTCOLOR;
+
+ return 0;
+}
+
+static int gxt4500_setcolreg(unsigned int reg, unsigned int red,
+ unsigned int green, unsigned int blue,
+ unsigned int transp, struct fb_info *info)
+{
+ u32 cmap_entry;
+ struct gxt4500_par *par = info->par;
+
+ if (reg > 1023)
+ return 1;
+ cmap_entry = ((transp & 0xff00) << 16) | ((blue & 0xff00) << 8) |
+ (green & 0xff00) | (red >> 8);
+ writereg(par, CMAP + reg * 4, cmap_entry);
+
+ if (reg < 16 && par->pixfmt != DFA_PIX_8BIT) {
+ u32 *pal = info->pseudo_palette;
+ u32 val = reg;
+ switch (par->pixfmt) {
+ case DFA_PIX_16BIT_565:
+ val |= (reg << 11) | (reg << 6);
+ break;
+ case DFA_PIX_16BIT_1555:
+ val |= (reg << 10) | (reg << 5);
+ break;
+ case DFA_PIX_32BIT:
+ val |= (reg << 24);
+ /* fall through */
+ case DFA_PIX_24BIT:
+ val |= (reg << 16) | (reg << 8);
+ break;
+ }
+ pal[reg] = val;
+ }
+
+ return 0;
+}
+
+static int gxt4500_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct gxt4500_par *par = info->par;
+
+ if (var->xoffset & 7)
+ return -EINVAL;
+ if (var->xoffset + var->xres > var->xres_virtual ||
+ var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+
+ writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset);
+ return 0;
+}
+
+static int gxt4500_blank(int blank, struct fb_info *info)
+{
+ struct gxt4500_par *par = info->par;
+ int ctrl, dctl;
+
+ ctrl = readreg(par, SYNC_CTL);
+ ctrl &= ~(SYNC_CTL_SYNC_OFF | SYNC_CTL_HSYNC_OFF | SYNC_CTL_VSYNC_OFF);
+ dctl = readreg(par, DISP_CTL);
+ dctl |= DISP_CTL_OFF;
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ dctl &= ~DISP_CTL_OFF;
+ break;
+ case FB_BLANK_POWERDOWN:
+ ctrl |= SYNC_CTL_SYNC_OFF;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ ctrl |= SYNC_CTL_HSYNC_OFF;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ ctrl |= SYNC_CTL_VSYNC_OFF;
+ break;
+ default: ;
+ }
+ writereg(par, SYNC_CTL, ctrl);
+ writereg(par, DISP_CTL, dctl);
+
+ return 0;
+}
+
+static const struct fb_fix_screeninfo gxt4500_fix __devinitdata = {
+ .id = "IBM GXT4500P",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_PSEUDOCOLOR,
+ .xpanstep = 8,
+ .ypanstep = 1,
+ .mmio_len = 0x20000,
+};
+
+static struct fb_ops gxt4500_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = gxt4500_check_var,
+ .fb_set_par = gxt4500_set_par,
+ .fb_setcolreg = gxt4500_setcolreg,
+ .fb_pan_display = gxt4500_pan_display,
+ .fb_blank = gxt4500_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/* PCI functions */
+static int __devinit gxt4500_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ unsigned long reg_phys, fb_phys;
+ struct gxt4500_par *par;
+ struct fb_info *info;
+ struct fb_var_screeninfo var;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "gxt4500: cannot enable PCI device: %d\n",
+ err);
+ return err;
+ }
+
+ reg_phys = pci_resource_start(pdev, 0);
+ if (!request_mem_region(reg_phys, pci_resource_len(pdev, 0),
+ "gxt4500 regs")) {
+ dev_err(&pdev->dev, "gxt4500: cannot get registers\n");
+ goto err_nodev;
+ }
+
+ fb_phys = pci_resource_start(pdev, 1);
+ if (!request_mem_region(fb_phys, pci_resource_len(pdev, 1),
+ "gxt4500 FB")) {
+ dev_err(&pdev->dev, "gxt4500: cannot get framebuffer\n");
+ goto err_free_regs;
+ }
+
+ info = framebuffer_alloc(sizeof(struct gxt4500_par), &pdev->dev);
+ if (!info) {
+ dev_err(&pdev->dev, "gxt4500: cannot alloc FB info record");
+ goto err_free_fb;
+ }
+ par = info->par;
+ info->fix = gxt4500_fix;
+ info->pseudo_palette = par->pseudo_palette;
+
+ info->fix.mmio_start = reg_phys;
+ par->regs = ioremap(reg_phys, pci_resource_len(pdev, 0));
+ if (!par->regs) {
+ dev_err(&pdev->dev, "gxt4500: cannot map registers\n");
+ goto err_free_all;
+ }
+
+ info->fix.smem_start = fb_phys;
+ info->fix.smem_len = pci_resource_len(pdev, 1);
+ info->screen_base = ioremap(fb_phys, pci_resource_len(pdev, 1));
+ if (!info->screen_base) {
+ dev_err(&pdev->dev, "gxt4500: cannot map framebuffer\n");
+ goto err_unmap_regs;
+ }
+
+ pci_set_drvdata(pdev, info);
+
+ /* Set byte-swapping for DFA aperture for all pixel sizes */
+ pci_write_config_dword(pdev, CFG_ENDIAN0, 0x333300);
+
+ info->fbops = &gxt4500_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ err = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (err) {
+ dev_err(&pdev->dev, "gxt4500: cannot allocate cmap\n");
+ goto err_unmap_all;
+ }
+
+ gxt4500_blank(FB_BLANK_UNBLANK, info);
+
+ if (!fb_find_mode(&var, info, mode_option, NULL, 0, &defaultmode, 8)) {
+ dev_err(&pdev->dev, "gxt4500: cannot find valid video mode\n");
+ goto err_free_cmap;
+ }
+ info->var = var;
+ if (gxt4500_set_par(info)) {
+ printk(KERN_ERR "gxt4500: cannot set video mode\n");
+ goto err_free_cmap;
+ }
+
+ if (register_framebuffer(info) < 0) {
+ dev_err(&pdev->dev, "gxt4500: cannot register framebuffer\n");
+ goto err_free_cmap;
+ }
+ printk(KERN_INFO "fb%d: %s frame buffer device\n",
+ info->node, info->fix.id);
+
+ return 0;
+
+ err_free_cmap:
+ fb_dealloc_cmap(&info->cmap);
+ err_unmap_all:
+ iounmap(info->screen_base);
+ err_unmap_regs:
+ iounmap(par->regs);
+ err_free_all:
+ framebuffer_release(info);
+ err_free_fb:
+ release_mem_region(fb_phys, pci_resource_len(pdev, 1));
+ err_free_regs:
+ release_mem_region(reg_phys, pci_resource_len(pdev, 0));
+ err_nodev:
+ return -ENODEV;
+}
+
+static void __devexit gxt4500_remove(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct gxt4500_par *par;
+
+ if (!info)
+ return;
+ par = info->par;
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ iounmap(par->regs);
+ iounmap(info->screen_base);
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_resource_len(pdev, 1));
+ framebuffer_release(info);
+}
+
+/* supported chipsets */
+static const struct pci_device_id gxt4500_pci_tbl[] = {
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4500P,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, gxt4500_pci_tbl);
+
+static struct pci_driver gxt4500_driver = {
+ .name = "gxt4500",
+ .id_table = gxt4500_pci_tbl,
+ .probe = gxt4500_probe,
+ .remove = __devexit_p(gxt4500_remove),
+};
+
+static int __devinit gxt4500_init(void)
+{
+#ifndef MODULE
+ if (fb_get_options("gxt4500", &mode_option))
+ return -ENODEV;
+#endif
+
+ return pci_register_driver(&gxt4500_driver);
+}
+module_init(gxt4500_init);
+
+static void __exit gxt4500_exit(void)
+{
+ pci_unregister_driver(&gxt4500_driver);
+}
+module_exit(gxt4500_exit);
+
+MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
+MODULE_DESCRIPTION("FBDev driver for IBM GXT4500P");
+MODULE_LICENSE("GPL");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");
diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c
index b952e45..961f4d4 100644
--- a/drivers/video/i810/i810-i2c.c
+++ b/drivers/video/i810/i810-i2c.c
@@ -137,15 +137,15 @@ void i810_create_i2c_busses(struct i810fb_par *par)
void i810_delete_i2c_busses(struct i810fb_par *par)
{
if (par->chan[0].par)
- i2c_bit_del_bus(&par->chan[0].adapter);
+ i2c_del_adapter(&par->chan[0].adapter);
par->chan[0].par = NULL;
if (par->chan[1].par)
- i2c_bit_del_bus(&par->chan[1].adapter);
+ i2c_del_adapter(&par->chan[1].adapter);
par->chan[1].par = NULL;
if (par->chan[2].par)
- i2c_bit_del_bus(&par->chan[2].adapter);
+ i2c_del_adapter(&par->chan[2].adapter);
par->chan[2].par = NULL;
}
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c
index 5686e21..33bc41f 100644
--- a/drivers/video/intelfb/intelfb_i2c.c
+++ b/drivers/video/intelfb/intelfb_i2c.c
@@ -188,11 +188,11 @@ void intelfb_delete_i2c_busses(struct intelfb_info *dinfo)
for (i = 0; i < MAX_OUTPUTS; i++) {
if (dinfo->output[i].i2c_bus.dinfo) {
- i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter);
+ i2c_del_adapter(&dinfo->output[i].i2c_bus.adapter);
dinfo->output[i].i2c_bus.dinfo = NULL;
}
if (dinfo->output[i].ddc_bus.dinfo) {
- i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter);
+ i2c_del_adapter(&dinfo->output[i].ddc_bus.adapter);
dinfo->output[i].ddc_bus.dinfo = NULL;
}
}
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c
index 795c1a9..fe28848 100644
--- a/drivers/video/matrox/i2c-matroxfb.c
+++ b/drivers/video/matrox/i2c-matroxfb.c
@@ -124,7 +124,7 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
static void i2c_bit_bus_del(struct i2c_bit_adapter* b) {
if (b->initialized) {
- i2c_bit_del_bus(&b->adapter);
+ i2c_del_adapter(&b->adapter);
b->initialized = 0;
}
}
@@ -146,7 +146,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) {
unsigned long flags;
struct matroxfb_dh_maven_info* m2info;
- m2info = (struct matroxfb_dh_maven_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
+ m2info = kmalloc(sizeof(*m2info), GFP_KERNEL);
if (!m2info)
return NULL;
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index e9b4115..cb2aa40 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -2028,7 +2028,7 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm
}
#ifdef CONFIG_FB_MATROX_MULTIHEAD
- minfo = (struct matrox_fb_info*)kmalloc(sizeof(*minfo), GFP_KERNEL);
+ minfo = kmalloc(sizeof(*minfo), GFP_KERNEL);
if (!minfo)
return -1;
#else
diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c
index 27eb4bb..2c98010 100644
--- a/drivers/video/matrox/matroxfb_crtc2.c
+++ b/drivers/video/matrox/matroxfb_crtc2.c
@@ -694,7 +694,7 @@ static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
/* hardware is CRTC2 incapable... */
if (!ACCESS_FBINFO(devflags.crtc2))
return NULL;
- m2info = (struct matroxfb_dh_fb_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
+ m2info = kmalloc(sizeof(*m2info), GFP_KERNEL);
if (!m2info) {
printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
return NULL;
diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c
index 442e853..8454adf 100644
--- a/drivers/video/nvidia/nv_i2c.c
+++ b/drivers/video/nvidia/nv_i2c.c
@@ -147,15 +147,15 @@ void nvidia_create_i2c_busses(struct nvidia_par *par)
void nvidia_delete_i2c_busses(struct nvidia_par *par)
{
if (par->chan[0].par)
- i2c_bit_del_bus(&par->chan[0].adapter);
+ i2c_del_adapter(&par->chan[0].adapter);
par->chan[0].par = NULL;
if (par->chan[1].par)
- i2c_bit_del_bus(&par->chan[1].adapter);
+ i2c_del_adapter(&par->chan[1].adapter);
par->chan[1].par = NULL;
if (par->chan[2].par)
- i2c_bit_del_bus(&par->chan[2].adapter);
+ i2c_del_adapter(&par->chan[2].adapter);
par->chan[2].par = NULL;
}
diff --git a/drivers/video/riva/rivafb-i2c.c b/drivers/video/riva/rivafb-i2c.c
index c15b259..01b85e3 100644
--- a/drivers/video/riva/rivafb-i2c.c
+++ b/drivers/video/riva/rivafb-i2c.c
@@ -144,15 +144,15 @@ void riva_create_i2c_busses(struct riva_par *par)
void riva_delete_i2c_busses(struct riva_par *par)
{
if (par->chan[0].par)
- i2c_bit_del_bus(&par->chan[0].adapter);
+ i2c_del_adapter(&par->chan[0].adapter);
par->chan[0].par = NULL;
if (par->chan[1].par)
- i2c_bit_del_bus(&par->chan[1].adapter);
+ i2c_del_adapter(&par->chan[1].adapter);
par->chan[1].par = NULL;
if (par->chan[2].par)
- i2c_bit_del_bus(&par->chan[2].adapter);
+ i2c_del_adapter(&par->chan[2].adapter);
par->chan[2].par = NULL;
}
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c
index cef5bf5..1411f3b 100644
--- a/drivers/video/savage/savagefb-i2c.c
+++ b/drivers/video/savage/savagefb-i2c.c
@@ -208,7 +208,7 @@ void savagefb_delete_i2c_busses(struct fb_info *info)
struct savagefb_par *par = info->par;
if (par->chan.par)
- i2c_bit_del_bus(&par->chan.adapter);
+ i2c_del_adapter(&par->chan.adapter);
par->chan.par = NULL;
}
diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c
index 711cb11..59cd1e7 100644
--- a/drivers/video/sstfb.c
+++ b/drivers/video/sstfb.c
@@ -21,6 +21,11 @@
* Remove never finished and bogus 24/32bit support
* Clean up macro abuse
* Minor tidying for format.
+ * 12/2006 Helge Deller <deller@gmx.de>
+ * add /sys/class/graphics/fbX/vgapass sysfs-interface
+ * add module option "mode_option" to set initial screen mode
+ * use fbdev default videomode database
+ * remove debug functions from ioctl
*/
/*
@@ -65,19 +70,10 @@
*
* sstfb specific ioctls:
* toggle vga (0x46db) : toggle vga_pass_through
- * fill fb (0x46dc) : fills fb
- * test disp (0x46de) : draws a test image
*/
#undef SST_DEBUG
-/*
- Default video mode .
- 0 800x600@60 took from glide
- 1 640x480@75 took from glide
- 2 1024x768@76 std fb.mode
- 3 640x480@60 glide default */
-#define DEFAULT_MODE 3
/*
* Includes
@@ -92,20 +88,24 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
-#include <asm/ioctl.h>
#include <asm/uaccess.h>
#include <video/sstfb.h>
/* initialized by setup */
-static int vgapass; /* enable Vga passthrough cable */
+static int vgapass; /* enable VGA passthrough cable */
static int mem; /* mem size in MB, 0 = autodetect */
static int clipping = 1; /* use clipping (slower, safer) */
static int gfxclk; /* force FBI freq in Mhz . Dangerous */
static int slowpci; /* slow PCI settings */
-static char *mode_option __devinitdata;
+/*
+ Possible default video modes: 800x600@60, 640x480@75, 1024x768@76, 640x480@60
+*/
+#define DEFAULT_VIDEO_MODE "640x480@60"
+
+static char *mode_option __devinitdata = DEFAULT_VIDEO_MODE;
enum {
ID_VOODOO1 = 0,
@@ -119,48 +119,11 @@ static struct sst_spec voodoo_spec[] __devinitdata = {
{ .name = "Voodoo2", .default_gfx_clock = 75000, .max_gfxclk = 85 },
};
-static struct fb_var_screeninfo sstfb_default =
-#if ( DEFAULT_MODE == 0 )
- { /* 800x600@60, 16 bpp .borowed from glide/sst1/include/sst1init.h */
- 800, 600, 800, 600, 0, 0, 16, 0,
- {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0,
- 25000, 86, 41, 23, 1, 127, 4,
- 0, FB_VMODE_NONINTERLACED };
-#elif ( DEFAULT_MODE == 1 )
- {/* 640x480@75, 16 bpp .borowed from glide/sst1/include/sst1init.h */
- 640, 480, 640, 480, 0, 0, 16, 0,
- {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0,
- 31746, 118, 17, 16, 1, 63, 3,
- 0, FB_VMODE_NONINTERLACED };
-#elif ( DEFAULT_MODE == 2 )
- { /* 1024x768@76 took from my /etc/fb.modes */
- 1024, 768, 1024, 768,0, 0, 16,0,
- {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0,
- 11764, 208, 8, 36, 16, 120, 3 ,
- 0, FB_VMODE_NONINTERLACED };
-#elif ( DEFAULT_MODE == 3 )
- { /* 640x480@60 , 16bpp glide default ?*/
- 640, 480, 640, 480, 0, 0, 16, 0,
- {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0,
- 39721 , 38, 26 , 25 ,18 , 96 ,2,
- 0, FB_VMODE_NONINTERLACED };
-#elif
- #error "Invalid DEFAULT_MODE value !"
-#endif
-
/*
* debug functions
*/
-static void sstfb_drawdebugimage(struct fb_info *info);
-static int sstfb_dump_regs(struct fb_info *info);
-
-
#if (SST_DEBUG_REG > 0)
static void sst_dbg_print_read_reg(u32 reg, u32 val) {
const char *regname;
@@ -726,51 +689,77 @@ static int sstfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
return 0;
}
-static int sstfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
+static void sstfb_setvgapass( struct fb_info *info, int enable )
{
struct sstfb_par *par = info->par;
struct pci_dev *sst_dev = par->dev;
- u32 fbiinit0, tmp, val;
- u_long p;
+ u32 fbiinit0, tmp;
+
+ enable = enable ? 1:0;
+ if (par->vgapass == enable)
+ return;
+ par->vgapass = enable;
+
+ pci_read_config_dword(sst_dev, PCI_INIT_ENABLE, &tmp);
+ pci_write_config_dword(sst_dev, PCI_INIT_ENABLE,
+ tmp | PCI_EN_INIT_WR );
+ fbiinit0 = sst_read (FBIINIT0);
+ if (par->vgapass) {
+ sst_write(FBIINIT0, fbiinit0 & ~DIS_VGA_PASSTHROUGH);
+ printk(KERN_INFO "fb%d: Enabling VGA pass-through\n", info->node );
+ } else {
+ sst_write(FBIINIT0, fbiinit0 | DIS_VGA_PASSTHROUGH);
+ printk(KERN_INFO "fb%d: Disabling VGA pass-through\n", info->node );
+ }
+ pci_write_config_dword(sst_dev, PCI_INIT_ENABLE, tmp);
+}
+
+static ssize_t store_vgapass(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ char ** last = NULL;
+ int val;
+
+ val = simple_strtoul(buf, last, 0);
+ sstfb_setvgapass(info, val);
+
+ return count;
+}
+
+static ssize_t show_vgapass(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ struct sstfb_par *par = info->par;
+ return snprintf(buf, PAGE_SIZE, "%d\n", par->vgapass);
+}
+
+static struct device_attribute device_attrs[] = {
+ __ATTR(vgapass, S_IRUGO|S_IWUSR, show_vgapass, store_vgapass)
+ };
+
+static int sstfb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sstfb_par *par;
+ u32 val;
switch (cmd) {
-
- /* dump current FBIINIT values to system log */
- case _IO('F', 0xdb): /* 0x46db */
- return sstfb_dump_regs(info);
-
- /* fills lfb with #arg pixels */
- case _IOW('F', 0xdc, u32): /* 0x46dc */
+ /* set/get VGA pass_through mode */
+ case SSTFB_SET_VGAPASS:
if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
return -EFAULT;
- if (val > info->fix.smem_len)
- val = info->fix.smem_len;
- for (p = 0 ; p < val; p += 2)
- writew(p >> 6, info->screen_base + p);
+ sstfb_setvgapass(info, val);
return 0;
-
- /* change VGA pass_through mode */
- case _IOW('F', 0xdd, u32): /* 0x46dd */
- if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
+ case SSTFB_GET_VGAPASS:
+ par = info->par;
+ val = par->vgapass;
+ if (copy_to_user((void __user *)arg, &val, sizeof(val)))
return -EFAULT;
- pci_read_config_dword(sst_dev, PCI_INIT_ENABLE, &tmp);
- pci_write_config_dword(sst_dev, PCI_INIT_ENABLE,
- tmp | PCI_EN_INIT_WR );
- fbiinit0 = sst_read (FBIINIT0);
- if (val)
- sst_write(FBIINIT0, fbiinit0 & ~EN_VGA_PASSTHROUGH);
- else
- sst_write(FBIINIT0, fbiinit0 | EN_VGA_PASSTHROUGH);
- pci_write_config_dword(sst_dev, PCI_INIT_ENABLE, tmp);
- return 0;
-
- /* draw test image */
- case _IO('F', 0xde): /* 0x46de */
- f_dprintk("test color display at %d bpp\n",
- info->var.bits_per_pixel);
- sstfb_drawdebugimage(info);
return 0;
}
+
return -EINVAL;
}
@@ -804,6 +793,7 @@ static void sstfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
/*
* FillRect 2D command (solidfill or invert (via ROP_XOR)) - Voodoo2 only
*/
+#if 0
static void sstfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct sstfb_par *par = info->par;
@@ -825,6 +815,7 @@ static void sstfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
| (BLT_16BPP_FMT << 3) /* | BIT(14) */ | BIT(15) | BIT(16) );
sst_wait_idle();
}
+#endif
@@ -1156,6 +1147,7 @@ static int __devinit sst_init(struct fb_info *info, struct sstfb_par *par)
struct pll_timing gfx_timings;
struct sst_spec *spec;
int Fout;
+ int gfx_clock;
spec = &voodoo_spec[par->type];
f_ddprintk(" fbiinit0 fbiinit1 fbiinit2 fbiinit3 fbiinit4 "
@@ -1196,15 +1188,15 @@ static int __devinit sst_init(struct fb_info *info, struct sstfb_par *par)
}
/* set graphic clock */
- par->gfx_clock = spec->default_gfx_clock;
+ gfx_clock = spec->default_gfx_clock;
if ((gfxclk >10 ) && (gfxclk < spec->max_gfxclk)) {
printk(KERN_INFO "sstfb: Using supplied graphic freq : %dMHz\n", gfxclk);
- par->gfx_clock = gfxclk *1000;
+ gfx_clock = gfxclk *1000;
} else if (gfxclk) {
printk(KERN_WARNING "sstfb: %dMhz is way out of spec! Using default\n", gfxclk);
}
- sst_calc_pll(par->gfx_clock, &Fout, &gfx_timings);
+ sst_calc_pll(gfx_clock, &Fout, &gfx_timings);
par->dac_sw.set_pll(info, &gfx_timings, GFX_CLOCK);
/* disable fbiinit remap */
@@ -1215,10 +1207,11 @@ static int __devinit sst_init(struct fb_info *info, struct sstfb_par *par)
fbiinit0 = FBIINIT0_DEFAULT;
fbiinit1 = FBIINIT1_DEFAULT;
fbiinit4 = FBIINIT4_DEFAULT;
- if (vgapass)
- fbiinit0 &= ~EN_VGA_PASSTHROUGH;
+ par->vgapass = vgapass;
+ if (par->vgapass)
+ fbiinit0 &= ~DIS_VGA_PASSTHROUGH;
else
- fbiinit0 |= EN_VGA_PASSTHROUGH;
+ fbiinit0 |= DIS_VGA_PASSTHROUGH;
if (slowpci) {
fbiinit1 |= SLOW_PCI_WRITES;
fbiinit4 |= SLOW_PCI_READS;
@@ -1267,7 +1260,7 @@ static void __devexit sst_shutdown(struct fb_info *info)
/* TODO maybe shutdown the dac, vrefresh and so on... */
pci_write_config_dword(dev, PCI_INIT_ENABLE,
PCI_EN_INIT_WR);
- sst_unset_bits(FBIINIT0, FBI_RESET | FIFO_RESET | EN_VGA_PASSTHROUGH);
+ sst_unset_bits(FBIINIT0, FBI_RESET | FIFO_RESET | DIS_VGA_PASSTHROUGH);
pci_write_config_dword(dev, PCI_VCLK_DISABLE,0);
/* maybe keep fbiinit* and PCI_INIT_enable in the fb_info struct
* from start ? */
@@ -1278,8 +1271,7 @@ static void __devexit sst_shutdown(struct fb_info *info)
/*
* Interface to the world
*/
-#ifndef MODULE
-static int __init sstfb_setup(char *options)
+static int __devinit sstfb_setup(char *options)
{
char *this_opt;
@@ -1312,7 +1304,7 @@ static int __init sstfb_setup(char *options)
}
return 0;
}
-#endif
+
static struct fb_ops sstfb_ops = {
.owner = THIS_MODULE,
@@ -1416,15 +1408,10 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
*/
fix->line_length = 2048; /* default value, for 24 or 32bit: 4096 */
- if ( mode_option &&
- fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 16)) {
- printk(KERN_ERR "sstfb: can't set supplied video mode. Using default\n");
- info->var = sstfb_default;
- } else
- info->var = sstfb_default;
+ fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 16);
if (sstfb_check_var(&info->var, info)) {
- printk(KERN_ERR "sstfb: invalid default video mode.\n");
+ printk(KERN_ERR "sstfb: invalid video mode.\n");
goto fail;
}
@@ -1442,10 +1429,11 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
goto fail;
}
- if (1) /* set to 0 to see an initial bitmap instead */
- sstfb_clear_screen(info);
- else
- sstfb_drawdebugimage(info);
+ sstfb_clear_screen(info);
+
+ if (device_create_file(info->dev, &device_attrs[0]))
+ printk(KERN_WARNING "sstfb: can't create sysfs entry.\n");
+
printk(KERN_INFO "fb%d: %s frame buffer device at 0x%p\n",
info->node, fix->id, info->screen_base);
@@ -1453,6 +1441,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
return 0;
fail:
+ fb_dealloc_cmap(&info->cmap);
iounmap(info->screen_base);
fail_fb_remap:
iounmap(par->mmio_vbase);
@@ -1473,21 +1462,23 @@ static void __devexit sstfb_remove(struct pci_dev *pdev)
info = pci_get_drvdata(pdev);
par = info->par;
+ device_remove_file(info->dev, &device_attrs[0]);
sst_shutdown(info);
- unregister_framebuffer(info);
iounmap(info->screen_base);
iounmap(par->mmio_vbase);
release_mem_region(info->fix.smem_start, 0x400000);
release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
+ fb_dealloc_cmap(&info->cmap);
+ unregister_framebuffer(info);
framebuffer_release(info);
}
-static struct pci_device_id sstfb_id_tbl[] = {
- { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_VOODOO1 },
- { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_VOODOO2 },
+static const struct pci_device_id sstfb_id_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO ),
+ .driver_data = ID_VOODOO1, },
+ { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2),
+ .driver_data = ID_VOODOO2, },
{ 0 },
};
@@ -1501,142 +1492,23 @@ static struct pci_driver sstfb_driver = {
static int __devinit sstfb_init(void)
{
-#ifndef MODULE
char *option = NULL;
if (fb_get_options("sstfb", &option))
return -ENODEV;
sstfb_setup(option);
-#endif
+
return pci_register_driver(&sstfb_driver);
}
-#ifdef MODULE
static void __devexit sstfb_exit(void)
{
pci_unregister_driver(&sstfb_driver);
}
-#endif
-/*
- * testing and debugging functions
- */
-
-static int sstfb_dump_regs(struct fb_info *info)
-{
-#ifdef SST_DEBUG
- static struct { u32 reg ; const char *reg_name;} pci_regs[] = {
- { PCI_INIT_ENABLE, "initenable"},
- { PCI_VCLK_ENABLE, "enable vclk"},
- { PCI_VCLK_DISABLE, "disable vclk"},
- };
-
- static struct { u32 reg ; const char *reg_name;} sst_regs[] = {
- {FBIINIT0,"fbiinit0"},
- {FBIINIT1,"fbiinit1"},
- {FBIINIT2,"fbiinit2"},
- {FBIINIT3,"fbiinit3"},
- {FBIINIT4,"fbiinit4"},
- {FBIINIT5,"fbiinit5"},
- {FBIINIT6,"fbiinit6"},
- {FBIINIT7,"fbiinit7"},
- {LFBMODE,"lfbmode"},
- {FBZMODE,"fbzmode"},
- };
-
- const int pci_s = ARRAY_SIZE(pci_regs);
- const int sst_s = ARRAY_SIZE(sst_regs);
- struct sstfb_par *par = info->par;
- struct pci_dev *dev = par->dev;
- u32 pci_res[pci_s];
- u32 sst_res[sst_s];
- int i;
-
- for (i=0; i<pci_s; i++) {
- pci_read_config_dword(dev, pci_regs[i].reg, &pci_res[i]);
- }
- for (i=0; i<sst_s; i++) {
- sst_res[i] = sst_read(sst_regs[i].reg);
- }
-
- dprintk("hardware register dump:\n");
- for (i=0; i<pci_s; i++) {
- dprintk("%s %0#10x\n", pci_regs[i].reg_name, pci_res[i]);
- }
- for (i=0; i<sst_s; i++) {
- dprintk("%s %0#10x\n", sst_regs[i].reg_name, sst_res[i]);
- }
- return 0;
-#else
- return -EINVAL;
-#endif
-}
-
-static void sstfb_fillrect_softw( struct fb_info *info, const struct fb_fillrect *rect)
-{
- u8 __iomem *fbbase_virt = info->screen_base;
- int x, y, w = info->var.bits_per_pixel == 16 ? 2 : 4;
- u32 color = rect->color, height = rect->height;
- u8 __iomem *p;
-
- if (w==2) color |= color<<16;
- for (y=rect->dy; height; y++, height--) {
- p = fbbase_virt + y*info->fix.line_length + rect->dx*w;
- x = rect->width;
- if (w==2) x>>=1;
- while (x) {
- writel(color, p);
- p += 4;
- x--;
- }
- }
-}
-
-static void sstfb_drawrect_XY( struct fb_info *info, int x, int y,
- int w, int h, int color, int hwfunc)
-{
- struct fb_fillrect rect;
- rect.dx = x;
- rect.dy = y;
- rect.height = h;
- rect.width = w;
- rect.color = color;
- rect.rop = ROP_COPY;
- if (hwfunc)
- sstfb_fillrect(info, &rect);
- else
- sstfb_fillrect_softw(info, &rect);
-}
-
-/* print some squares on the fb */
-static void sstfb_drawdebugimage(struct fb_info *info)
-{
- static int idx;
-
- /* clear screen */
- sstfb_clear_screen(info);
-
- idx = (idx+1) & 1;
-
- /* white rect */
- sstfb_drawrect_XY(info, 0, 0, 50, 50, 0xffff, idx);
-
- /* blue rect */
- sstfb_drawrect_XY(info, 50, 50, 50, 50, 0x001f, idx);
-
- /* green rect */
- sstfb_drawrect_XY(info, 100, 100, 80, 80, 0x07e0, idx);
-
- /* red rect */
- sstfb_drawrect_XY(info, 250, 250, 120, 100, 0xf800, idx);
-}
-
module_init(sstfb_init);
-
-#ifdef MODULE
module_exit(sstfb_exit);
-#endif
MODULE_AUTHOR("(c) 2000,2002 Ghozlane Toumi <gtoumi@laposte.net>");
MODULE_DESCRIPTION("FBDev driver for 3dfx Voodoo Graphics and Voodoo2 based video boards");
@@ -1652,3 +1524,6 @@ module_param(gfxclk, int, 0);
MODULE_PARM_DESC(gfxclk, "Force graphic chip frequency in MHz. DANGEROUS. (default=auto)");
module_param(slowpci, bool, 0);
MODULE_PARM_DESC(slowpci, "Uses slow PCI settings (0 or 1) (default=0)");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Initial video mode (default=" DEFAULT_VIDEO_MODE ")");
+
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index d18d642..904e5ae 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -9,7 +9,7 @@ config W1_SLAVE_THERM
tristate "Thermal family implementation"
depends on W1
help
- Say Y here if you want to connect 1-wire thermal sensors to you
+ Say Y here if you want to connect 1-wire thermal sensors to your
wire.
config W1_SLAVE_SMEM
@@ -17,7 +17,7 @@ config W1_SLAVE_SMEM
depends on W1
help
Say Y here if you want to connect 1-wire
- simple 64bit memory rom(ds2401/ds2411/ds1990*) to you wire.
+ simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
config W1_SLAVE_DS2433
tristate "4kb EEPROM family support (DS2433)"
OpenPOWER on IntegriCloud